diff options
author | ferrieux <ferrieux@users.sourceforge.net> | 2009-11-16 17:38:08 (GMT) |
---|---|---|
committer | ferrieux <ferrieux@users.sourceforge.net> | 2009-11-16 17:38:08 (GMT) |
commit | 3ffda83a5b3d9b03fa4bad1e5384919a46adf47a (patch) | |
tree | 1b93d42b56b88ab1862f7389658528282be889d6 /generic | |
parent | d264119bd45f0b0e694574efc0a627ac1a4232cb (diff) | |
download | tcl-3ffda83a5b3d9b03fa4bad1e5384919a46adf47a.zip tcl-3ffda83a5b3d9b03fa4bad1e5384919a46adf47a.tar.gz tcl-3ffda83a5b3d9b03fa4bad1e5384919a46adf47a.tar.bz2 |
(forward port) Fix [Bug 2891556] and improve test to detect similar manifestations in the future. Add tcltest support for finalization.
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tclBasic.c | 8 | ||||
-rw-r--r-- | generic/tclCmdIL.c | 57 | ||||
-rw-r--r-- | generic/tclEncoding.c | 28 | ||||
-rw-r--r-- | generic/tclInt.h | 4 | ||||
-rw-r--r-- | generic/tclNamesp.c | 36 | ||||
-rw-r--r-- | generic/tclResult.c | 9 | ||||
-rw-r--r-- | generic/tclTest.c | 47 |
7 files changed, 175 insertions, 14 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index cca8fba..c8ae5ae 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -16,7 +16,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclBasic.c,v 1.407 2009/11/10 20:53:24 andreas_kupries Exp $ + * RCS: @(#) $Id: tclBasic.c,v 1.408 2009/11/16 17:38:08 ferrieux Exp $ */ #include "tclInt.h" @@ -530,6 +530,9 @@ Tcl_CreateInterp(void) iPtr->errorInfo = NULL; TclNewLiteralStringObj(iPtr->eiVar, "::errorInfo"); Tcl_IncrRefCount(iPtr->eiVar); + iPtr->errorStack = Tcl_NewListObj(0, NULL); + Tcl_IncrRefCount(iPtr->errorStack); + iPtr->resetErrorStack = 1; iPtr->errorCode = NULL; TclNewLiteralStringObj(iPtr->ecVar, "::errorCode"); Tcl_IncrRefCount(iPtr->ecVar); @@ -1470,6 +1473,7 @@ DeleteInterpProc( Tcl_DecrRefCount(iPtr->errorInfo); iPtr->errorInfo = NULL; } + Tcl_DecrRefCount(iPtr->errorStack); if (iPtr->returnOpts) { Tcl_DecrRefCount(iPtr->returnOpts); } @@ -8820,5 +8824,7 @@ TclInfoCoroutineCmd( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index 03f5593..9c89ae4 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -16,7 +16,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclCmdIL.c,v 1.171 2009/08/20 10:56:55 dkf Exp $ + * RCS: @(#) $Id: tclCmdIL.c,v 1.172 2009/11/16 17:38:08 ferrieux Exp $ */ #include "tclInt.h" @@ -118,6 +118,9 @@ static int InfoCompleteCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int InfoDefaultCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +/* TIP #348 - New 'info' subcommand 'errorstack' */ +static int InfoErrorStackCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const objv[]); /* TIP #280 - New 'info' subcommand 'frame' */ static int InfoFrameCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -164,6 +167,7 @@ static const EnsembleImplMap defaultInfoMap[] = { {"complete", InfoCompleteCmd, NULL}, {"coroutine", TclInfoCoroutineCmd, NULL}, {"default", InfoDefaultCmd, NULL}, + {"errorstack", InfoErrorStackCmd, NULL}, {"exists", TclInfoExistsCmd, TclCompileInfoExistsCmd}, {"frame", InfoFrameCmd, NULL}, {"functions", InfoFunctionsCmd, NULL}, @@ -1017,6 +1021,55 @@ InfoDefaultCmd( /* *---------------------------------------------------------------------- * + * InfoErrorStackCmd -- + * + * Called to implement the "info errorstack" command that returns information + * about the last error's call stack. Handles the following syntax: + * + * info errorstack ?interp? + * + * Results: + * Returns TCL_OK if successful and TCL_ERROR if there is an error. + * + * Side effects: + * Returns a result in the interpreter's result object. If there is an + * error, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +InfoErrorStackCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tcl_Interp *target; + Interp *iPtr; + + if ((objc != 1) && (objc != 2)) { + Tcl_WrongNumArgs(interp, 1, objv, "?interp?"); + return TCL_ERROR; + } + + target = interp; + if (objc == 2) { + target = Tcl_GetSlave(interp, Tcl_GetString(objv[1])); + if (target == NULL) { + return TCL_ERROR; + } + } + + iPtr = (Interp *) target; + Tcl_SetObjResult(interp, iPtr->errorStack); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * * TclInfoExistsCmd -- * * Called to implement the "info exists" command that determines whether @@ -4388,5 +4441,7 @@ SelectObjFromSublist( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ diff --git a/generic/tclEncoding.c b/generic/tclEncoding.c index 441e099..2188256 100644 --- a/generic/tclEncoding.c +++ b/generic/tclEncoding.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclEncoding.c,v 1.66 2009/02/10 22:49:55 nijtmans Exp $ + * RCS: @(#) $Id: tclEncoding.c,v 1.67 2009/11/16 17:38:08 ferrieux Exp $ */ #include "tclInt.h" @@ -844,6 +844,9 @@ FreeEncoding( if (encodingPtr == NULL) { return; } + if (encodingPtr->refCount<=0) { + Tcl_Panic("FreeEncoding: refcount problem !!!"); + } encodingPtr->refCount--; if (encodingPtr->refCount == 0) { if (encodingPtr->freeProc != NULL) { @@ -3385,11 +3388,24 @@ EscapeFreeProc( if (dataPtr == NULL) { return; } - subTablePtr = dataPtr->subTables; - for (i = 0; i < dataPtr->numSubTables; i++) { - FreeEncoding((Tcl_Encoding) subTablePtr->encodingPtr); - subTablePtr++; - } + /* + * The subTables should be freed recursively in normal operation but not + * during TclFinalizeEncodingSubsystem because they are also present as a + * weak reference in the toplevel encodingTable (ie they don't have a +1 + * refcount for this), and unpredictable nuking order could remove them + * from under the following loop's feet [Bug 2891556]. + * + * The encodingsInitialized flag, being reset on entry to TFES, can serve + * as a "not in finalization" test. + */ + if (encodingsInitialized) + { + subTablePtr = dataPtr->subTables; + for (i = 0; i < dataPtr->numSubTables; i++) { + FreeEncoding((Tcl_Encoding) subTablePtr->encodingPtr); + subTablePtr++; + } + } ckfree((char *) dataPtr); } diff --git a/generic/tclInt.h b/generic/tclInt.h index 0f6654b..be536a9 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -15,7 +15,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.445 2009/09/30 03:11:26 dgp Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.446 2009/11/16 17:38:08 ferrieux Exp $ */ #ifndef _TCLINT @@ -1895,6 +1895,8 @@ typedef struct Interp { Tcl_Obj *eiVar; /* cached ref to ::errorInfo variable. */ Tcl_Obj *errorCode; /* errorCode value (now as a Tcl_Obj). */ Tcl_Obj *ecVar; /* cached ref to ::errorInfo variable. */ + Tcl_Obj *errorStack; /* [info errorstack] value (as a Tcl_Obj). */ + int resetErrorStack; /* controls cleaning up of ::errorStack */ int returnLevel; /* [return -level] parameter. */ /* diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index 94ade8f..2b9b508 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -23,7 +23,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclNamesp.c,v 1.193 2009/09/30 03:11:26 dgp Exp $ + * RCS: @(#) $Id: tclNamesp.c,v 1.194 2009/11/16 17:38:09 ferrieux Exp $ */ #include "tclInt.h" @@ -7574,7 +7574,7 @@ Tcl_LogCommandInfo( { register const char *p; Interp *iPtr = (Interp *) interp; - int overflow, limit = 150; + int overflow, limit = 150, len; Var *varPtr, *arrayPtr; if (iPtr->flags & ERR_ALREADY_LOGGED) { @@ -7633,6 +7633,36 @@ Tcl_LogCommandInfo( TCL_GLOBAL_ONLY); } } + + /* + * TIP #348 + */ + + if (Tcl_IsShared(iPtr->errorStack)) { + Tcl_Obj *newObj; + + newObj = Tcl_DuplicateObj(iPtr->errorStack); + Tcl_DecrRefCount(iPtr->errorStack); + Tcl_IncrRefCount(newObj); + iPtr->errorStack = newObj; + } + Tcl_ListObjLength(interp, iPtr->errorStack, &len); + if (iPtr->resetErrorStack) { + iPtr->resetErrorStack = 0; + /* reset while keeping the list intrep as much as possible */ + Tcl_ListObjReplace(interp, iPtr->errorStack, 0, len, 0, NULL); + len=0; + } + if (iPtr->varFramePtr != iPtr->rootFramePtr) { + Tcl_Obj *listPtr; + int result; + listPtr=Tcl_NewListObj(iPtr->varFramePtr->objc, + iPtr->varFramePtr->objv); + result = Tcl_ListObjReplace(interp, iPtr->errorStack, len, 0, 1, &listPtr); + if (result != TCL_OK) { + Tcl_DecrRefCount(listPtr); + } + } } /* @@ -7640,5 +7670,7 @@ Tcl_LogCommandInfo( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ diff --git a/generic/tclResult.c b/generic/tclResult.c index 921a867..3d6284e 100644 --- a/generic/tclResult.c +++ b/generic/tclResult.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclResult.c,v 1.54 2008/12/17 16:47:38 nijtmans Exp $ + * RCS: @(#) $Id: tclResult.c,v 1.55 2009/11/16 17:38:09 ferrieux Exp $ */ #include "tclInt.h" @@ -19,7 +19,7 @@ enum returnKeys { KEY_CODE, KEY_ERRORCODE, KEY_ERRORINFO, KEY_ERRORLINE, - KEY_LEVEL, KEY_OPTIONS, KEY_LAST + KEY_LEVEL, KEY_OPTIONS, KEY_ERRORSTACK, KEY_LAST }; /* @@ -922,6 +922,7 @@ Tcl_ResetResult( Tcl_DecrRefCount(iPtr->errorInfo); iPtr->errorInfo = NULL; } + iPtr->resetErrorStack = 1; iPtr->returnLevel = 1; iPtr->returnCode = TCL_OK; if (iPtr->returnOpts) { @@ -1158,6 +1159,7 @@ GetKeys(void) TclNewLiteralStringObj(keys[KEY_ERRORCODE], "-errorcode"); TclNewLiteralStringObj(keys[KEY_ERRORINFO], "-errorinfo"); TclNewLiteralStringObj(keys[KEY_ERRORLINE], "-errorline"); + TclNewLiteralStringObj(keys[KEY_ERRORSTACK],"-errorstack"); TclNewLiteralStringObj(keys[KEY_LEVEL], "-level"); TclNewLiteralStringObj(keys[KEY_OPTIONS], "-options"); @@ -1503,6 +1505,7 @@ Tcl_GetReturnOptions( Tcl_DictObjPut(NULL, options, keys[KEY_ERRORLINE], Tcl_NewIntObj(iPtr->errorLine)); } + Tcl_DictObjPut(NULL, options, keys[KEY_ERRORSTACK], iPtr->errorStack); return options; } @@ -1624,5 +1627,7 @@ Tcl_TransferResult( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ diff --git a/generic/tclTest.c b/generic/tclTest.c index da00e84..cdaa440 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -14,7 +14,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclTest.c,v 1.136 2009/02/10 23:09:08 nijtmans Exp $ + * RCS: @(#) $Id: tclTest.c,v 1.137 2009/11/16 17:38:09 ferrieux Exp $ */ #define TCL_TEST @@ -290,6 +290,9 @@ static int TestexitmainloopCmd(ClientData dummy, Tcl_Interp *interp, int argc, const char **argv); static int TestpanicCmd(ClientData dummy, Tcl_Interp *interp, int argc, const char **argv); +static int TestfinexitObjCmd(ClientData dummy, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); static int TestparserObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -619,6 +622,7 @@ Tcltest_Init( Tcl_CreateObjCommand(interp, "testlocale", TestlocaleCmd, (ClientData) 0, NULL); Tcl_CreateCommand(interp, "testpanic", TestpanicCmd, (ClientData) 0, NULL); + Tcl_CreateCommand(interp, "testfinexit", TestfinexitObjCmd, (ClientData) 0, NULL); Tcl_CreateObjCommand(interp, "testparser", TestparserObjCmd, (ClientData) 0, NULL); Tcl_CreateObjCommand(interp, "testparsevar", TestparsevarObjCmd, @@ -4358,6 +4362,47 @@ TestpanicCmd( return TCL_OK; } + +/* + *---------------------------------------------------------------------- + * + * TestfinexitObjCmd -- + * + * Calls a variant of [exit] including the full finalization path. + * + * Results: + * Error, or doesn't return. + * + * Side effects: + * Exits application. + * + *---------------------------------------------------------------------- + */ + +static int +TestfinexitObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + int value; + + if ((objc != 1) && (objc != 2)) { + Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); + return TCL_ERROR; + } + + if (objc == 1) { + value = 0; + } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) { + return TCL_ERROR; + } + Tcl_Finalize(); + TclpExit(value); + /*NOTREACHED*/ + return TCL_ERROR; /* Better not ever reach this! */ +} static int TestfileCmd( |