diff options
Diffstat (limited to 'generic/tclPkg.c')
-rw-r--r-- | generic/tclPkg.c | 82 |
1 files changed, 63 insertions, 19 deletions
diff --git a/generic/tclPkg.c b/generic/tclPkg.c index 919b1b9..43d859b 100644 --- a/generic/tclPkg.c +++ b/generic/tclPkg.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclPkg.c,v 1.8 2002/01/17 04:37:33 dgp Exp $ + * RCS: @(#) $Id: tclPkg.c,v 1.9 2002/02/22 22:36:09 dgp Exp $ */ #include "tclInt.h" @@ -187,7 +187,7 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr) Tcl_DString command; /* - * If an attempt is being made to load this into a standalong executable + * If an attempt is being made to load this into a standalone executable * on a platform where backlinking is not supported then this must be * a shared version of Tcl (Otherwise the load would have failed). * Detect this situation by checking that this library has been correctly @@ -195,7 +195,67 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr) * work. */ - if (!tclEmptyStringRep) { + if (tclEmptyStringRep == NULL) { + + /* + * OK, so what's going on here? + * + * First, what are we doing? We are performing a check on behalf of + * one particular caller, Tcl_InitStubs(). When a package is + * stub-enabled, it is statically linked to libtclstub.a, which + * contains a copy of Tcl_InitStubs(). When a stub-enabled package + * is loaded, its *_Init() function is supposed to call + * Tcl_InitStubs() before calling any other functions in the Tcl + * library. The first Tcl function called by Tcl_InitStubs() through + * the stub table is Tcl_PkgRequireEx(), so this code right here is + * the first code that is part of the original Tcl library in the + * executable that gets executed on behalf of a newly loaded + * stub-enabled package. + * + * One easy error for the developer/builder of a stub-enabled package + * to make is to forget to define USE_TCL_STUBS when compiling the + * package. When that happens, the package will contain symbols + * that are references to the Tcl library, rather than function + * pointers referencing the stub table. On platforms that lack + * backlinking, those unresolved references may cause the loading + * of the package to also load a second copy of the Tcl library, + * leading to all kinds of trouble. We would like to catch that + * error and report a useful message back to the user. That's + * what we're doing. + * + * Second, how does this work? If we reach this point, then the + * global variable tclEmptyStringRep has the value NULL. Compare + * that with the definition of tclEmptyStringRep near the top of + * the file generic/tclObj.c. It clearly should not have the value + * NULL; it should point to the char tclEmptyString. If we see it + * having the value NULL, then somehow we are seeing a Tcl library + * that isn't completely initialized, and that's an indicator for the + * error condition described above. (Further explanation is welcome.) + * + * Third, so what do we do about it? This situation indicates + * the package we just loaded wasn't properly compiled to be + * stub-enabled, yet it thinks it is stub-enabled (it called + * Tcl_InitStubs()). We want to report that the package just + * loaded is broken, so we want to place an error message in + * the interpreter result and return NULL to indicate failure + * to Tcl_InitStubs() so that it will also fail. (Further + * explanation why we don't want to Tcl_Panic() is welcome. + * After all, two Tcl libraries can't be a good thing!) + * + * Trouble is that's going to be tricky. We're now using a Tcl + * library that's not fully initialized. In particular, it + * doesn't have a proper value for tclEmptyStringRep. The + * Tcl_Obj system heavily depends on the value of tclEmptyStringRep + * and all of Tcl depends (increasingly) on the Tcl_Obj system, we + * need to correct that flaw before making the calls to set the + * interpreter result to the error message. That's the only flaw + * corrected; other problems with initialization of the Tcl library + * are not remedied, so be very careful about adding any other calls + * here without checking how they behave when initialization is + * incomplete. + */ + + tclEmptyStringRep = &tclEmptyString; Tcl_AppendResult(interp, "Cannot load package \"", name, "\" in standalone executable: This package is not ", "compiled with stub support", NULL); @@ -387,22 +447,6 @@ Tcl_PkgPresentEx(interp, name, version, exact, clientDataPtr) Package *pkgPtr; int satisfies, result; - /* - * If an attempt is being made to load this into a standalone executable - * on a platform where backlinking is not supported then this must be - * a shared version of Tcl (Otherwise the load would have failed). - * Detect this situation by checking that this library has been correctly - * initialised. If it has not been then return immediately as nothing will - * work. - */ - - if (!tclEmptyStringRep) { - Tcl_AppendResult(interp, "Cannot load package \"", name, - "\" in standalone executable: This package is not ", - "compiled with stub support", NULL); - return NULL; - } - hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); if (hPtr) { pkgPtr = (Package *) Tcl_GetHashValue(hPtr); |