Index: TODO =================================================================== diff -u -r08ec3c401fec6ee6e7358da7f75a3208308d8b6b -re014a6a18a4801102162659f5156600ebd0f7c85 --- TODO (.../TODO) (revision 08ec3c401fec6ee6e7358da7f75a3208308d8b6b) +++ TODO (.../TODO) (revision e014a6a18a4801102162659f5156600ebd0f7c85) @@ -5308,7 +5308,7 @@ C mixin add M2 - # M2 is mixed into C, and implicitely into D + # M2 is mixed into C, and implicitly into D # # Since M2 is a subclass of M1, classes C and D depend as well # on M1 and M0, as seen in the heritage: @@ -5335,7 +5335,7 @@ - cleanup of NsfParameterInvalidateClassCacheCmd(): performance improvements. After the fixing of indirect mixin validation, performance of invalidation went up by a factor of 5. At least, in some important cases (invalidation of - rootclasses like nx::Object / xotcl::Object), we are again on the same level + root-classes like nx::Object / xotcl::Object), we are again on the same level as before (actually slightly faster). - use functions IsRootClass(), IsRootMetaClass() and IsBaseClass() @@ -5365,28 +5365,36 @@ nsf.c: - improve performance of MixinInvalidateObjOrders() by about 30% - by recomping the list of the dependent classes over and over again, + by recompiling the list of the dependent classes over and over again, since MixinInvalidateObjOrders() iterates over the full list already. - Document NsfRelationClassMixinsSet() and add nonnull declarations and the usual assertions() - + +nsf.c +- base MixinInvalidateObjOrders() on DependentSubClasses() and + avoid the need of using a separate hash table for class-mixin + handling. The new implementation is several times faster and + improves the speed of the functions CleanupDestroyClass(), + SuperclassAdd() and NsfRelationClassMixinsSet(). Adding a + class-mixin to ::xotcl::Object in OpenACS is more than 4x faster. +- remove obsolete function MixinResetOrderForInstances() +- rename ResetOrderOfClassesUsedAsMixins() to + ResetOrderOfObjectsUsingThisClassAsObjectMixin() +- used consistenlty DependentSubClasses() instead of TransitiveSubClasses() + for invalidations. +- extend regression test ======================================================================== TODO: +- maybe add "/cl/ info subclasses -dependent" to return union of + transitive closure + class mixins. + - how should we treat cyclic subclass relationships built via class mixins. (either complain at mixin registration or soft warnings or ignoring - since redundant mixin classes are ignored so far). if we disallow cycles in class-mixins, we can't do "nx::Object mixin add M" -- check other places whether DependentSubClasses() should be used instead of - TransitiveSubClasses() - -- MixinInvalidateObjOrders() is based on TransitiveSubClasses but calls - NsfParameterCacheClassInvalidateCmd() which computes its own set - // MixinInvalidateObjOrders calls NsfParameterCacheClassInvalidateCmd - Some functions use before-lists for cleanup, which is fine. - - should we change "/obj/ info lookup syntax /methodName/" to return obj and method as well? (similar to "info method syntax /methodName/") - we could drop methods::object::info::objectparameter Index: generic/nsf.c =================================================================== diff -u -r08ec3c401fec6ee6e7358da7f75a3208308d8b6b -re014a6a18a4801102162659f5156600ebd0f7c85 --- generic/nsf.c (.../nsf.c) (revision 08ec3c401fec6ee6e7358da7f75a3208308d8b6b) +++ generic/nsf.c (.../nsf.c) (revision e014a6a18a4801102162659f5156600ebd0f7c85) @@ -7683,7 +7683,8 @@ assert(object); - /*fprintf(stderr, "MixinResetOrder for object %s \n", ObjectName(object));*/ + //fprintf(stderr, "MixinResetOrder for object %s \n", ObjectName(object)); + CmdListFree(&object->mixinOrder, NULL /*GuardDel*/); object->mixinOrder = NULL; } @@ -8718,51 +8719,8 @@ /* *---------------------------------------------------------------------- - * MixinResetOrderForInstances -- + * ResetOrderOfObjectsUsingThisClassAsObjectMixin -- * - * Reset the per-object mixin order for the instances of the provided - * class. - * - * Results: - * void - * - * Side effects: - * Deletes potentially the mixin list for the objects. - * - *---------------------------------------------------------------------- - */ - -static void MixinResetOrderForInstances(NsfClass *cl) nonnull(1); - -static void -MixinResetOrderForInstances(NsfClass *cl) { - Tcl_HashSearch hSrch; - Tcl_HashEntry *hPtr; - - assert(cl); - - /*fprintf(stderr, "invalidating instances of class %s\n", ClassName(clPtr->cl));*/ - - /* Here we should check, whether this class is used as an object or - class mixin somewhere else and invalidate the objects of these as - well -- */ - - for (hPtr = Tcl_FirstHashEntry(&cl->instances, &hSrch); hPtr; - hPtr = Tcl_NextHashEntry(&hSrch)) { - NsfObject *object = (NsfObject *)Tcl_GetHashKey(&cl->instances, hPtr); - if (object - && !(object->flags & NSF_DURING_DELETE) - && (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID)) { - MixinResetOrder(object); - object->flags &= ~NSF_MIXIN_ORDER_VALID; - } - } -} - -/* - *---------------------------------------------------------------------- - * ResetOrderOfClassesUsedAsMixins -- - * * Reset the per-object mixin order for all objects having this class as * per-object mixin. * @@ -8774,11 +8732,12 @@ * *---------------------------------------------------------------------- */ -static void ResetOrderOfClassesUsedAsMixins(NsfClass *cl) nonnull(1); +static void ResetOrderOfObjectsUsingThisClassAsObjectMixin(NsfClass *cl) nonnull(1); static void -ResetOrderOfClassesUsedAsMixins(NsfClass *cl) { - /*fprintf(stderr, "ResetOrderOfClassesUsedAsMixins %s - %p\n", +ResetOrderOfObjectsUsingThisClassAsObjectMixin(NsfClass *cl) { + + /*fprintf(stderr, "ResetOrderOfObjectsUsingThisClassAsObjectMixin %s - %p\n", ClassName(cl), cl->opt);*/ assert(cl); @@ -8802,9 +8761,9 @@ * MixinInvalidateObjOrders -- * * Reset mixin order for all instances of the class and the instances of - * its subclasses. This function is typically called, when the the class - * hierarchy or the class mixins have changed and invalidate mixin entries - * in all dependent instances. + * its dependent subclasses. This function is typically called, when the + * the class hierarchy or the class mixins have changed and invalidate + * mixin entries in all dependent instances. * * Results: * void @@ -8820,9 +8779,6 @@ static void MixinInvalidateObjOrders(Tcl_Interp *interp, NsfClass *cl, NsfClasses *subClasses) { - Tcl_HashSearch hSrch; - Tcl_HashEntry *hPtr; - Tcl_HashTable objTable, *commandTable = &objTable; assert(interp); assert(cl); @@ -8839,49 +8795,30 @@ /* * Reset mixin order for all objects having this class as per object mixin */ - ResetOrderOfClassesUsedAsMixins(subClasses->cl); + ResetOrderOfObjectsUsingThisClassAsObjectMixin(subClasses->cl); - /* fprintf(stderr, "invalidating instances of class %s\n", ClassName(clPtr)); - */ + //fprintf(stderr, "invalidating instances of class %s\n", ClassName(subClasses->cl)); + if (subClasses->cl->parsedParamPtr) { + ParsedParamFree(subClasses->cl->parsedParamPtr); + subClasses->cl->parsedParamPtr = NULL; + } + instanceTablePtr = &subClasses->cl->instances; for (hPtr = Tcl_FirstHashEntry(instanceTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfObject *object = (NsfObject *)Tcl_GetHashKey(instanceTablePtr, hPtr); - if (object->mixinOrder) { MixinResetOrder(object); } - object->flags &= ~NSF_MIXIN_ORDER_VALID; - } - } - /* - * Reset the mixin order for all objects having this class as a per class - * mixin. This means that we have to work through the class mixin hierarchy - * with its corresponding instances. - */ - Tcl_InitHashTable(commandTable, TCL_ONE_WORD_KEYS); - MEM_COUNT_ALLOC("Tcl_InitHashTable", commandTable); - GetAllClassMixinsOf(interp, commandTable, Tcl_GetObjResult(interp), - cl, 1, 0, NULL, NULL); + assert(object); - for (hPtr = Tcl_FirstHashEntry(commandTable, &hSrch); hPtr; - hPtr = Tcl_NextHashEntry(&hSrch)) { - NsfClass *ncl = (NsfClass *)Tcl_GetHashKey(commandTable, hPtr); - - if (ncl) { - MixinResetOrderForInstances(ncl); - /* - * Here it is sufficient to invalidate the computed configure parameter - * definitions without calling the full-blown - * NsfParameterCacheClassInvalidateCmd(interp, ncl), since we are - * iterating over the full relevant class ist. - */ - if (ncl->parsedParamPtr) { - ParsedParamFree(ncl->parsedParamPtr); - ncl->parsedParamPtr = NULL; + if (!(object->flags & NSF_DURING_DELETE) + && (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID)) { + MixinResetOrder(object); + object->flags &= ~NSF_MIXIN_ORDER_VALID; + //fprintf(stderr, "... %s clearing flag NSF_MIXIN_ORDER_VALID\n", ObjectName(object)); } } } - Tcl_DeleteHashTable(commandTable); - MEM_COUNT_FREE("Tcl_InitHashTable", commandTable); + } /* @@ -10538,7 +10475,7 @@ assert(arg); superClasses = PrecedenceOrder(cl); - subClasses = TransitiveSubClasses(cl); + subClasses = DependentSubClasses(cl); /* * We have to remove all dependent superclass filter referenced @@ -12356,7 +12293,6 @@ * to MethodDispatch/MethodDispatch * TODO: maybe remove NSF_CM_KEEP_CALLER_SELF when done. */ - //yyyy; result = MethodDispatch(object, interp, nobjc+1, nobjv-1, cmd, object, NULL /*NsfClass *cl*/, @@ -13349,7 +13285,6 @@ if (1//(object->flags & NSF_KEEP_CALLER_SELF) //&& (flags & NSF_CSC_CALL_IS_ENSEMBLE) == 0 && (flags & NSF_CM_KEEP_CALLER_SELF)) { - // yyyy calledObject = GetSelfObj(interp); if (calledObject == NULL) { NsfShowStack(interp); @@ -18509,7 +18444,7 @@ cl, ClassName(cl), IsMetaClass(interp, cl, 1), softrecreate, recreate, clopt);*/ - subClasses = TransitiveSubClasses(cl); + subClasses = DependentSubClasses(cl); if (subClasses) { /* @@ -25818,7 +25753,7 @@ * *---------------------------------------------------------------------- */ -static int NsfRelationClassMixinsSet(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *valueObj, int oc, Tcl_Obj **ov) +static int NsfRelationClassMixinsSet(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *valueObj, int oc, Tcl_Obj **ov) nonnull(1) nonnull(2) nonnull(3); static int @@ -25844,7 +25779,7 @@ CmdListFree(&clopt->classMixins, GuardDel); } - subClasses = TransitiveSubClasses(cl); + subClasses = DependentSubClasses(cl); MixinInvalidateObjOrders(interp, cl, subClasses); /* @@ -25891,7 +25826,6 @@ NsfRelationSetCmd(Tcl_Interp *interp, NsfObject *object, int relationtype, Tcl_Obj *valueObj) { int oc; Tcl_Obj **ov; - NsfObject *nObject = NULL; NsfClass *cl = NULL; NsfObjectOpt *objopt = NULL; NsfClassOpt *clopt = NULL, *nclopt = NULL; @@ -26059,7 +25993,7 @@ objopt->objMixins = newMixinCmdList; for (cmds = newMixinCmdList; cmds; cmds = cmds->nextPtr) { - nObject = NsfGetObjectFromCmdPtr(cmds->cmdPtr); + NsfObject *nObject = NsfGetObjectFromCmdPtr(cmds->cmdPtr); if (nObject) { nclopt = NsfRequireClassOpt((NsfClass *) nObject); CmdListAddSorted(&nclopt->isObjectMixinOf, object->id, NULL); @@ -26162,7 +26096,7 @@ } if (FiltersDefined(interp) > 0) { - NsfClasses *subClasses = TransitiveSubClasses(cl); + NsfClasses *subClasses = DependentSubClasses(cl); if (subClasses) { FilterInvalidateObjOrders(interp, subClasses); NsfClassListFree(subClasses); @@ -28279,7 +28213,7 @@ NsfCmdList *h = CmdListFindNameInList(interp, filter, opt->classFilters); if (h) { - NsfClasses *subClasses = TransitiveSubClasses(cl); + NsfClasses *subClasses = DependentSubClasses(cl); if (h->clientData) { GuardDel(h); @@ -28351,7 +28285,7 @@ GuardDel((NsfCmdList *) h); } GuardAdd(h, guardObj); - subClasses = TransitiveSubClasses(cl); + subClasses = DependentSubClasses(cl); MixinInvalidateObjOrders(interp, cl, subClasses); NsfClassListFree(subClasses); return TCL_OK; @@ -29022,7 +28956,7 @@ /* * Then add the class provided slot objects. */ - for (clPtr = precendenceList; clPtr; clPtr = clPtr->nextPtr) { + for (clPtr = precendenceList; likely(clPtr != NULL); clPtr = clPtr->nextPtr) { if (MethodSourceMatches(withSource, clPtr->cl, NULL)) { AddSlotObjects(interp, &clPtr->cl->object, "::slot", &slotTable, withSource, type, pattern, listObj); @@ -29221,6 +29155,7 @@ precedenceList = ComputePrecedenceList(interp, object, pattern, !withIntrinsicOnly, 1); for (pl = precedenceList; pl; pl = pl->nextPtr) { + assert(pl->cl); Tcl_ListObjAppendElement(interp, resultObj, pl->cl->object.cmdName); } if (precedenceList) NsfClassListFree(precedenceList); Index: tests/properties.test =================================================================== diff -u -rd86805a2250aaa861470d4f4a13945d603585aca -re014a6a18a4801102162659f5156600ebd0f7c85 --- tests/properties.test (.../properties.test) (revision d86805a2250aaa861470d4f4a13945d603585aca) +++ tests/properties.test (.../properties.test) (revision e014a6a18a4801102162659f5156600ebd0f7c85) @@ -1012,11 +1012,11 @@ nx::test configure -count 1 nx::test case indirect-transitive-mixin-info { - nx::Class create M0 + nx::Class create M0 {:public method foo {} {return M}} nx::Class create M1 -superclass M0 nx::Class create M2 -superclass M1 - nx::Class create C + nx::Class create C {:public method foo {} {return C}} nx::Class create D -superclass C C create c1 @@ -1025,9 +1025,12 @@ M1 create m1 M2 create m2 - ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} "objectName object-mixin class object-filter __initblock" + ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} \ + "objectName object-mixin class object-filter __initblock" set base [llength [lmap p [C info lookup parameters create] {nsf::parameter::info name $p}]] + ? [list set _ $base] 5 + ? {llength [C info lookup parameters create]} $base ? {llength [D info lookup parameters create]} $base ? {llength [M0 info lookup parameters create]} $base @@ -1040,11 +1043,28 @@ ? {llength [M1 info lookup parameters create]} [expr {$base + 1}] ? {llength [M2 info lookup parameters create]} [expr {$base + 1}] + ? {c1 foo} C + ? {d1 foo} C + + ? {c1 info precedence} "::C ::nx::Object" + ? {d1 info precedence} "::D ::C ::nx::Object" + + #puts stderr =========C-mixin-add-M2 C mixin add M2 + #puts stderr ========= + ? {c1 foo} M + ? {d1 foo} M + + ? {c1 info precedence} "::M2 ::M1 ::M0 ::C ::nx::Object" + ? {d1 info precedence} "::M2 ::M1 ::M0 ::D ::C ::nx::Object" + ? {C info heritage} "::M2 ::M1 ::M0 ::nx::Object" ? {D info heritage} "::M2 ::M1 ::M0 ::C ::nx::Object" + # Only M2 is a direct mixin, visible through "mixinof", + # but query-able via transitive -closure operator + ? {M2 info mixinof} "::C" ? {M2 info mixinof -closure} "::C ::D" @@ -1060,9 +1080,9 @@ ? {llength [C info lookup parameters create]} [expr {$base + 1}] ? {llength [D info lookup parameters create]} [expr {$base + 1}] - puts stderr =========-M1-property + #puts stderr =========-M1-property M1 property y - puts stderr ========= + #puts stderr ========= ? {C info heritage} "::M2 ::M1 ::M0 ::nx::Object" #::nsf::parameter::cache::classinvalidate ::C @@ -1081,6 +1101,17 @@ ? {llength [M0 info lookup parameters create]} [expr {$base + 1}] ? {llength [M1 info lookup parameters create]} [expr {$base + 2}] ? {llength [M2 info lookup parameters create]} [expr {$base + 2}] + + # clearning the mixin has to reset the orders of the instances of C and D + #puts stderr =========C-mixin-clear + C mixin clear + #puts stderr ========= + + ? {c1 foo} C + ? {d1 foo} C + + ? {c1 info precedence} "::C ::nx::Object" + ? {d1 info precedence} "::D ::C ::nx::Object" }