diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | doc/Load.3 | 3 | ||||
-rw-r--r-- | doc/load.n | 22 | ||||
-rw-r--r-- | generic/tcl.h | 8 | ||||
-rw-r--r-- | generic/tclLoad.c | 31 | ||||
-rw-r--r-- | tests/load.test | 20 | ||||
-rw-r--r-- | unix/tclLoadDl.c | 21 | ||||
-rw-r--r-- | unix/tclLoadDyld.c | 36 |
8 files changed, 124 insertions, 31 deletions
@@ -1,3 +1,17 @@ +2012-11-15 Jan Nijtmans <nijtmans@users.sf.net> + + IMPLEMENTATION OF TIP#416 + + New Options for 'load': -global and -lazy + + * generic/tcl.h: + * generic/tclLoad.c + * unix/tclLoadDl.c + * unix/tclLoadDyld.c + * tests/load.test + * doc/Load.3 + * doc/load.n + 2012-11-14 Donal K. Fellows <dkf@users.sf.net> * unix/tclUnixFCmd.c (TclUnixOpenTemporaryFile): [Bug 2933003]: Factor @@ -31,7 +31,8 @@ Array of names of symbols to be resolved during the load of the library, or NULL if no symbols are to be resolved. If an array is given, the last entry in the array must be NULL. .AP int flags in -Reserved for future expansion. Must be 0. +The value should normally be 0, but \fITCL_LOAD_GLOBALfR or \fITCL_LOAD_LAZYfR +or a combination of those two is allowed as well. .AP void *procPtrs out Points to an array that will hold the addresses of the functions described in the \fIsymbols\fR argument. Should be NULL if no symbols are to be resolved. @@ -11,11 +11,11 @@ .SH NAME load \- Load machine code and initialize new commands .SH SYNOPSIS -\fBload \fIfileName\fR +\fBload\fR ?\fB\-global\fR? ?\fB\-lazy\fR? ?\fB\-\-\fR? \fIfileName\fR .br -\fBload \fIfileName packageName\fR +\fBload\fR ?\fB\-global\fR? ?\fB\-lazy\fR? ?\fB\-\-\fR? \fIfileName packageName\fR .br -\fBload \fIfileName packageName interp\fR +\fBload\fR ?\fB\-global\fR? ?\fB\-lazy\fR? ?\fB\-\-\fR? \fIfileName packageName interp\fR .BE .SH DESCRIPTION .PP @@ -104,6 +104,22 @@ Otherwise, the \fBload\fR command searches for a dynamically loaded package by that name, and uses it if it is found. If several different files have been \fBload\fRed with different versions of the package, Tcl picks the file that was loaded first. +.PP +If \fB\-global\fR is specified preceding the filename, all symbols +found in the shared library are exported for global use by other +libraries. The option \fB\-lazy\fR delays the actual loading of +symbols until their first actual use. The options may be abbreviated. +The option \fB\-\-\fR indicates the end of the options, and should +be used if you wish to use a filename which starts with \fB\-\fR +and you provide a packageName to the \fBload\fR command. +.PP +On platforms which do not support the \fB\-global\fR or \fB\-lazy\fR +options, the options still exist but have no effect. Note that use +of the \fB\-global\fR or \fB\-lazy\fR option may lead to crashes +in your application later (in case of symbol conflicts resp. missing +symbols), which cannot be detected during the \fBload\fR. So, only +use this when you know what you are doing, you will not get a nice +error message when something is wrong with the loaded library. .SH "PORTABILITY ISSUES" .TP \fBWindows\fR\0\0\0\0\0 diff --git a/generic/tcl.h b/generic/tcl.h index 5f6146e..147672c 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -2362,6 +2362,14 @@ typedef int (Tcl_ArgvGenFuncProc)(ClientData clientData, Tcl_Interp *interp, /* *---------------------------------------------------------------------------- + * Definitions needed for the Tcl_LoadFile function. [TIP #416] + */ + +#define TCL_LOAD_GLOBAL 1 +#define TCL_LOAD_LAZY 2 + +/* + *---------------------------------------------------------------------------- * Single public declaration for NRE. */ diff --git a/generic/tclLoad.c b/generic/tclLoad.c index 3fead6f..5cacab1 100644 --- a/generic/tclLoad.c +++ b/generic/tclLoad.c @@ -132,9 +132,34 @@ Tcl_LoadObjCmd( Tcl_LoadHandle loadHandle; Tcl_UniChar ch; unsigned len; + int index, flags = 0; + Tcl_Obj *const *savedobjv = objv; + static const char *const options[] = { + "-global", "-lazy", "--", NULL + }; + enum options { + LOAD_GLOBAL, LOAD_LAZY, LOAD_LAST + }; + while (objc > 2) { + if (TclGetString(objv[1])[0] != '-') { + break; + } + if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + ++objv; --objc; + if (LOAD_GLOBAL == (enum options) index) { + flags |= TCL_LOAD_GLOBAL; + } else if (LOAD_LAZY == (enum options) index) { + flags |= TCL_LOAD_LAZY; + } else { + break; + } + } if ((objc < 2) || (objc > 4)) { - Tcl_WrongNumArgs(interp, 1, objv, "fileName ?packageName? ?interp?"); + Tcl_WrongNumArgs(interp, 1, savedobjv, "?-global? ?-lazy? ?--? fileName ?packageName? ?interp?"); return TCL_ERROR; } if (Tcl_FSConvertToPathType(interp, objv[1]) != TCL_OK) { @@ -365,7 +390,7 @@ Tcl_LoadObjCmd( symbols[1] = NULL; Tcl_MutexLock(&packageMutex); - code = Tcl_LoadFile(interp, objv[1], symbols, 0, &initProc, + code = Tcl_LoadFile(interp, objv[1], symbols, flags, &initProc, &loadHandle); Tcl_MutexUnlock(&packageMutex); if (code != TCL_OK) { @@ -391,7 +416,7 @@ Tcl_LoadObjCmd( pkgPtr->unloadProc = (Tcl_PackageUnloadProc *) Tcl_FindSymbol(interp, loadHandle, Tcl_DStringValue(&unloadName)); - pkgPtr->safeUnloadProc = (Tcl_PackageUnloadProc *) + pkgPtr->safeUnloadProc = (Tcl_PackageUnloadProc *) Tcl_FindSymbol(interp, loadHandle, Tcl_DStringValue(&safeUnloadName)); pkgPtr->interpRefCount = 0; diff --git a/tests/load.test b/tests/load.test index 78bf64c..eef677f 100644 --- a/tests/load.test +++ b/tests/load.test @@ -47,32 +47,38 @@ testConstraint testsimplefilesystem \ test load-1.1 {basic errors} {} { list [catch {load} msg] $msg -} "1 {wrong \# args: should be \"load fileName ?packageName? ?interp?\"}" +} "1 {wrong \# args: should be \"load ?-global? ?-lazy? ?--? fileName ?packageName? ?interp?\"}" test load-1.2 {basic errors} {} { list [catch {load a b c d} msg] $msg -} "1 {wrong \# args: should be \"load fileName ?packageName? ?interp?\"}" +} "1 {wrong \# args: should be \"load ?-global? ?-lazy? ?--? fileName ?packageName? ?interp?\"}" test load-1.3 {basic errors} {} { list [catch {load a b foobar} msg] $msg } {1 {could not find interpreter "foobar"}} test load-1.4 {basic errors} {} { - list [catch {load {}} msg] $msg + list [catch {load -global {}} msg] $msg } {1 {must specify either file name or package name}} test load-1.5 {basic errors} {} { - list [catch {load {} {}} msg] $msg + list [catch {load -lazy {} {}} msg] $msg } {1 {must specify either file name or package name}} test load-1.6 {basic errors} {} { list [catch {load {} Unknown} msg] $msg } {1 {package "Unknown" isn't loaded statically}} +test load-1.7 {basic errors} {} { + list [catch {load -abc foo} msg] $msg +} "1 {bad option \"-abc\": must be -global, -lazy, or --}" +test load-1.8 {basic errors} {} { + list [catch {load -global} msg] $msg +} "1 {couldn't figure out package name for -global}" test load-2.1 {basic loading, with guess for package name} \ [list $dll $loaded] { - load [file join $testDir pkga$ext] + load -global [file join $testDir pkga$ext] list [pkga_eq abc def] [lsort [info commands pkga_*]] } {0 {pkga_eq pkga_quote}} interp create -safe child test load-2.2 {loading into a safe interpreter, with package name conversion} \ [list $dll $loaded] { - load [file join $testDir pkgb$ext] pKgB child + load -lazy [file join $testDir pkgb$ext] pKgB child list [child eval pkgb_sub 44 13] [catch {child eval pkgb_unsafe} msg] $msg \ [catch {pkgb_sub 12 10} msg2] $msg2 } {31 1 {invalid command name "pkgb_unsafe"} 1 {invalid command name "pkgb_sub"}} @@ -126,7 +132,7 @@ test load-5.1 {file name not specified and no static package: pick default} \ [list $dll $loaded] { catch {interp delete x} interp create x - load [file join $testDir pkga$ext] pkga + load -global [file join $testDir pkga$ext] pkga load {} pkga x set result [info loaded x] interp delete x diff --git a/unix/tclLoadDl.c b/unix/tclLoadDl.c index 9ff7657..dc711f8 100644 --- a/unix/tclLoadDl.c +++ b/unix/tclLoadDl.c @@ -75,6 +75,7 @@ TclpDlopen( void *handle; Tcl_LoadHandle newHandle; const char *native; + int dlopenflags = 0; /* * First try the full path the user gave us. This is particularly @@ -84,9 +85,19 @@ TclpDlopen( native = Tcl_FSGetNativePath(pathPtr); /* - * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070] + * Use (RTLD_NOW|RTLD_LOCAL) as default, see [Bug #3216070] */ - handle = dlopen(native, RTLD_NOW | RTLD_LOCAL); + if (flags & TCL_LOAD_GLOBAL) { + dlopenflags |= RTLD_GLOBAL; + } else { + dlopenflags |= RTLD_LOCAL; + } + if (flags & TCL_LOAD_LAZY) { + dlopenflags |= RTLD_LAZY; + } else { + dlopenflags |= RTLD_NOW; + } + handle = dlopen(native, dlopenflags); if (handle == NULL) { /* * Let the OS loader examine the binary search path for whatever @@ -99,9 +110,9 @@ TclpDlopen( native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds); /* - * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070] + * Use (RTLD_NOW|RTLD_LOCAL) as default, see [Bug #3216070] */ - handle = dlopen(native, RTLD_NOW | RTLD_LOCAL); + handle = dlopen(native, dlopenflags); Tcl_DStringFree(&ds); } @@ -153,7 +164,7 @@ FindSymbol( const char *native; /* Name of the library to be loaded, in * system encoding */ Tcl_DString newName, ds; /* Buffers for converting the name to - * system encoding and prepending an + * system encoding and prepending an * underscore*/ void *handle = (void *) loadHandle->clientData; /* Native handle to the loaded library */ diff --git a/unix/tclLoadDyld.c b/unix/tclLoadDyld.c index 4f39d1f..5df022e 100644 --- a/unix/tclLoadDyld.c +++ b/unix/tclLoadDyld.c @@ -170,6 +170,9 @@ TclpDlopen( int result; Tcl_DString ds; const char *nativePath, *nativeFileName = NULL; +#if TCL_DYLD_USE_DLFCN + int dlopenflags = 0; +#endif /* TCL_DYLD_USE_DLFCN */ /* * First try the full path the user gave us. This is particularly @@ -183,20 +186,27 @@ TclpDlopen( #if TCL_DYLD_USE_DLFCN /* - * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070] + * Use (RTLD_NOW|RTLD_LOCAL) as default, see [Bug #3216070] */ - dlHandle = dlopen(nativePath, RTLD_NOW | RTLD_LOCAL); + if (flags & TCL_LOAD_GLOBAL) { + dlopenflags |= RTLD_GLOBAL; + } else { + dlopenflags |= RTLD_LOCAL; + } + if (flags & TCL_LOAD_LAZY) { + dlopenflags |= RTLD_LAZY; + } else { + dlopenflags |= RTLD_NOW; + } + dlHandle = dlopen(nativePath, dlopenflags); if (!dlHandle) { /* * Let the OS loader examine the binary search path for whatever string * the user gave us which hopefully refers to a file on the binary * path. - * - * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070] - */ - dlHandle = dlopen(nativeFileName, RTLD_NOW | RTLD_LOCAL); + dlHandle = dlopen(nativeFileName, dlopenflags); if (!dlHandle) { errMsg = dlerror(); } @@ -238,9 +248,10 @@ TclpDlopen( err = NSCreateObjectFileImageFromFile(nativePath, &dyldObjFileImage); if (err == NSObjectFileImageSuccess && dyldObjFileImage) { - module = NSLinkModule(dyldObjFileImage, nativePath, - NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_PRIVATE - | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + int nsflags = NSLINKMODULE_OPTION_RETURN_ON_ERROR; + if (!(flags & 1)) nsflags |= NSLINKMODULE_OPTION_PRIVATE; + if (!(flags & 2)) nsflags |= NSLINKMODULE_OPTION_BINDNOW; + module = NSLinkModule(dyldObjFileImage, nativePath, nsflags); NSDestroyObjectFileImage(dyldObjFileImage); if (module) { modulePtr = ckalloc(sizeof(Tcl_DyldModuleHandle)); @@ -565,6 +576,7 @@ TclpLoadMemory( Tcl_DyldModuleHandle *modulePtr; NSModule module; const char *objFileImageErrMsg = NULL; + int nsflags = NSLINKMODULE_OPTION_RETURN_ON_ERROR; /* * Try to create an object file image that we can load from. @@ -659,9 +671,9 @@ TclpLoadMemory( * Extract the module we want from the image of the object file. */ - module = NSLinkModule(dyldObjFileImage, "[Memory Based Bundle]", - NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_PRIVATE - | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if (!(flags & 1)) nsflags |= NSLINKMODULE_OPTION_PRIVATE; + if (!(flags & 2)) nsflags |= NSLINKMODULE_OPTION_BINDNOW; + module = NSLinkModule(dyldObjFileImage, "[Memory Based Bundle]", nsflags); NSDestroyObjectFileImage(dyldObjFileImage); if (!module) { NSLinkEditErrors editError; |