Index: generic/xotcl.c =================================================================== diff -u -r2fd2bf117e5554b6fb3c9184f0910e3e6fa7168d -rcb78a7cd38f8015209d1d0d44fe037bfaebb4197 --- generic/xotcl.c (.../xotcl.c) (revision 2fd2bf117e5554b6fb3c9184f0910e3e6fa7168d) +++ generic/xotcl.c (.../xotcl.c) (revision cb78a7cd38f8015209d1d0d44fe037bfaebb4197) @@ -785,19 +785,19 @@ */ #if defined(XOTCLOBJ_TRACE) # define XOTclObjectRefCountIncr(obj) \ - obj->refCount++; \ + (obj)->refCount++; \ fprintf(stderr, "RefCountIncr %p count=%d %s\n", obj, obj->refCount, obj->cmdName?ObjStr(obj->cmdName):"no name"); \ MEM_COUNT_ALLOC("XOTclObject RefCount", obj) # define XOTclObjectRefCountDecr(obj) \ - obj->refCount--; \ + (obj)->refCount--; \ fprintf(stderr, "RefCountDecr %p count=%d\n", obj, obj->refCount); \ MEM_COUNT_FREE("XOTclObject RefCount", obj) #else # define XOTclObjectRefCountIncr(obj) \ - obj->refCount++; \ + (obj)->refCount++; \ MEM_COUNT_ALLOC("XOTclObject RefCount", obj) # define XOTclObjectRefCountDecr(obj) \ - obj->refCount--; \ + (obj)->refCount--; \ MEM_COUNT_FREE("XOTclObject RefCount", obj) #endif @@ -849,6 +849,8 @@ XOTclCleanupObject(XOTclObject *obj) { XOTclObjectRefCountDecr(obj); + /*fprintf(stderr, "XOTclCleanupObject %p refcount %d\n", obj, obj->refCount);*/ + if (obj->refCount <= 0) { assert(obj->refCount == 0); assert(obj->flags & XOTCL_DELETED); @@ -860,7 +862,7 @@ #if !defined(NDEBUG) memset(obj, 0, sizeof(XOTclObject)); #endif - /* fprintf(stderr,"CKFREE obj %p\n", obj);*/ + /*fprintf(stderr,"CKFREE obj %p\n", obj);*/ ckfree((char *) obj); } } @@ -1676,7 +1678,6 @@ Tcl_HashTable *cmdTable = Tcl_Namespace_cmdTable(ns); Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; - Tcl_Command cmd; #ifdef OBJDELETION_TRACE fprintf(stderr, "NSCleanupNamespace %p varTable %p\n", ns, varTable); @@ -1693,13 +1694,22 @@ */ for (hPtr = Tcl_FirstHashEntry(cmdTable, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { - cmd = (Tcl_Command) Tcl_GetHashValue(hPtr); - /* objects should not be deleted here to preseve children deletion order*/ - if (!XOTclGetObjectFromCmdPtr(cmd)) { + Tcl_Command cmd = (Tcl_Command) Tcl_GetHashValue(hPtr); + Tcl_ObjCmdProc *proc = Tcl_Command_objProc(cmd); + XOTclObject *invokeObj = proc == XOTclObjDispatch ? (XOTclObject *)Tcl_Command_objClientData(cmd) : NULL; + + /* objects should not be deleted here to preseve children deletion order*/ + if (invokeObj && cmd != invokeObj->id) { + /* + * cmd is an aliased object, reduce the refcount + */ + XOTclCleanupObject(invokeObj); + } + /*fprintf(stderr,"NSCleanupNamespace deleting %s %p\n", Tcl_Command_nsPtr(cmd)->fullName, cmd);*/ + XOTcl_DeleteCommandFromToken(interp, cmd); - } } } @@ -5158,7 +5168,7 @@ char *methodName, int frameType) { ClientData cp = Tcl_Command_objClientData(cmd); XOTclCallStackContent csc, *cscPtr = &csc; - register Tcl_ObjCmdProc *proc = Tcl_Command_objProc(cmd); + register Tcl_ObjCmdProc *proc = Tcl_Command_objProc(cmd); int rc; assert (!obj->teardown); @@ -5170,6 +5180,9 @@ /*fprintf(stderr, "InvokeMethod method '%s' cmd %p cp=%p objc=%d\n",methodName,cmd, cp, objc);*/ if (proc == TclObjInterpProc) { + /* + * invoke a Tcl-defined method + */ #if defined(TCL85STACK) CallStackPush(cscPtr, obj, cl, cmd, frameType); #else @@ -5181,10 +5194,22 @@ return rc; } else if (cp) { - /* a cmd with client data */ + /* some cmd with client data */ if (proc == XOTclObjDispatch) { - assert((TclIsProc((Command *)cmd) == NULL)); - /*fprintf(stderr,"\t ObjDispatch\n");*/ + /* + * invoke an aliased object via method interface + */ + XOTclObject *invokeObj = (XOTclObject *)cp; + if (invokeObj->flags & XOTCL_DELETED) { + /* + * when we try to call a deleted object, the cmd (alias) is + * automatically removed + */ + Tcl_DeleteCommandFromToken(interp, cmd); + XOTclCleanupObject(invokeObj); + return XOTclVarErrMsg(interp, "Trying to dispatch deleted object via method '", + methodName, "'", (char *) NULL); + } } else if (proc == XOTclForwardMethod || proc == XOTclObjscopedMethod) { tclCmdClientData *tcd = (tclCmdClientData *)cp; @@ -6864,6 +6889,7 @@ */ static void CleanupDestroyObject(Tcl_Interp *interp, XOTclObject *obj, int softrecreate) { + /*fprintf(stderr, "CleanupDestroyObject obj %p\n", obj);*/ /* remove the instance, but not for ::Class/::Object */ if ((obj->flags & XOTCL_IS_ROOT_CLASS) == 0 && @@ -6903,7 +6929,6 @@ CmdListRemoveList(&opt->mixins, GuardDel); CmdListRemoveList(&opt->filters, GuardDel); - FREE(XOTclObjectOpt, opt); opt = obj->opt = 0; } @@ -6975,7 +7000,7 @@ XOTclObject *obj = (XOTclObject*)clientData; Tcl_Interp *interp; - /* fprintf(stderr, "****** PrimitiveODestroy %p\n", obj);*/ + /*fprintf(stderr, "****** PrimitiveODestroy %p flags %.6x\n", obj, obj->flags);*/ assert(obj && !(obj->flags & XOTCL_DELETED)); /* @@ -7012,7 +7037,6 @@ FilterStackPop(obj); obj->teardown = NULL; - if (obj->nsPtr) { /*fprintf(stderr,"primitive odestroy calls deletenamespace for obj %p\n", obj);*/ XOTcl_DeleteNamespace(interp, obj->nsPtr); @@ -9491,6 +9515,17 @@ tcd = Tcl_Command_objClientData(cmd); } + if (objProc == XOTclObjDispatch) { + /* + * if we register an alias for an object, we have to take care to + * handle cases, where the aliased object is destroyed and the + * alias points to nowhere. We realize this via using the object + * refcount. + */ + /*fprintf(stderr, "registering an object %p\n",tcd);*/ + XOTclObjectRefCountIncr((XOTclObject *)tcd); + } + if (withProtected) { flags = XOTCL_CMD_PROTECTED_METHOD; } Index: tests/destroytest.xotcl =================================================================== diff -u -r543e283a1681f4e7075eb65a0dd97f54cf48e2f7 -rcb78a7cd38f8015209d1d0d44fe037bfaebb4197 --- tests/destroytest.xotcl (.../destroytest.xotcl) (revision 543e283a1681f4e7075eb65a0dd97f54cf48e2f7) +++ tests/destroytest.xotcl (.../destroytest.xotcl) (revision cb78a7cd38f8015209d1d0d44fe037bfaebb4197) @@ -405,6 +405,90 @@ ? "set ::firstDestroy" 1 "$::case, firstDestroy called" ? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" +# +set case "nesting destroy" +Object x +Object x::y +x destroy +? {Object isobject x} 0 "$case: parent object gone" +? {Object isobject x::y} 0 "$case: child object gone" + +set case "deleting aliased object" +Object o +Object o2 +::xotcl::alias o x o2 +? {o x} ::o2 "$case: call object via alias" +? {o x info vars} "" "$case: call info on aliased object" +? {o2 set x 10} 10 "$case: set variable on object" +? {o x info vars} x "$case: query vars via alias" +? {o x set x} 10 "$case: set var via alias" +o2 destroy +catch {o x info vars} errMsg +? {set errMsg} "Trying to dispatch deleted object via method 'x'" "$case: 1st call on deleted object" +catch {o x info vars} errMsg +? {set errMsg} "::o: unable to dispatch method 'x'" "$case: 2nd call on deleted object" +o destroy + +set case "deleting object with alias to object" +Object o +Object o3 +::xotcl::alias o x o3 +o destroy +? {Object isobject o} 0 "$case: parent object gone" +? {Object isobject o3} 1 "$case: aliased object still here" +o3 destroy +? {Object isobject o3} 0 "$case: aliased object destroyed" + +set case "create an alias, and delete cmd via aggregation" +Object o +Object o3 +::xotcl::alias o x o3 +o::x destroy +? {Object isobject o3} 0 "$case: aliased object destroyed" +o destroy + +set case "create an alias, and recreate obj" +Object o +Object o3 +::xotcl::alias o x o3 +Object o3 +o3 set a 13 +? {o x set a} 13 "$case: aliased object works after recreate" +o destroy + +set case "create an alias on the class level, double aliasing, delete aliased object" +Class C +Object o +Object o3 +::xotcl::alias o a o3 +::xotcl::alias C b o +C c1 +? {c1 b set B 2} 2 "$case: call 1st level" +? {c1 b a set A 3} 3 "$case: call 2nd level" +? {o set B} 2 "$case: call 1st level ok" +? {o3 set A} 3 "$case: call 2nd level ok" +o destroy +catch {c1 b} errMsg +? {set errMsg} "Trying to dispatch deleted object via method 'b'" "$case: call via alias to deleted object" +C destroy +c1 destroy +o3 destroy + +set case "create an alias on the class level, double aliasing, destroy class" +Class C +Object o +Object o3 +::xotcl::alias o a o3 +::xotcl::alias C b o +C c1 +C destroy +? {Object isobject o} 1 "$case: object o still here" +? {Object isobject o3} 1 "$case: object o3 still here" +o destroy +o3 destroy +c1 destroy + + puts stderr "==== EXIT ====" exit