diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | generic/tclBasic.c | 4 | ||||
-rw-r--r-- | generic/tclCompile.c | 64 |
3 files changed, 78 insertions, 0 deletions
@@ -1,3 +1,13 @@ +2011-05-17 Andreas Kupries <andreask@activestate.com> + + * generic/tclCompile.c (TclFixupForwardJump): Tracked down and fixed + * generic/tclBasic.c (TclArgumentBCEnter): the cause of a violation + of my assertion that 'ePtr->nline == objc' in TclArgumentBCEnter. + When a bytecode was grown during jump fixup the pc -> command line + mapping was not updated. When things aligned just wrong the mapping + would direct command A to the data for command B, with a different + number of arguments. + 2011-05-10 Don Porter <dgp@users.sourceforge.net> * generic/tclInt.h: New internal routines TclScanElement() and diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 71bd45c..750c9e2 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4764,6 +4764,10 @@ TclArgumentBCEnter(interp,objv,objc,codePtr,cfPtr,pc) * have to save them at compile time. */ + if (ePtr->nline != objc) { + Tcl_Panic ("TIP 280 data structure inconsistency"); + } + for (word = 1; word < objc; word++) { if (ePtr->line[word] >= 0) { int isnew; diff --git a/generic/tclCompile.c b/generic/tclCompile.c index 2d8d58c..8a7cbf8 100644 --- a/generic/tclCompile.c +++ b/generic/tclCompile.c @@ -3020,6 +3020,70 @@ TclFixupForwardJump( rangePtr->type); } } + + /* + * TIP #280: Adjust the mapping from PC values to the per-command + * information about arguments and their line numbers. + * + * Note: We cannot simply remove an out-of-date entry and then reinsert + * with the proper PC, because then we might overwrite another entry which + * was at that location. Therefore we pull (copy + delete) all effected + * entries (beyond the fixed PC) into an array, update them there, and at + * last reinsert them all. + */ + + { + ExtCmdLoc* eclPtr = envPtr->extCmdMapPtr; + + /* A helper structure */ + + typedef struct { + int pc; + int cmd; + } MAP; + + /* + * And the helper array. At most the whole hashtable is placed into + * this. + */ + + MAP *map = (MAP*) ckalloc (sizeof(MAP) * eclPtr->litInfo.numEntries); + + Tcl_HashSearch hSearch; + Tcl_HashEntry* hPtr; + int n, k, isnew; + + /* + * Phase I: Locate the affected entries, and save them in adjusted + * form to the array. This removes them from the hash. + */ + + for (n = 0, hPtr = Tcl_FirstHashEntry(&eclPtr->litInfo, &hSearch); + hPtr != NULL; + hPtr = Tcl_NextHashEntry(&hSearch)) { + + map [n].cmd = PTR2INT(Tcl_GetHashValue(hPtr)); + map [n].pc = PTR2INT(Tcl_GetHashKey (&eclPtr->litInfo,hPtr)); + + if (map[n].pc >= (jumpFixupPtr->codeOffset + 2)) { + Tcl_DeleteHashEntry(hPtr); + map [n].pc += 3; + n++; + } + } + + /* + * Phase II: Re-insert the modified entries into the hash. + */ + + for (k=0;k<n;k++) { + hPtr = Tcl_CreateHashEntry(&eclPtr->litInfo, INT2PTR(map[k].pc), &isnew); + Tcl_SetHashValue(hPtr, INT2PTR(map[k].cmd)); + } + + ckfree (map); + } + return 1; /* the jump was grown */ } |