Posted to tcl by cjo at Mon Sep 11 14:01:43 GMT 2017view pretty
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 5a33445..e5b09b8 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -6141,7 +6141,10 @@ TclNREvalObjEx( iPtr->varFramePtr = iPtr->rootFramePtr; } Tcl_IncrRefCount(objPtr); - codePtr = TclCompileObj(interp, objPtr, invoker, word); + ByteCodeGetIntRep(objPtr, &tclByteCodeType, codePtr); + if (codePtr == NULL) { + codePtr = TclCompileObj(interp, objPtr, invoker, word); + } TclNRAddCallback(interp, TEOEx_ByteCodeCallback, savedVarFramePtr, objPtr, INT2PTR(allowExceptions), NULL); diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 59d83b8..282e70b 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -1836,6 +1836,8 @@ EXTERN void Tcl_StoreIntRep(Tcl_Obj *objPtr, const Tcl_ObjIntRep *irPtr); /* 636 */ EXTERN int Tcl_HasStringRep(Tcl_Obj *objPtr); +/* 637 */ +EXTERN void Tcl_FreeOtherIntReps(Tcl_Obj *objPtr, const Tcl_ObjType *typePtr); typedef struct { const struct TclPlatStubs *tclPlatStubs; @@ -2508,6 +2510,7 @@ typedef struct TclStubs { Tcl_ObjIntRep * (*tcl_FetchIntRep) (Tcl_Obj *objPtr, const Tcl_ObjType *typePtr); /* 634 */ void (*tcl_StoreIntRep) (Tcl_Obj *objPtr, const Tcl_ObjType *typePtr, const Tcl_ObjIntRep *irPtr); /* 635 */ int (*tcl_HasStringRep) (Tcl_Obj *objPtr); /* 636 */ + void (*tcl_FreeOtherIntReps) (Tcl_Obj *objPtr, const Tcl_ObjType *typePtr); /* 637 */ } TclStubs; extern const TclStubs *tclStubsPtr; @@ -3812,6 +3815,8 @@ extern const TclStubs *tclStubsPtr; (tclStubsPtr->tcl_StoreIntRep) /* 635 */ #define Tcl_HasStringRep \ (tclStubsPtr->tcl_HasStringRep) /* 636 */ +#define Tcl_FreeOtherIntReps \ + (tclStubsPtr->tcl_FreeOtherIntReps) /* 636 */ #endif /* defined(USE_TCL_STUBS) */ diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index f4e15a6..e4b06bc 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -959,6 +959,7 @@ Tcl_DictObjPut( return TCL_ERROR; } + Tcl_FreeOtherIntReps(dictPtr, &tclDictType); TclInvalidateStringRep(dictPtr); hPtr = CreateChainEntry(dict, keyPtr, &isNew); Tcl_IncrRefCount(valuePtr); diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 8389bef..728e52c 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -9742,8 +9742,13 @@ TclGetSrcInfoForPc( ECL *locPtr = NULL; int srcOffset, i; Interp *iPtr = (Interp *) *codePtr->interpHandle; - Tcl_HashEntry *hePtr = - Tcl_FindHashEntry(iPtr->lineBCPtr, codePtr); + Tcl_HashEntry *hePtr; + + if (!iPtr->lineBCPtr) { + fprintf(stderr, "TclGetSrcInfoForPc: iPtr->lineBCPtr is invalid\n"); + return; + } + hePtr = Tcl_FindHashEntry(iPtr->lineBCPtr, codePtr); if (!hePtr) { return; diff --git a/generic/tclListObj.c b/generic/tclListObj.c index c33b95e..6e81a23 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -66,7 +66,13 @@ const Tcl_ObjType tclListType = { } while (0) #define ListResetIntRep(objPtr, listRepPtr) \ - Tcl_FetchIntRep((objPtr), &tclListType)->twoPtrValue.ptr1 = (listRepPtr) + do { \ + Tcl_ObjIntRep *irPtr; \ + Tcl_FreeOtherIntReps((objPtr), &tclListType); \ + irPtr = Tcl_FetchIntRep((objPtr), &tclListType); \ + irPtr->twoPtrValue.ptr1 = (listRepPtr); \ + irPtr->twoPtrValue.ptr2 = NULL; \ + } while (0) #ifndef TCL_MIN_ELEMENT_GROWTH #define TCL_MIN_ELEMENT_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Obj *) @@ -1257,7 +1263,7 @@ TclLindexFlat( int index, listLen = 0; Tcl_Obj **elemPtrs = NULL, *sublistCopy; - /* + /* TODO hydra: is this still necessary if shimmering is no longer an issue? * Here we make a private copy of the current sublist, so we avoid any * shimmering issues that might invalidate the elemPtr array below * while we are still using it. See test lindex-8.4. diff --git a/generic/tclObj.c b/generic/tclObj.c index 6e4011e..8bf69ce 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -206,6 +206,10 @@ static Tcl_ThreadDataKey pendingObjDataKey; * Prototypes for functions defined later in this file: */ +static void FreeHydra(Tcl_Obj *objPtr); +static void DupHydra(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); +static void UpdateStringOfHydra(Tcl_Obj *objPtr); +static int SetHydraFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static int ParseBoolean(Tcl_Obj *objPtr); static int SetDoubleFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static int SetIntFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); @@ -243,6 +247,26 @@ static int SetCmdNameFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); * implementations. */ +#define MAX_HYDRA_CLIENTS 5 +static const Tcl_ObjType tclHydraType = { + "hydra", /* name */ + FreeHydra, /* freeIntRepProc */ + DupHydra, /* dupIntRepProc */ + UpdateStringOfHydra, /* updateStringProc */ + SetHydraFromAny /* setFromAnyProc */ +}; + +#define HydraGetIntRep(objPtr, hydraPtr) \ + (hydraPtr) = (Hydra *)((objPtr)->internalRep.twoPtrValue.ptr1) + +typedef struct HydraClient { + const Tcl_ObjType *typePtr; + Tcl_ObjIntRep internalRep; +} HydraClient; +typedef struct Hydra { + HydraClient client[MAX_HYDRA_CLIENTS]; +} Hydra; + static const Tcl_ObjType oldBooleanType = { "boolean", /* name */ NULL, /* freeIntRepProc */ @@ -1694,6 +1718,175 @@ Tcl_GetStringFromObj( return objPtr->bytes; } +static void +FreeHydra( + Tcl_Obj *objPtr) +{ + int i; + Hydra *hydraPtr; + Tcl_Obj fakeObj; + + memset(&fakeObj, 0, sizeof(fakeObj)); + fakeObj.refCount = 10; + HydraGetIntRep(objPtr, hydraPtr); + + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr) { + if (clientPtr->typePtr->freeIntRepProc) { + fakeObj.internalRep = hydraPtr->client[i].internalRep; + fakeObj.typePtr = hydraPtr->client[i].typePtr; + Tcl_FreeIntRep(&fakeObj); + + if (fakeObj.refCount != 10) { + Tcl_Panic("Invalid reference taken to fakeObj while " + "freeing intrep for %s", + hydraPtr->client[i].typePtr->name); + } + } + + hydraPtr->client[i].typePtr = NULL; + memset(&hydraPtr->client[i].internalRep, 0, sizeof(hydraPtr->client[i].internalRep)); + } + } + + Tcl_Free((char *)hydraPtr); + hydraPtr = objPtr->internalRep.twoPtrValue.ptr1 = NULL; +} + +static void +DupHydra( + Tcl_Obj *srcPtr, + Tcl_Obj *copyPtr) +{ + int i; + Hydra *hydraPtr; + Hydra *hydraCopyPtr; + Tcl_Obj fakeSrcObj, fakeCopyObj; + + if (Tcl_HasStringRep(srcPtr) && srcPtr->bytes == &tclEmptyString) { + return; + } + + memset(&fakeSrcObj, 0, sizeof(fakeSrcObj)); + fakeSrcObj.refCount = 10; + memset(&fakeCopyObj, 0, sizeof(fakeCopyObj)); + fakeCopyObj.refCount = 10; + + HydraGetIntRep(srcPtr, hydraPtr); + + hydraCopyPtr = (Hydra *)Tcl_Alloc(sizeof(Hydra)); + memset(hydraCopyPtr, 0, sizeof(*hydraCopyPtr)); + + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + HydraClient *clientCopyPtr = &hydraCopyPtr->client[i]; + + if (clientPtr->typePtr) { + if (clientPtr->typePtr->dupIntRepProc) { + fakeCopyObj.typePtr = NULL; + fakeSrcObj.internalRep = hydraPtr->client[i].internalRep; + fakeSrcObj.typePtr = hydraPtr->client[i].typePtr; + + fakeSrcObj.typePtr->dupIntRepProc(&fakeSrcObj, &fakeCopyObj); + + if (fakeSrcObj.refCount != 10) { + Tcl_Panic("Invalid reference taken to fakeSrcObj while " + "duplicating intrep for %s", + hydraPtr->client[i].typePtr->name); + } + if (fakeCopyObj.refCount != 10) { + Tcl_Panic("Invalid reference taken to fakeCopyObj while " + "duplicating intrep for %s", + hydraPtr->client[i].typePtr->name); + } + + if (clientCopyPtr->internalRep.twoPtrValue.ptr1 || clientCopyPtr->internalRep.twoPtrValue.ptr2) { + clientCopyPtr->internalRep = fakeCopyObj.internalRep; + clientCopyPtr->typePtr = fakeCopyObj.typePtr; + } + } + } + } + + TclFreeIntRep(copyPtr); /* Paranoia? */ + copyPtr->internalRep.twoPtrValue.ptr1 = hydraPtr; + copyPtr->typePtr = &tclHydraType; +} + +static void +UpdateStringOfHydra( + Tcl_Obj *objPtr) +{ + int i; + Hydra *hydraPtr; + Tcl_Obj fakeObj; + + memset(&fakeObj, 0, sizeof(fakeObj)); + fakeObj.refCount = 10; + + HydraGetIntRep(objPtr, hydraPtr); + + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr) { + if (clientPtr->typePtr->updateStringProc) { + fakeObj.internalRep = hydraPtr->client[i].internalRep; + fakeObj.typePtr = hydraPtr->client[i].typePtr; + /* Don't know if this is necessary */ + fakeObj.bytes = NULL; + fakeObj.length = 0; + + fakeObj.typePtr->updateStringProc(&fakeObj); + + if (fakeObj.refCount != 10) { + Tcl_Panic("Invalid reference taken to fakeObj while " + "updating string rep using %s", + hydraPtr->client[i].typePtr->name); + } + + if (TclHasStringRep(&fakeObj)) { /* Not sure about this */ + objPtr->bytes = fakeObj.bytes; + objPtr->length = fakeObj.length; + fakeObj.bytes = NULL; + fakeObj.length = 0; + return; + } + } + } + } + + /* TODO: what? */ + Tcl_Panic("Could not update string rep of hydra: %s", + "No clients capable of regenerating string rep found"); +} + +static int +SetHydraFromAny( + Tcl_Interp *interp, + Tcl_Obj *objPtr) +{ + Hydra *hydraPtr; + + if (objPtr->typePtr == &tclHydraType) { + return TCL_OK; + } + + hydraPtr = (Hydra *)Tcl_Alloc(sizeof(Hydra)); + memset(hydraPtr, 0, sizeof(Hydra)); + + hydraPtr->client[0].typePtr = objPtr->typePtr; + hydraPtr->client[0].internalRep = objPtr->internalRep; + + objPtr->internalRep.twoPtrValue.ptr1 = hydraPtr; + objPtr->internalRep.twoPtrValue.ptr2 = NULL; + objPtr->typePtr = &tclHydraType; + + return TCL_OK; +} + /* *---------------------------------------------------------------------- * @@ -1852,16 +2045,125 @@ Tcl_StoreIntRep( const Tcl_ObjType *typePtr, /* New type for the object */ const Tcl_ObjIntRep *irPtr) /* New IntRep for the object */ { - /* Clear out any existing IntRep ( "shimmer" ) */ - TclFreeIntRep(objPtr); + int i; + Hydra *hydraPtr; - /* When irPtr == NULL, just leave objPtr with no IntRep for typePtr */ - if (irPtr) { - /* Copy the new IntRep into place */ + if (objPtr->typePtr == NULL) { + /* Special case - updating (or clearing) a pure string object */ + TclFreeIntRep(objPtr); objPtr->internalRep = *irPtr; - - /* Set the type to match */ objPtr->typePtr = typePtr; + return; + } + + if (objPtr->typePtr == typePtr) { + /* Special case - updating (or clearing) an object's existing intrep */ + TclFreeIntRep(objPtr); + if (irPtr) { + objPtr->internalRep = *irPtr; + } else { + memset(&objPtr->internalRep, 0, sizeof(objPtr->internalRep)); + } + return; + } + + if (objPtr->typePtr != &tclHydraType) { + SetHydraFromAny(NULL, objPtr); + } + + HydraGetIntRep(objPtr, hydraPtr); + + if (irPtr) { + int firstAvailableSlot = -1; + + /* If we have an existing client with a matching type, we need to + * update that intrep even if there is an open slot before it */ + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr == typePtr) { + if (irPtr) { + /* In the case where we are updating an intrep for a type we + * already have, take that as a sign that the value has changed + * ([incr], [lset], [append], etc), and free all the others. + * Since we will be left with a single intrep, drop back to a + * simple object (non-hydra) */ + Tcl_FreeIntRep(objPtr); + objPtr->internalRep = *irPtr; + objPtr->typePtr = typePtr; + } else { + if (clientPtr->typePtr->freeIntRepProc) { + /* Free the matching client intrep, using a fake obj */ + Tcl_Obj fakeObj; + + memset(&fakeObj, 0, sizeof(fakeObj)); + fakeObj.refCount = 10; + fakeObj.typePtr = typePtr; + fakeObj.internalRep = clientPtr->internalRep; + TclFreeIntRep(&fakeObj); + + if (fakeObj.refCount != 10) { + Tcl_Panic("Invalid reference taken to fakeObj while " + "freeing hydra client interp for %s", + typePtr->name); + } + } + + /* Mark the client slot as available */ + clientPtr->typePtr = NULL; + memset(&clientPtr->internalRep, 0, sizeof(clientPtr->internalRep)); + return; + } + return; + } else if (firstAvailableSlot == -1 && clientPtr->typePtr == NULL) { + /* Record the first available slot in case we need to add this + * intrep there */ + firstAvailableSlot = i; + } + } + + if (firstAvailableSlot > -1) { + HydraClient *clientPtr = &hydraPtr->client[firstAvailableSlot]; + + if (clientPtr->typePtr == NULL) { + /* Found available slot, put this intrep there */ + clientPtr->internalRep = *irPtr; + clientPtr->typePtr = typePtr; + return; + } + } + + /* No available client slots. Upconvert to linked list? */ + Tcl_Panic("Unable to add client intrep for %s to hydra: " + "No slots available", typePtr->name); + } else { + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr == typePtr) { + if (clientPtr->typePtr->freeIntRepProc) { + /* Free the matching client intrep, using a fake obj */ + Tcl_Obj fakeObj; + + memset(&fakeObj, 0, sizeof(fakeObj)); + fakeObj.refCount = 10; + fakeObj.typePtr = typePtr; + fakeObj.internalRep = clientPtr->internalRep; + TclFreeIntRep(&fakeObj); + + if (fakeObj.refCount != 10) { + Tcl_Panic("Invalid reference taken to fakeObj while " + "freeing hydra client interp for %s", + typePtr->name); + } + } + + /* Mark the client slot as available */ + clientPtr->typePtr = NULL; + memset(&clientPtr->internalRep, 0, sizeof(clientPtr->internalRep)); + return; + } + } } } @@ -1878,8 +2180,7 @@ Tcl_StoreIntRep( * NULL if no such internal representation exists. * * Side effects: - * Calls the freeIntRepProc of the current Tcl_ObjType, if any. - * Sets the internalRep and typePtr fields to the submitted values. + * None. * *---------------------------------------------------------------------- */ @@ -1889,13 +2190,27 @@ Tcl_FetchIntRep( Tcl_Obj *objPtr, /* Object to fetch from. */ const Tcl_ObjType *typePtr) /* Requested type */ { - /* If objPtr type doesn't match request, nothing can be fetched */ - if (objPtr->typePtr != typePtr) { - return NULL; + if (objPtr->typePtr == typePtr) { + /* Type match! objPtr IntRep is the one sought. */ + return &(objPtr->internalRep); } - /* Type match! objPtr IntRep is the one sought. */ - return &(objPtr->internalRep); + if (objPtr->typePtr == &tclHydraType) { + int i; + Hydra *hydraPtr; + + HydraGetIntRep(objPtr, hydraPtr); + + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr == typePtr) { + return &(clientPtr->internalRep); + } + } + } + + return NULL; } /* @@ -1922,6 +2237,43 @@ Tcl_FreeIntRep( TclFreeIntRep(objPtr); } +void +Tcl_FreeOtherIntReps( + Tcl_Obj *objPtr, + const Tcl_ObjType *typePtr) +{ + if (objPtr->typePtr == NULL || objPtr->typePtr == typePtr) { + return; + } + + if (objPtr->typePtr == &tclHydraType) { + int i; + Hydra *hydraPtr; + Tcl_Obj fakeObj; + + HydraGetIntRep(objPtr, hydraPtr); + memset(&fakeObj, 0, sizeof(fakeObj)); + + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr != NULL && clientPtr->typePtr != typePtr) { + if (clientPtr->typePtr->freeIntRepProc) { + fakeObj.typePtr = clientPtr->typePtr; + fakeObj.internalRep = clientPtr->internalRep; + fakeObj.typePtr->freeIntRepProc(&fakeObj); + } + clientPtr->typePtr = NULL; + memset(&clientPtr->internalRep, 0, sizeof(clientPtr->internalRep)); + } + } + + /* Convert back to a plain object of type typePtr */ + objPtr->internalRep = fakeObj.internalRep; + objPtr->typePtr = typePtr; + Tcl_Free((char *)hydraPtr); + } +} /* *---------------------------------------------------------------------- * @@ -4694,6 +5046,32 @@ Tcl_RepresentationCmd( Tcl_AppendToObj(descObj, ", no string representation", -1); } + if (objv[1]->typePtr == &tclHydraType) { + int i; + Hydra *hydraPtr; + + Tcl_AppendToObj(descObj, ", with client representations:", -1); + HydraGetIntRep(objv[1], hydraPtr); + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + HydraClient *clientPtr = &hydraPtr->client[i]; + + if (clientPtr->typePtr == NULL) { + continue; + } + + Tcl_AppendPrintfToObj(descObj, "\n\t%d: %s", i, clientPtr->typePtr->name); + + if (clientPtr->typePtr == &tclDoubleType) { + Tcl_AppendPrintfToObj(descObj, ", internal representation %g", + clientPtr->internalRep.doubleValue); + } else { + Tcl_AppendPrintfToObj(descObj, ", internal representation %p:%p", + (void *) clientPtr->internalRep.twoPtrValue.ptr1, + (void *) clientPtr->internalRep.twoPtrValue.ptr2); + } + } + } + Tcl_SetObjResult(interp, descObj); return TCL_OK; } diff --git a/generic/tclStringRep.h b/generic/tclStringRep.h index 1ef1957..506d2f7 100644 --- a/generic/tclStringRep.h +++ b/generic/tclStringRep.h @@ -84,9 +84,21 @@ typedef struct { #define stringAttemptRealloc(ptr, numChars) \ (String *) attemptckrealloc((ptr), STRING_SIZE(numChars)) #define GET_STRING(objPtr) \ - ((String *) (objPtr)->internalRep.twoPtrValue.ptr1) + ((String *) ((Tcl_FetchIntRep((objPtr), &tclStringType))->twoPtrValue.ptr1)) #define SET_STRING(objPtr, stringPtr) \ - ((objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (stringPtr)) + do { \ + Tcl_ObjIntRep *irPtr; \ + Tcl_FreeOtherIntReps((objPtr), &tclStringType); \ + irPtr = Tcl_FetchIntRep((objPtr), &tclStringType); \ + if (irPtr == NULL) { \ + Tcl_ObjIntRep newIr; \ + newIr.twoPtrValue.ptr1 = (void *) (stringPtr); \ + newIr.twoPtrValue.ptr2 = NULL; \ + Tcl_StoreIntRep((objPtr), &tclStringType, &newIr); \ + } else { \ + irPtr->twoPtrValue.ptr1 = (void *) (stringPtr); \ + } \ + } while (0) /* * Local Variables: diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 8ff6291..449464a 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -1531,6 +1531,7 @@ const TclStubs tclStubs = { Tcl_FetchIntRep, /* 634 */ Tcl_StoreIntRep, /* 635 */ Tcl_HasStringRep, /* 636 */ + Tcl_FreeOtherIntReps, /* 637 */ }; /* !END!: Do not edit above this line. */ diff --git a/generic/tclVar.c b/generic/tclVar.c index 4c3d4a1..fa65be4 100644 --- a/generic/tclVar.c +++ b/generic/tclVar.c @@ -5564,10 +5564,13 @@ DupLocalVarName( Tcl_Obj *namePtr; LocalGetIntRep(srcPtr, index, namePtr); - if (!namePtr) { - namePtr = srcPtr; + if (namePtr) { + LocalSetIntRep(dupPtr, index, namePtr); + } else { + dupPtr->typePtr = NULL; + dupPtr->internalRep.twoPtrValue.ptr1 = NULL; + dupPtr->internalRep.twoPtrValue.ptr2 = NULL; } - LocalSetIntRep(dupPtr, index, namePtr); } /*