Index: TODO =================================================================== diff -u -rc8f193d5682e216200703a2404e38531e2f2918a -r2152f4606b7c4e81fd18018c7c43bf29961a9d1b --- TODO (.../TODO) (revision c8f193d5682e216200703a2404e38531e2f2918a) +++ TODO (.../TODO) (revision 2152f4606b7c4e81fd18018c7c43bf29961a9d1b) @@ -3772,12 +3772,16 @@ nsf.c: - handle duplicates in the cmd-list during cleanup +- Avoid duplicate entries in instance lists for diamond inheritance by + maintaining the set of already processed entries in + NsfClassInfoInstancesMethod1() and GetAllInstances(). +- extended regression test +- removed "namespace import" in objectsystem test + ======================================================================== TODO: - - don't produce duplicates in instance lists for diamond inheritance - - make rough comparsion table with NX, XOTcl, tclOO, itcl, Ruby, Python Most general superclass Metaclass Index: generic/nsf.c =================================================================== diff -u -r20cf684ddc5e9c54159e1b2d591b4eddccaa661d -r2152f4606b7c4e81fd18018c7c43bf29961a9d1b --- generic/nsf.c (.../nsf.c) (revision 20cf684ddc5e9c54159e1b2d591b4eddccaa661d) +++ generic/nsf.c (.../nsf.c) (revision 2152f4606b7c4e81fd18018c7c43bf29961a9d1b) @@ -261,7 +261,8 @@ /*static NsfObject *GetHiddenObjectFromCmd(Tcl_Interp *interp, Tcl_Command cmdPtr); static int ReverseLookupCmdFromCmdTable(Tcl_Interp *interp, Tcl_Command searchCmdPtr, Tcl_HashTable *cmdTablePtr);*/ -static void GetAllInstances(Tcl_Interp *interp, NsfCmdList **instances, NsfClass *startClass); +static void GetAllInstances(Tcl_Interp *interp, Tcl_HashTable *processedClasses, + NsfCmdList **instances, NsfClass *startClass); NSF_INLINE static Tcl_Command FindMethod(Tcl_Namespace *nsPtr, CONST char *methodName); /* prototypes for namespace specific calls */ @@ -2750,6 +2751,7 @@ ObjectSystemsCleanup(Tcl_Interp *interp, int withKeepvars) { NsfCmdList *instances = NULL, *entryPtr; NsfObjectSystem *osPtr, *nPtr; + Tcl_HashTable processedClassesTable, *processedClasses = &processedClassesTable; /* Deletion is performed in two rounds: * (a) SOFT DESTROY: invoke all user-defined destroy methods @@ -2767,11 +2769,17 @@ /* * Collect all instances from all object systems */ + Tcl_InitHashTable(processedClasses, TCL_ONE_WORD_KEYS); + MEM_COUNT_ALLOC("Tcl_InitHashTable", processedClasses); + for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { /*fprintf(stderr, "destroyObjectSystem deletes %s\n", ClassName(osPtr->rootClass));*/ - GetAllInstances(interp, &instances, osPtr->rootClass); + GetAllInstances(interp, processedClasses, &instances, osPtr->rootClass); } + Tcl_DeleteHashTable(processedClasses); + MEM_COUNT_FREE("Tcl_InitHashTable", processedClasses); + /***** SOFT DESTROY *****/ RUNTIME_STATE(interp)->exitHandlerDestroyRound = NSF_EXITHANDLER_ON_SOFT_DESTROY; @@ -6101,7 +6109,8 @@ *---------------------------------------------------------------------- */ static void -GetAllInstances(Tcl_Interp *interp, NsfCmdList **instances, NsfClass *startCl) { +GetAllInstances(Tcl_Interp *interp, Tcl_HashTable *processedClasses, + NsfCmdList **instances, NsfClass *startCl) { NsfClasses *sc; Tcl_HashSearch search; Tcl_HashEntry *hPtr; @@ -6158,7 +6167,16 @@ } for (sc = startCl->sub; sc; sc = sc->nextPtr) { - GetAllInstances(interp, instances, sc->cl); + int isNew; + + if (processedClasses != NULL) { + Tcl_CreateHashEntry(processedClasses, (char *)sc->cl, &isNew); + } else { + isNew = 1; + } + if (isNew) { + GetAllInstances(interp, processedClasses, instances, sc->cl); + } } } @@ -17737,7 +17755,7 @@ * Collect all instances from all object systems */ for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { - GetAllInstances(interp, &instances, osPtr->rootClass); + GetAllInstances(interp, NULL, &instances, osPtr->rootClass); } for (entry = instances; entry; entry = entry->nextPtr) { @@ -22437,7 +22455,8 @@ * String key hashtable */ static int -NsfClassInfoInstancesMethod1(Tcl_Interp *interp, NsfClass *startCl, Tcl_Obj *resultObj, +NsfClassInfoInstancesMethod1(Tcl_Interp *interp, NsfClass *startCl, + Tcl_HashTable *processedClasses, Tcl_Obj *resultObj, int withClosure, CONST char *pattern, NsfObject *matchObject) { Tcl_HashTable *tablePtr = &startCl->instances; NsfClasses *sc; @@ -22451,8 +22470,7 @@ for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr; hPtr = Tcl_NextHashEntry(&search)) { NsfObject *inst = (NsfObject *) Tcl_GetHashKey(tablePtr, hPtr); - /*fprintf(stderr, "match '%s' %p %p '%s'\n", - ObjectName(matchObject), matchObject, inst, ObjectName(inst));*/ + if (matchObject && inst == matchObject) { Tcl_SetStringObj(resultObj, ObjStr(matchObject->cmdName), -1); return 1; @@ -22461,7 +22479,13 @@ } if (withClosure) { for (sc = startCl->sub; sc; sc = sc->nextPtr) { - rc = NsfClassInfoInstancesMethod1(interp, sc->cl, resultObj, withClosure, pattern, matchObject); + int isNew; + + Tcl_CreateHashEntry(processedClasses, (char *)sc->cl, &isNew); + if (isNew) { + rc = NsfClassInfoInstancesMethod1(interp, sc->cl, processedClasses, + resultObj, withClosure, pattern, matchObject); + } if (rc) break; } } @@ -22478,10 +22502,17 @@ NsfClassInfoInstancesMethod(Tcl_Interp *interp, NsfClass *startCl, int withClosure, CONST char *pattern, NsfObject *matchObject) { Tcl_Obj *resultObj = Tcl_NewObj(); + Tcl_HashTable processedClassesTable, *processedClasses = &processedClassesTable; - NsfClassInfoInstancesMethod1(interp, startCl, resultObj, withClosure, pattern, matchObject); + Tcl_InitHashTable(processedClasses, TCL_ONE_WORD_KEYS); + MEM_COUNT_ALLOC("Tcl_InitHashTable", processedClasses); + + NsfClassInfoInstancesMethod1(interp, startCl, processedClasses, resultObj, withClosure, pattern, matchObject); Tcl_SetObjResult(interp, resultObj); + Tcl_DeleteHashTable(processedClasses); + MEM_COUNT_FREE("Tcl_InitHashTable", processedClasses); + return TCL_OK; } @@ -23204,25 +23235,15 @@ entry; lastEntry = entry, entry = entry->nextPtr) { NsfObject *object = (NsfObject *)entry->clorobj; - - if (Tcl_Command_flags(entry->cmdPtr) & CMD_IS_DELETED) { - /* - * This is a stale entry, since the cmd in the entry was already - * deleted. This might happen with duplicates in instances. We drop - * the entry from the list. - */ - if (entry == *instances) { - *instances = entry->nextPtr; - CmdListDeleteCmdListEntry(entry, NULL); - entry = *instances; - } else { - lastEntry->nextPtr = entry->nextPtr; - CmdListDeleteCmdListEntry(entry, NULL); - entry = lastEntry; - } - continue; - } + /* + * The list if the instances should contain only alive objects, and non + * of these are duplicates. We would recognize duplicates since a + * deletion of one object would trigger the CMD_IS_DELETED flag of the cmdPtr + * of the duplicate. + */ + assert((Tcl_Command_flags(entry->cmdPtr) & CMD_IS_DELETED) == 0); + if (object && !NsfObjectIsClass(object) && !ObjectHasChildren(object)) { /*fprintf(stderr, "check %p obj->flags %.6x cmd %p deleted %d\n", object, object->flags, entry->cmdPtr, Index: tests/object-system.test =================================================================== diff -u -r6ec717176dee759bf70840eabb6b5728229ed8f9 -r2152f4606b7c4e81fd18018c7c43bf29961a9d1b --- tests/object-system.test (.../object-system.test) (revision 6ec717176dee759bf70840eabb6b5728229ed8f9) +++ tests/object-system.test (.../object-system.test) (revision 2152f4606b7c4e81fd18018c7c43bf29961a9d1b) @@ -1,6 +1,5 @@ # -*- Tcl -*- package require nx -namespace import nx::* ::nsf::configure dtrace on # @@ -21,18 +20,18 @@ ? {::nsf::configure objectsystem} "{::nx::Object ::nx::Class {-class.alloc alloc -class.create create -class.dealloc dealloc -class.objectparameter objectparameter -class.recreate recreate -object.configure configure -object.defaultmethod defaultmethod -object.destroy destroy -object.init init -object.move move -object.unknown unknown}}" -? {::nsf::object::exists Object} 1 -? {::nsf::object::property Object initialized} 1 -? {::nsf::is class Object} 1 -? {::nsf::is metaclass Object} 0 -? {Object info superclass} "" -? {Object info class} ::nx::Class +? {::nsf::object::exists nx::Object} 1 +? {::nsf::object::property nx::Object initialized} 1 +? {::nsf::is class nx::Object} 1 +? {::nsf::is metaclass nx::Object} 0 +? {nx::Object info superclass} "" +? {nx::Object info class} ::nx::Class -? {::nsf::object::exists Class} 1 -? {::nsf::is class Class} 1 -? {::nsf::is metaclass Class} 1 -? {Class info superclass} ::nx::Object -? {Class info class} ::nx::Class +? {::nsf::object::exists nx::Class} 1 +? {::nsf::is class nx::Class} 1 +? {::nsf::is metaclass nx::Class} 1 +? {nx::Class info superclass} ::nx::Object +? {nx::Class info class} ::nx::Class # # Minimal argument passing tests for early problem detection @@ -77,28 +76,28 @@ # Create objects and test its properties # -Object create o -? {::nsf::object::exists Object} 1 +nx::Object create o +? {::nsf::object::exists nx::Object} 1 ? {::nsf::is class o} 0 ? {::nsf::is metaclass o} 0 ? {o info class} ::nx::Object -? {Object info instances o} ::o -? {Object info instances ::o} ::o +? {nx::Object info instances o} ::o +? {nx::Object info instances ::o} ::o -Object create o2 { +nx::Object create o2 { ? {::nsf::object::exists ::o2} 1 ? {::nsf::object::property ::o2 initialized} 0 } ? {::nsf::object::property ::o2 initialized} 1 -Class create C0 +nx::Class create C0 ? {::nsf::is class C0} 1 ? {::nsf::is metaclass C0} 0 ? {C0 info superclass} ::nx::Object ? {C0 info class} ::nx::Class #? {lsort [Class info vars]} "__default_metaclass __default_superclass" -Class create M -superclass ::nx::Class +nx::Class create M -superclass ::nx::Class ? {::nsf::object::exists M} 1 ? {::nsf::is class M} 1 ? {::nsf::is metaclass M} 1 @@ -118,7 +117,7 @@ ? {::nsf::is metaclass c1} 0 ? {c1 info class} ::C -Class create M2 -superclass M +nx::Class create M2 -superclass M ? {::nsf::object::exists M2} 1 ? {::nsf::is class M2} 1 ? {::nsf::is metaclass M2} 1 @@ -156,7 +155,7 @@ # # tests for dispatching methods # -Object create o +nx::Object create o o public method foo {} {return foo} o public method bar1 {} {return bar1-[:foo]} o public method bar2 {} {return bar2-[: foo]} @@ -179,7 +178,7 @@ # basic attributes tests -Class create C { +nx::Class create C { :property {x 1} :property {y 2} } @@ -221,7 +220,7 @@ # # tests for the dispatch command # -Object create o +nx::Object create o o method foo {} {return goo} o method bar {x} {return goo-$x} @@ -234,15 +233,15 @@ o destroy # dispatch with colon names -Object create o {set :x 1} +nx::Object create o {set :x 1} ::nsf::dispatch ::o ::incr x ? {o eval {set :x}} 1 "cmd dispatch without -frame object did not modify the instance variable" ::nsf::directdispatch ::o -frame object ::incr x ? {o eval {set :x}} 2 "cmd dispatch -frame object modifies the instance variable" ? {catch {::nsf::dispatch ::o -frame object ::xxx x}} 1 "cmd dispatch with unknown command" o destroy -Object create o { +nx::Object create o { :public method foo {} { foreach var [list x1 y1 x2 y2 x3 y3] { lappend results $var [info exists :$var] @@ -316,5 +315,18 @@ ::C destroy + +# +# Test instances of diamond class structure. Leave class structure +# around until exit to test handling of pot. duplicated entries +# +nx::Class create A +nx::Class create B1 -superclass A +nx::Class create B2 -superclass A +nx::Class create C -superclass {B1 B2} +? {C create c1} ::c1 +? {A info instances -closure} ::c1 + + puts stderr ===EXIT ::nsf::configure dtrace off