Posted to tcl by cjo at Fri Sep 08 19:29:26 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/tclObj.c b/generic/tclObj.c index 6e4011e..9ea55e7 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,122 @@ Tcl_GetStringFromObj( return objPtr->bytes; } +static void +FreeHydra( + Tcl_Obj *objPtr) +{ + int i; + Hydra *hydraPtr; + Tcl_Obj fakeObj; + + memset(&fakeObj, 0, sizeof(fakeObj)); + HydraGetIntRep(objPtr, hydraPtr); + + for (i=0; i<MAX_HYDRA_CLIENTS; i++) { + if (hydraPtr->client[i].typePtr) { + fakeObj.internalRep = hydraPtr->client[i].internalRep; + fakeObj.typePtr = hydraPtr->client[i].typePtr; + Tcl_FreeIntRep(&fakeObj); + if (fakeObj.refCount > 0) { + 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) +{ + if (!Tcl_HasStringRep(srcPtr)) { + UpdateStringOfHydra(srcPtr); + } + /* Ensure that duplicates of hydras are pure strings, since the most likely + * situation is that we're being duplicated in order to modify the value, + * which would invalidate the cached intreps */ + return; +} + +static void +UpdateStringOfHydra( + Tcl_Obj *objPtr) +{ + int i; + Hydra *hydraPtr; + Tcl_Obj fakeObj; + + memset(&fakeObj, 0, sizeof(fakeObj)); + + 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 > 0) { + 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 +1992,104 @@ 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 objects existing intrep */ + TclFreeIntRep(objPtr); + objPtr->internalRep = *irPtr; + 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) { + Tcl_Obj fakeObj; + + /* Free the matching client intrep, using a fake obj */ + memset(&fakeObj, 0, sizeof(fakeObj)); + fakeObj.typePtr = typePtr; + fakeObj.internalRep = clientPtr->internalRep; + //Tcl_InvalidateStringRep(&fakeObj); + TclFreeIntRep(&fakeObj); + + if (fakeObj.refCount > 0) { + Tcl_Panic("Invalid reference taken to fakeObj while " + "freeing hydra client interp for %s", + typePtr->name); + } + + /* Update the intrep */ + clientPtr->internalRep = *irPtr; + 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) { + Tcl_Obj fakeObj; + + /* Free the matching client intrep, using a fake obj */ + memset(&fakeObj, 0, sizeof(fakeObj)); + fakeObj.typePtr = typePtr; + fakeObj.internalRep = clientPtr->internalRep; + //Tcl_InvalidateStringRep(&fakeObj); + TclFreeIntRep(&fakeObj); + + if (fakeObj.refCount > 0) { + 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 +2106,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 +2116,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); + } + + 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); + } + } } - /* Type match! objPtr IntRep is the one sought. */ - return &(objPtr->internalRep); + return NULL; } /* @@ -4694,6 +4935,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; }