diff options
author | griffin <briang42@easystreet.net> | 2023-06-20 01:28:25 (GMT) |
---|---|---|
committer | griffin <briang42@easystreet.net> | 2023-06-20 01:28:25 (GMT) |
commit | be39d74ee7ad07b23a55da56bcc926a5fcd1e1dc (patch) | |
tree | a0c4c577864e8a1f879d8642f026c6a1d09404c1 | |
parent | d8ce1ffde8b833f9a255676b3a916df861c3d8da (diff) | |
download | tcl-be39d74ee7ad07b23a55da56bcc926a5fcd1e1dc.zip tcl-be39d74ee7ad07b23a55da56bcc926a5fcd1e1dc.tar.gz tcl-be39d74ee7ad07b23a55da56bcc926a5fcd1e1dc.tar.bz2 |
Fix crash in BC execution when str concat and abstract list lindex operations have recursive BC execution.
Add testcase for this bug.
-rw-r--r-- | generic/tclExecute.c | 18 | ||||
-rw-r--r-- | generic/tclTestABSList.c | 299 | ||||
-rw-r--r-- | tests/abstractlist.test | 61 |
3 files changed, 371 insertions, 7 deletions
diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 80c7e51..336815d 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -2598,13 +2598,16 @@ TEBCresume( case INST_STR_CONCAT1: opnd = TclGetUInt1AtPtr(pc+1); + DECACHE_STACK_INFO(); objResultPtr = TclStringCat(interp, opnd, &OBJ_AT_DEPTH(opnd-1), TCL_STRING_IN_PLACE); if (objResultPtr == NULL) { + CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } + CACHE_STACK_INFO(); TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); NEXT_INST_V(2, opnd, 1); break; @@ -4670,6 +4673,7 @@ TEBCresume( /* special case for AbstractList */ if (TclObjTypeHasProc(valuePtr,indexProc)) { + DECACHE_STACK_INFO(); length = TclObjTypeHasProc(valuePtr, lengthProc)(valuePtr); if (TclGetIntForIndexM(interp, value2Ptr, length-1, &index)!=TCL_OK) { CACHE_STACK_INFO(); @@ -4765,11 +4769,13 @@ TEBCresume( index = TclIndexDecode(opnd, length-1); /* Compute value @ index */ + DECACHE_STACK_INFO(); if (Tcl_ObjTypeIndex(interp, valuePtr, index, &objResultPtr)!=TCL_OK) { CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } + CACHE_STACK_INFO(); pcAdjustment = 5; goto lindexFastPath2; @@ -4850,6 +4856,7 @@ TEBCresume( if (TclObjTypeHasProc(valuePtr, setElementProc)) { + DECACHE_STACK_INFO(); objResultPtr = Tcl_ObjTypeSetElement(interp, valuePtr, numIndices, &OBJ_AT_DEPTH(numIndices), OBJ_AT_TOS); @@ -4858,6 +4865,7 @@ TEBCresume( &OBJ_AT_DEPTH(numIndices), OBJ_AT_TOS); } if (!objResultPtr) { + CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } @@ -4865,7 +4873,7 @@ TEBCresume( /* * Set result. */ - + CACHE_STACK_INFO(); TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); NEXT_INST_V(5, numIndices+1, -1); @@ -4978,6 +4986,7 @@ TEBCresume( fromIdx = TclIndexDecode(fromIdx, objc - 1); if (TclObjTypeHasProc(valuePtr, sliceProc)) { + DECACHE_STACK_INFO(); if (Tcl_ObjTypeSlice(interp, valuePtr, fromIdx, toIdx, &objResultPtr) != TCL_OK) { objResultPtr = NULL; } @@ -4985,10 +4994,12 @@ TEBCresume( objResultPtr = TclListObjRange(interp, valuePtr, fromIdx, toIdx); } if (objResultPtr == NULL) { + CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } + CACHE_STACK_INFO(); TRACE_APPEND(("\"%.30s\"", O2S(objResultPtr))); NEXT_INST_F(9, 1, 1); @@ -5015,10 +5026,13 @@ TEBCresume( do { if (isAbstractList) { + DECACHE_STACK_INFO(); if (Tcl_ObjTypeIndex(interp, value2Ptr, i, &o) != TCL_OK) { + CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } + CACHE_STACK_INFO(); } else { Tcl_ListObjIndex(NULL, value2Ptr, i, &o); } @@ -6488,7 +6502,7 @@ TEBCresume( pc += 5 - infoPtr->loopCtTemp; - case INST_FOREACH_STEP: + case INST_FOREACH_STEP: /* TODO: address abstract list indexing here! */ /* * "Step" a foreach loop (i.e., begin its next iteration) by assigning * the next value list element to each loop var. diff --git a/generic/tclTestABSList.c b/generic/tclTestABSList.c index 0799886..8c2a26e 100644 --- a/generic/tclTestABSList.c +++ b/generic/tclTestABSList.c @@ -319,7 +319,8 @@ DupLStringRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr) memcpy(copyLString, srcLString, sizeof(LString)); copyLString->string = (char*)Tcl_Alloc(srcLString->allocated); - strcpy(copyLString->string, srcLString->string); + strncpy(copyLString->string, srcLString->string, srcLString->strlen); + copyLString->string[srcLString->strlen] = '\0'; copyLString->elements = NULL; Tcl_ObjInternalRep itr; itr.twoPtrValue.ptr1 = copyLString; @@ -903,17 +904,304 @@ lLStringObjCmd( } /* +** lgen - Derived from TIP 192 - Lazy Lists +** Generate a list using a command provided as argument(s). +** The command computes the value for a given index. +*/ + +/* + * Internal rep for the Generate Series + */ +typedef struct LgenSeries { + Tcl_Interp *interp; // used to evaluate gen script + Tcl_Size len; // list length + Tcl_Size nargs; // Number of arguments in genFn including "index" + Tcl_Obj *genFnObj; // The preformed command as a list. Index is set in + // the last element (last argument) +} LgenSeries; + +/* + * Evaluate the generation function. + * The provided funtion computes the value for a give index + */ +static Tcl_Obj* +lgen( + Tcl_Obj* objPtr, + Tcl_Size index) +{ + LgenSeries *lgenSeriesPtr = (LgenSeries*)objPtr->internalRep.twoPtrValue.ptr1; + Tcl_Obj *elemObj = NULL; + Tcl_Interp *intrp = lgenSeriesPtr->interp; + Tcl_Obj *genCmd = lgenSeriesPtr->genFnObj; + Tcl_Size endidx = lgenSeriesPtr->nargs-1; + + if (0 <= index && index < lgenSeriesPtr->len) { + Tcl_Obj *indexObj = Tcl_NewWideIntObj(index); + Tcl_ListObjReplace(intrp, genCmd, endidx, 1, 1, &indexObj); + // EVAL DIRECT to avoid interfering with bytecode compile which may be + // active on the stack + int flags = TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT; + int status = Tcl_EvalObjEx(intrp, genCmd, flags); + elemObj = Tcl_GetObjResult(intrp); + if (status != TCL_OK) { + fprintf(stderr,"Error: %s\nwhile executing %s\n", + elemObj ? Tcl_GetString(elemObj) : "NULL", + Tcl_GetString(genCmd)); + } + // Interp may be only holder of the result, + // incr refCount to hold on to it. + Tcl_IncrRefCount(elemObj); + } + return elemObj; +} + +/* + * Abstract List Length function + */ +static Tcl_Size +lgenSeriesObjLength(Tcl_Obj *objPtr) +{ + LgenSeries *lgenSeriesRepPtr = (LgenSeries *)objPtr->internalRep.twoPtrValue.ptr1; + return lgenSeriesRepPtr->len; +} + +/* + * Abstract List Index function + */ +static int +lgenSeriesObjIndex( + Tcl_Interp *interp, + Tcl_Obj *lgenSeriesObjPtr, + Tcl_Size index, + Tcl_Obj **elemPtr) +{ + LgenSeries *lgenSeriesRepPtr; + Tcl_Obj *element; + + lgenSeriesRepPtr = (LgenSeries*)lgenSeriesObjPtr->internalRep.twoPtrValue.ptr1; + + if (index < 0 || index >= lgenSeriesRepPtr->len) + return TCL_ERROR; + + if (lgenSeriesRepPtr->interp == NULL && interp == NULL) { + return TCL_ERROR; + } + + lgenSeriesRepPtr->interp = interp; + + element = lgen(lgenSeriesObjPtr, index); + if (element) { + *elemPtr = element; + } else { + return TCL_ERROR; + } + + return TCL_OK; +} + +/* +** UpdateStringRep +*/ + +static void +UpdateStringOfLgen(Tcl_Obj *objPtr) +{ + LgenSeries *lgenSeriesRepPtr; + Tcl_Obj *element; + Tcl_Size i; + size_t bytlen; + Tcl_Obj *tmpstr = Tcl_NewObj(); + + lgenSeriesRepPtr = (LgenSeries*)objPtr->internalRep.twoPtrValue.ptr1; + + for (i=0, bytlen=0; i<lgenSeriesRepPtr->len; i++) { + element = lgen(objPtr, i); + if (element) { + if (i) { + Tcl_AppendToObj(tmpstr," ",1); + } + Tcl_AppendObjToObj(tmpstr,element); + } + } + + bytlen = Tcl_GetCharLength(tmpstr); + Tcl_InitStringRep(objPtr, Tcl_GetString(tmpstr), bytlen); + Tcl_DecrRefCount(tmpstr); + + return; +} + +/* + * ObjType Free Internal Rep function + */ +static void +FreeLgenInternalRep(Tcl_Obj *objPtr) +{ + LgenSeries *lgenSeries = (LgenSeries*)objPtr->internalRep.twoPtrValue.ptr1; + if (lgenSeries->genFnObj) { + Tcl_DecrRefCount(lgenSeries->genFnObj); + } + lgenSeries->interp = NULL; + Tcl_Free(lgenSeries); + objPtr->internalRep.twoPtrValue.ptr1 = 0; +} + +static void DupLgenSeriesRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); + +/* + * Abstract List ObjType definition + */ + +static Tcl_ObjType lgenType = { + "lgenseries", + FreeLgenInternalRep, + DupLgenSeriesRep, + UpdateStringOfLgen, + NULL, /* SetFromAnyProc */ + TCL_OBJTYPE_V2( + lgenSeriesObjLength, + lgenSeriesObjIndex, + NULL, /* slice */ + NULL, /* reverse */ + NULL, /* get elements */ + NULL, /* set element */ + NULL) /* replace */ +}; + +/* + * ObjType Duplicate Internal Rep Function + */ +static void +DupLgenSeriesRep( + Tcl_Obj *srcPtr, + Tcl_Obj *copyPtr) +{ + LgenSeries *srcLgenSeries = (LgenSeries*)srcPtr->internalRep.twoPtrValue.ptr1; + Tcl_Size repSize = sizeof(LgenSeries); + LgenSeries *copyLgenSeries = (LgenSeries*)Tcl_Alloc(repSize); + + copyLgenSeries->interp = srcLgenSeries->interp; + copyLgenSeries->nargs = srcLgenSeries->nargs; + copyLgenSeries->len = srcLgenSeries->len; + copyLgenSeries->genFnObj = Tcl_DuplicateObj(srcLgenSeries->genFnObj); + Tcl_IncrRefCount(copyLgenSeries->genFnObj); + copyPtr->typePtr = &lgenType; + copyPtr->internalRep.twoPtrValue.ptr1 = copyLgenSeries; + copyPtr->internalRep.twoPtrValue.ptr2 = NULL; + return; +} + +/* + * Create a new lgen Tcl_Obj + */ +Tcl_Obj * +newLgenObj( + Tcl_Interp *interp, + int objc, + Tcl_Obj * const objv[]) +{ + Tcl_WideInt length; + LgenSeries *lGenSeriesRepPtr; + Tcl_Size repSize; + Tcl_Obj *lGenSeriesObj; + + if (objc < 2) { + return NULL; + } + + if (Tcl_GetWideIntFromObj(NULL, objv[0], &length) != TCL_OK + || length < 0) { + return NULL; + } + + lGenSeriesObj = Tcl_NewObj(); + repSize = sizeof(LgenSeries); + lGenSeriesRepPtr = (LgenSeries*)Tcl_Alloc(repSize); + lGenSeriesRepPtr->interp = interp; //Tcl_CreateInterp(); + lGenSeriesRepPtr->len = length; + + // Allocate array of *obj for cmd + index + args + // objv length cmd arg1 arg2 arg3 ... + // argsv 0 1 2 3 ... index + + lGenSeriesRepPtr->nargs = objc; + lGenSeriesRepPtr->genFnObj = Tcl_NewListObj(objc-1, objv+1); + // Addd 0 placeholder for index + Tcl_ListObjAppendElement(interp, lGenSeriesRepPtr->genFnObj, Tcl_NewIntObj(0)); + Tcl_IncrRefCount(lGenSeriesRepPtr->genFnObj); + lGenSeriesObj->internalRep.twoPtrValue.ptr1 = lGenSeriesRepPtr; + lGenSeriesObj->internalRep.twoPtrValue.ptr2 = NULL; + lGenSeriesObj->typePtr = &lgenType; + + if (length > 0) { + Tcl_InvalidateStringRep(lGenSeriesObj); + } else { + Tcl_InitStringRep(lGenSeriesObj, NULL, 0); + } + return lGenSeriesObj; +} + +/* + * The [lgen] command + */ +static int +lGenObjCmd( + TCL_UNUSED(void *), + Tcl_Interp *interp, + int objc, + Tcl_Obj * const objv[]) +{ + Tcl_Obj *genObj = newLgenObj(interp, objc-1, &objv[1]); + if (genObj) { + Tcl_SetObjResult(interp, genObj); + return TCL_OK; + } + Tcl_WrongNumArgs(interp, 1, objv, "length cmd ?args?"); + return TCL_ERROR; +} + +/* + * lgen package init + */ +int Lgen_Init(Tcl_Interp *interp) { + if (Tcl_InitStubs(interp, "8.7", 0) == NULL) { + return TCL_ERROR; + } + Tcl_CreateObjCommand(interp, "lgen", lGenObjCmd, NULL, NULL); + Tcl_PkgProvide(interp, "lgen", "1.0"); + return TCL_OK; +} + + + +/* *---------------------------------------------------------------------- * - * Lstring_Init -- + * ABSListTest_Init -- + * + * Provides Abstract List implemenations via new commands + * + * lstring command + * Usage: + * lstring /string/ + * + * Description: + * Creates a list where each character in the string is treated as an + * element. The string is kept as a string, not an actual list. Indexing + * is done by char. + * + * lgen command + * Usage: + * lgen /length/ /cmd/ ?args...? * - * DL load init function. Defines the "lstring" command. + * The /cmd/ should take the last argument as the index value, and return + * a value for that element. * * Results: - * "lstring" command added to the interp. + * The commands listed above are added to the interp. * * Side effects: - * A new command is defined. + * New commands defined. * *---------------------------------------------------------------------- */ @@ -923,6 +1211,7 @@ int Tcl_ABSListTest_Init(Tcl_Interp *interp) { return TCL_ERROR; } Tcl_CreateObjCommand(interp, "lstring", lLStringObjCmd, NULL, NULL); + Tcl_CreateObjCommand(interp, "lgen", lGenObjCmd, NULL, NULL); Tcl_PkgProvide(interp, "abstractlisttest", "1.0.0"); return TCL_OK; } diff --git a/tests/abstractlist.test b/tests/abstractlist.test index 6930446..cca24c5 100644 --- a/tests/abstractlist.test +++ b/tests/abstractlist.test @@ -513,6 +513,67 @@ testConstraint [format "%sShimmer" [string totitle $not]] 1 } +# +# Test fix for bug in TEBC for STR CONCAT, and LIST INDEX +# instructions. +# This example abstract list (lgen) causes a rescursive call in TEBC, +# stack management was not included for these instructions in TEBC. +# +test abstractlist-lgen-bug {} -setup { + set lgenfile [makeFile { + # Test TIP 192 - Lazy Lists + set cntr 0 + # Fatal error here when [source]'d -- It is a refcounting problem... + lappend res Index*2:[lgen 1 expr 2* ]:-- + set x [lseq 17] + set y [lgen 17 apply {{index} {expr {$index * 6}}}] ;# expr * 6 + foreach i $x n $y { + lappend res "$i -> $n" + } + proc my_expr {offset index} { + expr {$index + $offset} + } + lappend res my_expr(3):[my_expr 3 0] + + lappend res [set ss [lgen 15 my_expr 7]] + lappend res s2:[list "Index+7:" $ss ":--"] + + lappend res foo:[list "Index-8:" [lgen 15 my_expr -8] ":--"] + + set 9 [lgen 15 my_expr 7] + lappend res 9len=[llength $9] + lappend res 9(3)=[lindex $9 3] + lappend res bar:[list "Index+7:" $9 ":--"] + + lappend res Index+7:$9:-- + + lappend res Index+7:[lgen 15 my_expr 7]:-- + + proc fib {phi n} { + set d [expr {round(pow($phi, $n) / sqrt(5.0))}] + return $d + } + set phi [expr {(1 + sqrt(5.0)) / 2.0}] + + lappend res fib:[lmap n [lseq 5] {fib $phi $n}] + + set x [lgen 20 fib $phi] + lappend res "First 20 fibbinacci:[lgen 20 fib $phi]" + lappend res "First 20 fibbinacci from x :$x" + unset x + lappend res Good-Bye! + set res + } lgen.tcl] +} -body { + set tcl_traceExec 0 + set tcl_traceCompile 0 + set f $lgenfile + #set script [format "puts ====-%s-====\nsource %s\nputs ====-done-====\n" $f $f] + set script [format "source %s" $f] + #puts stderr "eval $script" + eval $script +} -result {lstring {V i z z i n i : { } H E { } a l m o s t { } D I D N ' T { } F A L L ? { } I N C O N C E I V A B L E . { } I n i g o { } M o n t o y a : { } Y o u { } k e e p { } u s i n g { } t h a t { } w o r d . { } I { } d o { } n o t { } t h i n k { } i t { } m e a n s { } w h a t { } y o u { } t h i n k { } i t { } m e a n s .} lstring ' none {V i z z i n i : { } H E { } a l m o s t { } D I D N T { } F A L L ? { } I N C O N C E I V A B L E . { } I n i g o { } M o n t o y a : { } Y o u { } k e e p { } u s i n g { } t h a t { } w o r d . { } I { } d o { } n o t { } t h i n k { } i t { } m e a n s { } w h a t { } y o u { } t h i n k { } i t { } m e a n s .} lstring Index*2:0:-- {0 -> 0} {1 -> 6} {2 -> 12} {3 -> 18} {4 -> 24} {5 -> 30} {6 -> 36} {7 -> 42} {8 -> 48} {9 -> 54} {10 -> 60} {11 -> 66} {12 -> 72} {13 -> 78} {14 -> 84} {15 -> 90} {16 -> 96} my_expr(3):3 {7 8 9 10 11 12 13 14 15 16 17 18 19 20 21} {s2:Index+7: {7 8 9 10 11 12 13 14 15 16 17 18 19 20 21} :--} {foo:Index-8: {-8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6} :--} 9len=15 9(3)=10 {bar:Index+7: {7 8 9 10 11 12 13 14 15 16 17 18 19 20 21} :--} {Index+7:7 8 9 10 11 12 13 14 15 16 17 18 19 20 21:--} {Index+7:7 8 9 10 11 12 13 14 15 16 17 18 19 20 21:--} {fib:0 1 1 2 3} {First 20 fibbinacci:0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181} {First 20 fibbinacci from x :0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181} Good-Bye!} + # lsort # cleanup |