summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclOOCall.c58
-rw-r--r--tests/oo.test107
-rw-r--r--win/Makefile.in14
3 files changed, 170 insertions, 9 deletions
diff --git a/generic/tclOOCall.c b/generic/tclOOCall.c
index 85ca995..23db75c 100644
--- a/generic/tclOOCall.c
+++ b/generic/tclOOCall.c
@@ -1068,15 +1068,28 @@ InitCallChain(
Object *oPtr,
int flags)
{
+ /*
+ * Note that it's possible to end up with a NULL oPtr->selfCls here if
+ * there is a call into stereotypical object after it has finished running
+ * its destructor phase. Such things can't be cached for a long time so the
+ * epoch can be bogus. [Bug 7842f33a5c]
+ */
+
callPtr->flags = flags &
(PUBLIC_METHOD | PRIVATE_METHOD | SPECIAL | FILTER_HANDLING);
if (oPtr->flags & USE_CLASS_CACHE) {
- oPtr = oPtr->selfCls->thisPtr;
+ oPtr = (oPtr->selfCls ? oPtr->selfCls->thisPtr : NULL);
callPtr->flags |= USE_CLASS_CACHE;
}
- callPtr->epoch = oPtr->fPtr->epoch;
- callPtr->objectCreationEpoch = oPtr->creationEpoch;
- callPtr->objectEpoch = oPtr->epoch;
+ if (oPtr) {
+ callPtr->epoch = oPtr->fPtr->epoch;
+ callPtr->objectCreationEpoch = oPtr->creationEpoch;
+ callPtr->objectEpoch = oPtr->epoch;
+ } else {
+ callPtr->epoch = 0;
+ callPtr->objectCreationEpoch = 0;
+ callPtr->objectEpoch = 0;
+ }
callPtr->refCount = 1;
callPtr->numChain = 0;
callPtr->chain = callPtr->staticChain;
@@ -1107,6 +1120,13 @@ IsStillValid(
int mask)
{
if ((oPtr->flags & USE_CLASS_CACHE)) {
+ /*
+ * If the object is in a weird state (due to stereotype tricks) then
+ * just declare the cache invalid. [Bug 7842f33a5c]
+ */
+ if (!oPtr->selfCls) {
+ return 0;
+ }
oPtr = oPtr->selfCls->thisPtr;
flags |= USE_CLASS_CACHE;
}
@@ -1204,8 +1224,16 @@ TclOOGetCallContext(
Tcl_StoreInternalRep(cacheInThisObj, &methodNameType, NULL);
}
- if (oPtr->flags & USE_CLASS_CACHE) {
- if (oPtr->selfCls->classChainCache != NULL) {
+ /*
+ * Note that it's possible to end up with a NULL oPtr->selfCls here if
+ * there is a call into stereotypical object after it has finished
+ * running its destructor phase. It's quite a tangle, but at that
+ * point, we simply can't get stereotypes from the cache.
+ * [Bug 7842f33a5c]
+ */
+
+ if (oPtr->flags & USE_CLASS_CACHE && oPtr->selfCls) {
+ if (oPtr->selfCls->classChainCache) {
hPtr = Tcl_FindHashEntry(oPtr->selfCls->classChainCache,
(char *) methodNameObj);
} else {
@@ -1418,6 +1446,17 @@ TclOOGetStereotypeCallChain(
Object obj;
/*
+ * Note that it's possible to end up with a NULL clsPtr here if there is
+ * a call into stereotypical object after it has finished running its
+ * destructor phase. It's quite a tangle, but at that point, we simply
+ * can't get stereotypes. [Bug 7842f33a5c]
+ */
+
+ if (clsPtr == NULL) {
+ return NULL;
+ }
+
+ /*
* Synthesize a temporary stereotypical object so that we can use existing
* machinery to produce the stereotypical call chain.
*/
@@ -1644,9 +1683,16 @@ AddPrivatesFromClassChainToCallContext(
*
* Note that mixins must be processed before the main class hierarchy.
* [Bug 1998221]
+ *
+ * Note also that it's possible to end up with a null classPtr here if
+ * there is a call into stereotypical object after it has finished running
+ * its destructor phase. [Bug 7842f33a5c]
*/
tailRecurse:
+ if (classPtr == NULL) {
+ return;
+ }
FOREACH(superPtr, classPtr->mixins) {
if (AddPrivatesFromClassChainToCallContext(superPtr, contextCls,
methodName, cbPtr, doneFilters, flags|TRAVERSED_MIXIN,
diff --git a/tests/oo.test b/tests/oo.test
index d2569be..910b2b0 100644
--- a/tests/oo.test
+++ b/tests/oo.test
@@ -4517,7 +4517,112 @@ test oo-35.6 {
} -cleanup {
rename obj {}
} -result done
-
+test oo-35.7.1 {Bug 7842f33a5c: destructor cascading in stereotypes} -setup {
+ oo::class create base
+ oo::class create RpcClient {
+ superclass base
+ method write name {
+ lappend ::result "RpcClient -> $name"
+ }
+ method create_bug {} {
+ MkObjectRpc create cfg [self] 111
+ }
+ }
+ oo::class create MkObjectRpc {
+ superclass base
+ variable hdl
+ constructor {rpcHdl mqHdl} {
+ set hdl $mqHdl
+ oo::objdefine [self] forward rpc $rpcHdl
+ }
+ destructor {
+ my rpc write otto-$hdl
+ }
+ }
+ set ::result {}
+} -body {
+ set FH [RpcClient new]
+ $FH create_bug
+ $FH destroy
+ join $result \n
+} -cleanup {
+ base destroy
+} -result {}
+test oo-35.7.2 {Bug 7842f33a5c: destructor cascading in stereotypes} -setup {
+ oo::class create base
+ oo::class create RpcClient {
+ superclass base
+ method write name {
+ lappend ::result "RpcClient -> $name"
+ }
+ method create_bug {} {
+ MkObjectRpc create cfg [self] 111
+ }
+ destructor {
+ lappend ::result "Destroyed"
+ }
+ }
+ oo::class create MkObjectRpc {
+ superclass base
+ variable hdl
+ constructor {rpcHdl mqHdl} {
+ set hdl $mqHdl
+ oo::objdefine [self] forward rpc $rpcHdl
+ }
+ destructor {
+ my rpc write otto-$hdl
+ }
+ }
+ set ::result {}
+} -body {
+ set FH [RpcClient new]
+ $FH create_bug
+ $FH destroy
+ join $result \n
+} -cleanup {
+ base destroy
+} -result {Destroyed}
+test oo-35.7.3 {Bug 7842f33a5c: destructor cascading in stereotypes} -setup {
+ oo::class create base
+ oo::class create RpcClient {
+ superclass base
+ variable interiorObjects
+ method write name {
+ lappend ::result "RpcClient -> $name"
+ }
+ method create_bug {} {
+ set obj [MkObjectRpc create cfg [self] 111]
+ lappend interiorObjects $obj
+ return $obj
+ }
+ destructor {
+ lappend ::result "Destroyed"
+ # Explicit destroy of interior objects
+ foreach obj $interiorObjects {
+ $obj destroy
+ }
+ }
+ }
+ oo::class create MkObjectRpc {
+ superclass base
+ variable hdl
+ constructor {rpcHdl mqHdl} {
+ set hdl $mqHdl
+ oo::objdefine [self] forward rpc $rpcHdl
+ }
+ destructor {
+ my rpc write otto-$hdl
+ }
+ }
+ set ::result {}
+} -body {
+ set FH [RpcClient new]
+ $FH create_bug
+ $FH destroy
+ join $result \n
+} -cleanup {
+ base destroy
+} -result "Destroyed\nRpcClient -> otto-111"
test oo-36.1 {TIP #470: introspection within oo::define} {
oo::define oo::object self
} ::oo::object
diff --git a/win/Makefile.in b/win/Makefile.in
index f9d4e61..1ae55f1 100644
--- a/win/Makefile.in
+++ b/win/Makefile.in
@@ -158,7 +158,7 @@ TEST_LOAD_PRMS = lappend ::auto_path {$(ROOT_DIR_WIN_NATIVE)/tests};\
TEST_LOAD_FACILITIES = package ifneeded tcl::test ${VERSION}@TCL_PATCH_LEVEL@ [list load ${TEST_DLL_FILE} Tcltest];\
$(TEST_LOAD_PRMS)
ZLIB_DLL_FILE = zlib1.dll
-TOMMATH_DLL_FILE = libtommath.dll
+TOMMATH_DLL_FILE = libtommath.dll
SHARED_LIBRARIES = $(TCL_DLL_FILE) @ZLIB_DLL_FILE@ @TOMMATH_DLL_FILE@
STATIC_LIBRARIES = $(TCL_LIB_FILE)
@@ -208,6 +208,7 @@ SHELL = @SHELL@
RM = rm -f
COPY = cp
LN = ln
+GDB = gdb
###
# Tip 430 - ZipFS Modifications
@@ -989,7 +990,16 @@ shell: binaries
# This target can be used to run tclsh inside either gdb or insight
gdb: binaries
@echo "set env TCL_LIBRARY=$(LIBRARY_DIR)" > gdb.run
- gdb ./$(TCLSH) --command=gdb.run
+ $(GDB) ./$(TCLSH) --command=gdb.run
+ rm gdb.run
+
+shquotequote = $(subst ',\",$(subst ",\",$(1)))
+gdb-test: tcltest
+ @printf '%s ' 'set env TCL_LIBRARY=$(LIBRARY_DIR)' > gdb.run
+ @printf '\n' >>gdb.run
+ @printf '%s ' set args $(ROOT_DIR_NATIVE)/tests/all.tcl \
+ $(call shquotequote,$(TESTFLAGS)) -singleproc 1 >> gdb.run
+ $(GDB) ${TEST_EXE_FILE} --command=gdb.run
rm gdb.run
depend: