From 0c09b35d995096c2ff289565f73f5eb46481d4b4 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 15 May 2025 15:14:11 +0000 Subject: New function Tcl_AttemptGetString(). WIP --- doc/ObjectType.3 | 3 ++ generic/tcl.decls | 6 ++++ generic/tclArithSeries.c | 16 ++++++--- generic/tclCmdMZ.c | 8 +++++ generic/tclDecls.h | 26 ++++++++++---- generic/tclDictObj.c | 29 +++++++++++++--- generic/tclInt.h | 8 +++++ generic/tclListObj.c | 30 ++++++++++++---- generic/tclMain.c | 36 +++++++++++++------ generic/tclObj.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++-- generic/tclStringObj.c | 25 ++++++++++---- generic/tclStubInit.c | 4 +-- 12 files changed, 238 insertions(+), 43 deletions(-) diff --git a/doc/ObjectType.3 b/doc/ObjectType.3 index 688a04a..673a766 100644 --- a/doc/ObjectType.3 +++ b/doc/ObjectType.3 @@ -269,6 +269,9 @@ Tcl routines accepting string values as arguments. Storage for the byte array must be allocated in the heap by \fBTcl_Alloc\fR. Note that \fIupdateStringProc\fRs must allocate enough storage for the string's bytes and the terminating null byte. +If the \fIupdateStringProc\fR cannot do that, it must set +interp->bytes to be NULL (freeing the previous content), and set +interp->length to the number of bytes it tried to allocate. .PP The \fIupdateStringProc\fR for Tcl's built-in double type, for example, calls Tcl_PrintDouble to write to a buffer of size TCL_DOUBLE_SPACE, diff --git a/generic/tcl.decls b/generic/tcl.decls index 05849fc..5bb3951 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -104,6 +104,9 @@ declare 20 { declare 21 { int Tcl_DbIsShared(Tcl_Obj *objPtr, const char *file, int line) } +declare 22 { + char *Tcl_DbGetStringFromObj(Tcl_Obj *objPtr, Tcl_Size *lengthPtr, const char *file, int line) +} declare 23 { Tcl_Obj *Tcl_DbNewByteArrayObj(const unsigned char *bytes, Tcl_Size numBytes, const char *file, int line) @@ -1038,6 +1041,9 @@ declare 338 { declare 339 { Tcl_Size Tcl_WriteObj(Tcl_Channel chan, Tcl_Obj *objPtr) } +declare 341 { + char *Tcl_AttemptGetStringFromObj(Tcl_Obj *objPtr, Tcl_Size *lengthPtr) +} declare 343 { void Tcl_AlertNotifier(void *clientData) } diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index dbd3d47..47e8726 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -1175,9 +1175,7 @@ UpdateStringOfArithSeries( Tcl_PrintDouble(NULL,d,tmp); elen = strlen(tmp); if (bytlen > TCL_SIZE_MAX - elen) { - /* overflow, todo: check we could use some representation instead of the panic - * to signal it is too large for string representation, because too heavy */ - Tcl_Panic("UpdateStringOfArithSeries: too large to represent"); + goto repTooLarge; } bytlen += elen; } @@ -1188,8 +1186,16 @@ UpdateStringOfArithSeries( * Pass 2: generate the string repr. */ - p = srep = Tcl_InitStringRep(arithSeriesObjPtr, NULL, bytlen); - TclOOM(p, bytlen+1); + p = srep = TclAttemptInitStringRep(arithSeriesObjPtr, NULL, bytlen); + if (!p) { + repTooLarge: + if (arithSeriesObjPtr->bytes) { + Tcl_Free(arithSeriesObjPtr->bytes); + arithSeriesObjPtr->bytes = 0; + } + arithSeriesObjPtr->length = bytlen; + return; + } if (!arithSeriesRepPtr->isDouble) { for (i = 0; i < arithSeriesRepPtr->len; i++) { diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index 6277f5f..349dd91 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -2681,6 +2681,10 @@ StringEqualCmd( objv += objc-2; match = TclStringCmp(objv[0], objv[1], 1, nocase, reqlength); + if (match == INT_MIN) { + Tcl_AppendResult(interp, "memory allocation error", (char *)NULL); + return TCL_ERROR; + } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(match ? 0 : 1)); return TCL_OK; } @@ -2726,6 +2730,10 @@ StringCmpCmd( objv += objc-2; match = TclStringCmp(objv[0], objv[1], 0, nocase, reqlength); + if (match == INT_MIN) { + Tcl_AppendResult(interp, "memory allocation error", (char *)NULL); + return TCL_ERROR; + } Tcl_SetObjResult(interp, Tcl_NewWideIntObj(match)); return TCL_OK; } diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 47f6b9a..1c9704f 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -107,7 +107,10 @@ EXTERN void Tcl_DbIncrRefCount(Tcl_Obj *objPtr, const char *file, /* 21 */ EXTERN int Tcl_DbIsShared(Tcl_Obj *objPtr, const char *file, int line); -/* Slot 22 is reserved */ +/* 22 */ +EXTERN char * Tcl_DbGetStringFromObj(Tcl_Obj *objPtr, + Tcl_Size *lengthPtr, const char *file, + int line); /* 23 */ EXTERN Tcl_Obj * Tcl_DbNewByteArrayObj(const unsigned char *bytes, Tcl_Size numBytes, const char *file, @@ -895,7 +898,9 @@ EXTERN Tcl_Size Tcl_WriteChars(Tcl_Channel chan, const char *src, /* 339 */ EXTERN Tcl_Size Tcl_WriteObj(Tcl_Channel chan, Tcl_Obj *objPtr); /* Slot 340 is reserved */ -/* Slot 341 is reserved */ +/* 341 */ +EXTERN char * Tcl_AttemptGetStringFromObj(Tcl_Obj *objPtr, + Tcl_Size *lengthPtr); /* Slot 342 is reserved */ /* 343 */ EXTERN void Tcl_AlertNotifier(void *clientData); @@ -1907,7 +1912,7 @@ typedef struct TclStubs { void (*tcl_DbDecrRefCount) (Tcl_Obj *objPtr, const char *file, int line); /* 19 */ void (*tcl_DbIncrRefCount) (Tcl_Obj *objPtr, const char *file, int line); /* 20 */ int (*tcl_DbIsShared) (Tcl_Obj *objPtr, const char *file, int line); /* 21 */ - void (*reserved22)(void); + char * (*tcl_DbGetStringFromObj) (Tcl_Obj *objPtr, Tcl_Size *lengthPtr, const char *file, int line); /* 22 */ Tcl_Obj * (*tcl_DbNewByteArrayObj) (const unsigned char *bytes, Tcl_Size numBytes, const char *file, int line); /* 23 */ Tcl_Obj * (*tcl_DbNewDoubleObj) (double doubleValue, const char *file, int line); /* 24 */ Tcl_Obj * (*tcl_DbNewListObj) (Tcl_Size objc, Tcl_Obj *const *objv, const char *file, int line); /* 25 */ @@ -2226,7 +2231,7 @@ typedef struct TclStubs { Tcl_Size (*tcl_WriteChars) (Tcl_Channel chan, const char *src, Tcl_Size srcLen); /* 338 */ Tcl_Size (*tcl_WriteObj) (Tcl_Channel chan, Tcl_Obj *objPtr); /* 339 */ void (*reserved340)(void); - void (*reserved341)(void); + char * (*tcl_AttemptGetStringFromObj) (Tcl_Obj *objPtr, Tcl_Size *lengthPtr); /* 341 */ void (*reserved342)(void); void (*tcl_AlertNotifier) (void *clientData); /* 343 */ void (*tcl_ServiceModeHook) (int mode); /* 344 */ @@ -2635,7 +2640,8 @@ extern const TclStubs *tclStubsPtr; (tclStubsPtr->tcl_DbIncrRefCount) /* 20 */ #define Tcl_DbIsShared \ (tclStubsPtr->tcl_DbIsShared) /* 21 */ -/* Slot 22 is reserved */ +#define Tcl_DbGetStringFromObj \ + (tclStubsPtr->tcl_DbGetStringFromObj) /* 22 */ #define Tcl_DbNewByteArrayObj \ (tclStubsPtr->tcl_DbNewByteArrayObj) /* 23 */ #define Tcl_DbNewDoubleObj \ @@ -3220,7 +3226,8 @@ extern const TclStubs *tclStubsPtr; #define Tcl_WriteObj \ (tclStubsPtr->tcl_WriteObj) /* 339 */ /* Slot 340 is reserved */ -/* Slot 341 is reserved */ +#define Tcl_AttemptGetStringFromObj \ + (tclStubsPtr->tcl_AttemptGetStringFromObj) /* 341 */ /* Slot 342 is reserved */ #define Tcl_AlertNotifier \ (tclStubsPtr->tcl_AlertNotifier) /* 343 */ @@ -4010,6 +4017,8 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GetString(objPtr) \ Tcl_GetStringFromObj(objPtr, (Tcl_Size *)NULL) +#define Tcl_AttemptGetString(objPtr) \ + Tcl_AttemptGetStringFromObj(objPtr, (Tcl_Size *)NULL) #define Tcl_GetUnicode(objPtr) \ Tcl_GetUnicodeFromObj(objPtr, (Tcl_Size *)NULL) #undef Tcl_GetIndexFromObjStruct @@ -4063,6 +4072,11 @@ extern const TclStubs *tclStubsPtr; # undef Tcl_AttemptRealloc # define Tcl_AttemptRealloc(x,y) \ (Tcl_AttemptDbCkrealloc((x), (y), __FILE__, __LINE__)) +# define Tcl_Alloc(x) \ + (Tcl_DbCkalloc((x), __FILE__, __LINE__)) +# undef Tcl_GetStringFromObj +# define Tcl_GetStringFromObj(x,y) \ + (Tcl_DbGetStringFromObj((x), (y), __FILE__, __LINE__)) #endif /* !TCL_MEM_DEBUG */ #define Tcl_NewLongObj(value) Tcl_NewWideIntObj((long)(value)) diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index 892ba87..2961011 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -520,7 +520,17 @@ UpdateStringOfDict( if (numElems <= LOCAL_SIZE) { flagPtr = localFlags; } else { - flagPtr = (char *)Tcl_Alloc(numElems); + flagPtr = (char *)Tcl_AttemptAlloc(numElems); + if (!flagPtr) { + dictPtr->length = numElems; + allocError: + /* Allocation error. Just give up. */ + if (dictPtr->bytes) { + Tcl_Free(dictPtr->bytes); + dictPtr->bytes = NULL; + } + return; + } } for (i=0,cPtr=dict->entryChainHead; inextPtr) { /* @@ -530,11 +540,19 @@ UpdateStringOfDict( flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 ); keyPtr = (Tcl_Obj *)Tcl_GetHashKey(&dict->table, &cPtr->entry); - elem = TclGetStringFromObj(keyPtr, &length); + elem = TclAttemptGetStringFromObj(keyPtr, &length); + if (!elem) { + dictPtr->length = keyPtr->length; + goto allocError; + } bytesNeeded += TclScanElement(elem, length, flagPtr+i); flagPtr[i+1] = TCL_DONT_QUOTE_HASH; valuePtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); - elem = TclGetStringFromObj(valuePtr, &length); + elem = TclAttemptGetStringFromObj(valuePtr, &length); + if (!elem) { + dictPtr->length = valuePtr->length; + goto allocError; + } bytesNeeded += TclScanElement(elem, length, flagPtr+i+1); } bytesNeeded += numElems; @@ -544,7 +562,10 @@ UpdateStringOfDict( */ dst = Tcl_InitStringRep(dictPtr, NULL, bytesNeeded - 1); - TclOOM(dst, bytesNeeded); + if (!dst) { + dictPtr->length = bytesNeeded; + goto allocError; + } for (i=0,cPtr=dict->entryChainHead; inextPtr) { if (i) { flagPtr[i] |= TCL_DONT_QUOTE_HASH; diff --git a/generic/tclInt.h b/generic/tclInt.h index 6b82e84..cb58bf5 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -4418,6 +4418,14 @@ MODULE_SCOPE void TclDbInitNewObj(Tcl_Obj *objPtr, const char *file, ? (*(lenPtr) = (objPtr)->length, (objPtr)->bytes) \ : (Tcl_GetStringFromObj)((objPtr), (lenPtr))) +#define TclAttemptGetString(objPtr) \ + ((objPtr)->bytes? (objPtr)->bytes : Tcl_AttemptGetString(objPtr)) + +#define TclAttemptGetStringFromObj(objPtr, lenPtr) \ + ((objPtr)->bytes \ + ? (*(lenPtr) = (objPtr)->length, (objPtr)->bytes) \ + : (Tcl_AttemptGetStringFromObj)((objPtr), (lenPtr))) + /* *---------------------------------------------------------------- * Macro used by the Tcl core to clean out an object's internal diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 2cc4fb3..9f91552 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -3539,14 +3539,25 @@ UpdateStringOfList( flagPtr = localFlags; } else { /* We know numElems <= LIST_MAX, so this is safe. */ - flagPtr = (char *)Tcl_Alloc(numElems); + flagPtr = (char *)Tcl_AttemptAlloc(numElems); + if (!flagPtr) { + listObj->length = numElems; + allocError: + /* Allocation error. Just give up. */ + if (listObj->bytes) { + Tcl_Free(listObj->bytes); + listObj->bytes = NULL; + } + return; + } } for (i = 0; i < numElems; i++) { flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0); - elem = TclGetStringFromObj(elemPtrs[i], &length); + elem = TclAttemptGetStringFromObj(elemPtrs[i], &length); bytesNeeded += TclScanElement(elem, length, flagPtr+i); if (bytesNeeded > SIZE_MAX - numElems) { - Tcl_Panic("max size for a Tcl value (%" TCL_Z_MODIFIER "u bytes) exceeded", SIZE_MAX); + listObj->length = bytesNeeded; + goto allocError; } } bytesNeeded += numElems - 1; @@ -3555,13 +3566,20 @@ UpdateStringOfList( * Pass 2: copy into string rep buffer. */ - start = dst = Tcl_InitStringRep(listObj, NULL, bytesNeeded); - TclOOM(dst, bytesNeeded); + start = dst = TclAttemptInitStringRep(listObj, NULL, bytesNeeded); + if (!start) { + listObj->length = bytesNeeded; + goto allocError; + } for (i = 0; i < numElems; i++) { if (i) { flagPtr[i] |= TCL_DONT_QUOTE_HASH; } - elem = TclGetStringFromObj(elemPtrs[i], &length); + elem = TclAttemptGetStringFromObj(elemPtrs[i], &length); + if (!elem) { + listObj->length = elemPtrs[i]->length; + goto allocError; + } dst += TclConvertElement(elem, length, dst, flagPtr[i]); *dst++ = ' '; } diff --git a/generic/tclMain.c b/generic/tclMain.c index e604a60..53c39e7 100644 --- a/generic/tclMain.c +++ b/generic/tclMain.c @@ -533,13 +533,20 @@ Tcl_MainEx( } else if (is.tty) { resultPtr = Tcl_GetObjResult(interp); Tcl_IncrRefCount(resultPtr); - (void)Tcl_GetStringFromObj(resultPtr, &length); - chan = Tcl_GetStdChannel(TCL_STDOUT); - if ((length > 0) && chan) { - if (Tcl_WriteObj(chan, resultPtr) < 0) { - Tcl_WriteChars(chan, ENCODING_ERROR, -1); + (void)Tcl_AttemptGetStringFromObj(resultPtr, &length); + if (!resultPtr->bytes) { + chan = Tcl_GetStdChannel(TCL_STDERR); + if (chan) { + Tcl_WriteChars(chan, "Memory allocation error\n", -1); + } + } else { + chan = Tcl_GetStdChannel(TCL_STDOUT); + if ((length > 0) && chan) { + if (Tcl_WriteObj(chan, resultPtr) < 0) { + Tcl_WriteChars(chan, ENCODING_ERROR, -1); + } + Tcl_WriteChars(chan, "\n", 1); } - Tcl_WriteChars(chan, "\n", 1); } Tcl_DecrRefCount(resultPtr); } @@ -813,12 +820,19 @@ StdinProc( chan = Tcl_GetStdChannel(TCL_STDOUT); Tcl_IncrRefCount(resultPtr); - (void)Tcl_GetStringFromObj(resultPtr, &length); - if ((length > 0) && (chan != NULL)) { - if (Tcl_WriteObj(chan, resultPtr) < 0) { - Tcl_WriteChars(chan, ENCODING_ERROR, -1); + (void)Tcl_AttemptGetStringFromObj(resultPtr, &length); + if (!resultPtr->bytes) { + chan = Tcl_GetStdChannel(TCL_STDERR); + if (chan) { + Tcl_WriteChars(chan, "Memory allocation error\n", -1); + } + } else { + if ((length > 0) && (chan != NULL)) { + if (Tcl_WriteObj(chan, resultPtr) < 0) { + Tcl_WriteChars(chan, ENCODING_ERROR, -1); + } + Tcl_WriteChars(chan, "\n", 1); } - Tcl_WriteChars(chan, "\n", 1); } Tcl_DecrRefCount(resultPtr); } diff --git a/generic/tclObj.c b/generic/tclObj.c index 51f19ee..5e2091f 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -1674,8 +1674,11 @@ Tcl_GetStringFromObj( objPtr->typePtr->name); } objPtr->typePtr->updateStringProc(objPtr); - if (objPtr->bytes == NULL - || objPtr->bytes[objPtr->length] != '\0') { + if (objPtr->bytes == NULL) { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to allocate %" TCL_SIZE_MODIFIER "d bytes", + objPtr->typePtr->name, objPtr->length); + } else if (objPtr->bytes[objPtr->length] != '\0') { Tcl_Panic("UpdateStringProc for type '%s' " "failed to create a valid string rep", objPtr->typePtr->name); @@ -1686,6 +1689,89 @@ Tcl_GetStringFromObj( } return objPtr->bytes; } + +char * +Tcl_DbGetStringFromObj( + Tcl_Obj *objPtr, /* Object whose string rep byte pointer should + * be returned. */ + Tcl_Size *lengthPtr, /* If non-NULL, the location where the string + * rep's byte array length should * be stored. + * If NULL, no length is stored. */ + const char *file, + int line) +{ + if (objPtr->bytes == NULL) { + /* + * Note we do not check for objPtr->typePtr == NULL. An invariant + * of a properly maintained Tcl_Obj is that at least one of + * objPtr->bytes and objPtr->typePtr must not be NULL. If broken + * extensions fail to maintain that invariant, we can crash here. + */ + + if (objPtr->typePtr->updateStringProc == NULL) { + /* + * Those Tcl_ObjTypes which choose not to define an + * updateStringProc must be written in such a way that + * (objPtr->bytes) never becomes NULL. + */ + Tcl_Panic("UpdateStringProc should not be invoked for type %s. %s:%d", + objPtr->typePtr->name, file, line); + } + objPtr->typePtr->updateStringProc(objPtr); + if (objPtr->bytes == NULL) { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to allocate %" TCL_SIZE_MODIFIER "d bytes. %s:%d", + objPtr->typePtr->name, objPtr->length, file, line); + } else if (objPtr->bytes[objPtr->length] != '\0') { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to create a valid string rep. %s:%d", + objPtr->typePtr->name, file, line); + } + } + if (lengthPtr != NULL) { + *lengthPtr = objPtr->length; + } + return objPtr->bytes; +} + +char * +Tcl_AttemptGetStringFromObj( + Tcl_Obj *objPtr, /* Object whose string rep byte pointer should + * be returned. */ + Tcl_Size *lengthPtr) /* If non-NULL, the location where the string + * rep's byte array length should * be stored. + * If NULL, no length is stored. */ +{ + if (objPtr->bytes == NULL) { + /* + * Note we do not check for objPtr->typePtr == NULL. An invariant + * of a properly maintained Tcl_Obj is that at least one of + * objPtr->bytes and objPtr->typePtr must not be NULL. If broken + * extensions fail to maintain that invariant, we can crash here. + */ + + if (objPtr->typePtr->updateStringProc == NULL) { + /* + * Those Tcl_ObjTypes which choose not to define an + * updateStringProc must be written in such a way that + * (objPtr->bytes) never becomes NULL. + */ + Tcl_Panic("UpdateStringProc should not be invoked for type %s", + objPtr->typePtr->name); + } + objPtr->typePtr->updateStringProc(objPtr); + if (objPtr->bytes != NULL + && objPtr->bytes[objPtr->length] != '\0') { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to create a valid string rep", + objPtr->typePtr->name); + } + } + if (lengthPtr != NULL) { + *lengthPtr = objPtr->bytes ? objPtr->length : -1; + } + return objPtr->bytes; +} /* *---------------------------------------------------------------------- diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c index 5f33950..d7d0aa2 100644 --- a/generic/tclStringObj.c +++ b/generic/tclStringObj.c @@ -3720,7 +3720,7 @@ TclStringCmp( case -1: s1 = ""; s1len = 0; - s2 = TclGetStringFromObj(value2Ptr, &s2len); + s2 = TclAttemptGetStringFromObj(value2Ptr, &s2len); break; case 0: match = -1; @@ -3735,7 +3735,7 @@ TclStringCmp( case -1: s2 = ""; s2len = 0; - s1 = TclGetStringFromObj(value1Ptr, &s1len); + s1 = TclAttemptGetStringFromObj(value1Ptr, &s1len); break; case 0: match = 1; @@ -3746,8 +3746,11 @@ TclStringCmp( goto matchdone; } } else { - s1 = TclGetStringFromObj(value1Ptr, &s1len); - s2 = TclGetStringFromObj(value2Ptr, &s2len); + s1 = TclAttemptGetStringFromObj(value1Ptr, &s1len); + s2 = TclAttemptGetStringFromObj(value2Ptr, &s2len); + } + if (!s1 || !s2) { + return INT_MIN; } if (!nocase && checkEq && reqlength < 0) { /* @@ -4501,7 +4504,7 @@ DupStringInternalRep( static int SetStringFromAny( - TCL_UNUSED(Tcl_Interp *), + Tcl_Interp *interp, Tcl_Obj *objPtr) /* The object to convert. */ { if (!TclHasInternalRep(objPtr, &tclStringType)) { @@ -4511,7 +4514,11 @@ SetStringFromAny( * Convert whatever we have into an untyped value. Just A String. */ - (void) TclGetString(objPtr); + (void)TclAttemptGetString(objPtr); + if (!objPtr->bytes) { + Tcl_AppendResult(interp, "allocation error", (char *)NULL); + return TCL_ERROR; + } TclFreeInternalRep(objPtr); /* @@ -4612,7 +4619,11 @@ ExtendStringRepWithUnicode( size += TclUtfCount(unicode[i]); } if (size < 0) { - Tcl_Panic("max size for a Tcl value (%" TCL_SIZE_MODIFIER "d bytes) exceeded", TCL_SIZE_MAX); + if (objPtr->bytes) { + Tcl_Free(objPtr->bytes); + objPtr->bytes = NULL; + } + return TCL_ERROR; } /* diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 8839e0b..9596343 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -829,7 +829,7 @@ const TclStubs tclStubs = { Tcl_DbDecrRefCount, /* 19 */ Tcl_DbIncrRefCount, /* 20 */ Tcl_DbIsShared, /* 21 */ - 0, /* 22 */ + Tcl_DbGetStringFromObj, /* 22 */ Tcl_DbNewByteArrayObj, /* 23 */ Tcl_DbNewDoubleObj, /* 24 */ Tcl_DbNewListObj, /* 25 */ @@ -1148,7 +1148,7 @@ const TclStubs tclStubs = { Tcl_WriteChars, /* 338 */ Tcl_WriteObj, /* 339 */ 0, /* 340 */ - 0, /* 341 */ + Tcl_AttemptGetStringFromObj, /* 341 */ 0, /* 342 */ Tcl_AlertNotifier, /* 343 */ Tcl_ServiceModeHook, /* 344 */ -- cgit v0.12