Index: TODO =================================================================== diff -u -r994c14a5b0e1d662fc4f903f097ed0ee7a130986 -reea18b07ddfc917545d48ab6a272c0bfb9656f07 --- TODO (.../TODO) (revision 994c14a5b0e1d662fc4f903f097ed0ee7a130986) +++ TODO (.../TODO) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) @@ -5134,9 +5134,25 @@ - added methods "info lookup filters ?-guards? ?/pattern/?" and "info lookup methods *-guards? ?/pattern/?" + +nsf.c +- force again literal "-guard" in a "mixinreg" type to avoid + potential confusions +- Base nsfRelationSet for mixins on cmds rather than TclObjs + to avoid confusions and duplication of guard detection logic +- Add interp to NsfMixinregGet() and NsfFilterregGet() to be + able to return error messages +- return more error message from mixinreg converter +- provide at least minimal support for "funny class names" + (i.e. containing spaces) +- FinalObjectDeletion: don't enforce namespace = 1 for cases + with wierd namespace deletion order - extended regression test + + ======================================================================== TODO: +- /obj/ info parameter -> nsf::parameter ??? - TODO: update tutorial and migration guide - finish nx-property reform (merge into master) Index: generic/nsf.c =================================================================== diff -u -r994c14a5b0e1d662fc4f903f097ed0ee7a130986 -reea18b07ddfc917545d48ab6a272c0bfb9656f07 --- generic/nsf.c (.../nsf.c) (revision 994c14a5b0e1d662fc4f903f097ed0ee7a130986) +++ generic/nsf.c (.../nsf.c) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) @@ -7777,6 +7777,7 @@ /*fprintf(stderr, "MixinAdd gets obj %p type %p %s\n", nameObj, nameObj->typePtr, nameObj->typePtr?nameObj->typePtr->name : "NULL");*/ + /* * When the provided nameObj is of type NsfMixinregObjType, the nsf specific * converter was called already; otherwise call the converter here. @@ -7787,9 +7788,12 @@ } } - NsfMixinregGet(nameObj, &mixinCl, &guardObj); + NsfMixinregGet(interp, nameObj, &mixinCl, &guardObj); + + assert((Tcl_Command_flags(mixinCl->object.id) & CMD_IS_DELETED) == 0); new = CmdListAdd(mixinList, mixinCl->object.id, NULL, /*noDuplicates*/ 1, 1); + if (guardObj) { GuardAdd(new, guardObj); } else if (new->clientData) { @@ -9636,7 +9640,7 @@ /*fprintf(stderr, "FilterAdd: %s already converted\n", ObjStr(filterregObj));*/ } - NsfFilterregGet(filterregObj, &filterObj, &guardObj); + NsfFilterregGet(interp, filterregObj, &filterObj, &guardObj); if (!(cmd = FilterSearch(ObjStr(filterObj), startingObject, startingClass, &cl))) { if (startingObject) { @@ -25422,8 +25426,11 @@ switch (relationtype) { case RelationtypeObject_mixinIdx: { - NsfCmdList *newMixinCmdList = NULL; + NsfCmdList *newMixinCmdList = NULL, *cmds; + /* + * Add every mixin class + */ for (i = 0; i < oc; i++) { if (MixinAdd(interp, &newMixinCmdList, ov[i], object->cl->object.cl) != TCL_OK) { CmdListFree(&newMixinCmdList, GuardDel); @@ -25434,6 +25441,9 @@ if (objopt->objMixins) { NsfCmdList *cmdlist, *del; + /* + * Delete from old isObjectMixinOf lists + */ for (cmdlist = objopt->objMixins; cmdlist; cmdlist = cmdlist->nextPtr) { cl = NsfGetClassFromCmdPtr(cmdlist->cmdPtr); clopt = cl ? cl->opt : NULL; @@ -25462,23 +25472,20 @@ object->flags &= ~NSF_FILTER_ORDER_VALID; /* - * Now add the specified mixins. + * Now register the specified mixins. */ objopt->objMixins = newMixinCmdList; - for (i = 0; i < oc; i++) { - Tcl_Obj *ocl = NULL; - /* fprintf(stderr, "Added to mixins of %s: %s\n", ObjectName(object), ObjStr(ov[i])); */ - Tcl_ListObjIndex(interp, ov[i], 0, &ocl); - GetObjectFromObj(interp, ocl, &nObject); + for (cmds = newMixinCmdList; cmds; cmds = cmds->nextPtr) { + nObject = NsfGetObjectFromCmdPtr(cmds->cmdPtr); if (nObject) { - /* fprintf(stderr, "Registering object %s to isObjectMixinOf of class %s\n", - ObjectName(object), ObjectName(nObject)); */ - nclopt = NsfRequireClassOpt((NsfClass *)nObject); + nclopt = NsfRequireClassOpt((NsfClass *) nObject); CmdListAddSorted(&nclopt->isObjectMixinOf, object->id, NULL); - - } /* else fprintf(stderr, "Problem registering %s as a mixinof of %s\n", - ObjStr(ov[i]), ClassName(cl)); */ + } else { + NsfLog(interp, NSF_LOG_WARN, + "Problem registering %s as a mixin of %s\n", + ObjStr(valueObj), ClassName(cl)); + } } MixinComputeDefined(interp, object); @@ -25511,7 +25518,7 @@ case RelationtypeClass_mixinIdx: { - NsfCmdList *newMixinCmdList = NULL; + NsfCmdList *newMixinCmdList = NULL, *cmds; NsfClasses *subClasses; for (i = 0; i < oc; i++) { @@ -25527,6 +25534,7 @@ subClasses = TransitiveSubClasses(cl); MixinInvalidateObjOrders(interp, cl, subClasses); + /* * Since methods of mixed in classes may be used as filters, we have to * invalidate the filters as well. @@ -25536,22 +25544,26 @@ } NsfClassListFree(subClasses); + /* + * Now register the specified mixins. + */ clopt->classMixins = newMixinCmdList; - for (i = 0; i < oc; i++) { - Tcl_Obj *ocl = NULL; - /* fprintf(stderr, "Added to class-mixins of %s: %s\n", - ClassName(cl), ObjStr(ov[i])); */ - Tcl_ListObjIndex(interp, ov[i], 0, &ocl); - GetObjectFromObj(interp, ocl, &nObject); + /* + * Finally, update classMixinOfs + */ + for (cmds = newMixinCmdList; cmds; cmds = cmds->nextPtr) { + nObject = NsfGetObjectFromCmdPtr(cmds->cmdPtr); if (nObject) { - /* fprintf(stderr, "Registering class %s to isClassMixinOf of class %s\n", - ClassName(cl), ObjectName(nObject)); */ nclopt = NsfRequireClassOpt((NsfClass *) nObject); CmdListAddSorted(&nclopt->isClassMixinOf, cl->object.id, NULL); - } /* else fprintf(stderr, "Problem registering %s as a class-mixin of %s\n", - ObjStr(ov[i]), ClassName(cl)); */ + } else { + NsfLog(interp, NSF_LOG_WARN, + "Problem registering %s as a mixin of %s\n", + ObjStr(valueObj), ClassName(cl)); + } } + break; } @@ -29349,13 +29361,13 @@ */ if (unlikely(object->refCount != 1)) { if (object->refCount > 1) { - NsfLog(interp, NSF_LOG_WARN, "Have to fix refCount for obj %p refCount %d (name %s)", + NsfLog(interp, NSF_LOG_WARN, "RefCount for obj %p %d (name %s) > 1", object, object->refCount, ObjectName(object)); } else { - NsfLog(interp, NSF_LOG_WARN, "Have to fix refCount for obj %p refCount %d", + NsfLog(interp, NSF_LOG_WARN, "Refcount for obj %p %d > 1", object, object->refCount); } - object->refCount = 1; + /*object->refCount = 1;*/ } #if !defined(NDEBUG) Index: generic/nsfInt.h =================================================================== diff -u -rf671281a240219965d436e2bfa762baf85274ca6 -reea18b07ddfc917545d48ab6a272c0bfb9656f07 --- generic/nsfInt.h (.../nsfInt.h) (revision f671281a240219965d436e2bfa762baf85274ca6) +++ generic/nsfInt.h (.../nsfInt.h) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) @@ -680,12 +680,12 @@ /* obj types */ EXTERN Tcl_ObjType NsfMixinregObjType; -int NsfMixinregGet(Tcl_Obj *obj, NsfClass **clPtr, Tcl_Obj **guardObj) - nonnull(1) nonnull(2) nonnull(3); +int NsfMixinregGet(Tcl_Interp *interp, Tcl_Obj *obj, NsfClass **clPtr, Tcl_Obj **guardObj) + nonnull(1) nonnull(2) nonnull(3) nonnull(4); EXTERN Tcl_ObjType NsfFilterregObjType; -int NsfFilterregGet(Tcl_Obj *obj, Tcl_Obj **filterObj, Tcl_Obj **guardObj) - nonnull(1) nonnull(2) nonnull(3); +int NsfFilterregGet(Tcl_Interp *interp, Tcl_Obj *obj, Tcl_Obj **filterObj, Tcl_Obj **guardObj) + nonnull(1) nonnull(2) nonnull(3) nonnull(4); /* Next Scripting ShadowTclCommands */ typedef struct NsfShadowTclCommandInfo { Index: generic/nsfObj.c =================================================================== diff -u -r24cc5e107fd8d246061a9d4b4fafefc767811c2b -reea18b07ddfc917545d48ab6a272c0bfb9656f07 --- generic/nsfObj.c (.../nsfObj.c) (revision 24cc5e107fd8d246061a9d4b4fafefc767811c2b) +++ generic/nsfObj.c (.../nsfObj.c) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) @@ -26,6 +26,8 @@ */ #include "nsfInt.h" +#include "nsfAccessInt.h" + /* *---------------------------------------------------------------------- * @@ -405,7 +407,7 @@ register Tcl_Obj *objPtr) /* The object to convert. */ { NsfClass *mixin = NULL; - Tcl_Obj *guardObj = NULL, *nameObj; + Tcl_Obj *guardObj = NULL, *nameObj = NULL; Mixinreg *mixinRegPtr; int oc; Tcl_Obj **ov; @@ -414,20 +416,20 @@ if (oc == 1) { nameObj = ov[0]; - } else if (oc == 2) { + /*} else if (oc == 2) { nameObj = ov[0]; - guardObj = ov[1]; + guardObj = ov[1];*/ } else if (oc == 3 && !strcmp(ObjStr(ov[1]), NsfGlobalStrings[NSF_GUARD_OPTION])) { nameObj = ov[0]; guardObj = ov[2]; } else { - return TCL_ERROR; + nameObj = objPtr; } } else { - return TCL_ERROR; + return NsfObjErrType(interp, "mixin", nameObj, "a class as mixin", NULL); } /* @@ -484,14 +486,30 @@ */ int -NsfMixinregGet(Tcl_Obj *obj, NsfClass **clPtr, Tcl_Obj **guardObj) { +NsfMixinregGet(Tcl_Interp *interp, Tcl_Obj *obj, NsfClass **clPtr, Tcl_Obj **guardObj) { + assert(interp); assert(obj); assert(clPtr); assert(guardObj); if (obj->typePtr == &NsfMixinregObjType) { Mixinreg *mixinRegPtr = obj->internalRep.twoPtrValue.ptr1; + + /* + * We got a cmd, but this might be already deleted. + */ + if ((Tcl_Command_flags(mixinRegPtr->mixin->object.id) & CMD_IS_DELETED)) { + /* + * The cmd is deleted. retry to refetch it. + */ + if (MixinregSetFromAny(interp, obj) == TCL_OK) { + mixinRegPtr = obj->internalRep.twoPtrValue.ptr1; + } else { + return TCL_ERROR; + } + } + *guardObj = mixinRegPtr->guardObj; *clPtr = mixinRegPtr->mixin; return TCL_OK; @@ -610,9 +628,9 @@ if (oc == 1) { filterObj = ov[0]; - } else if (oc == 2) { + /* } else if (oc == 2) { filterObj = ov[0]; - guardObj = ov[1]; + guardObj = ov[1];*/ } else if (oc == 3 && !strcmp(ObjStr(ov[1]), NsfGlobalStrings[NSF_GUARD_OPTION])) { filterObj = ov[0]; @@ -672,7 +690,7 @@ */ int -NsfFilterregGet(Tcl_Obj *obj, Tcl_Obj **filterObj, Tcl_Obj **guardObj) { +NsfFilterregGet(Tcl_Interp *UNUSED(interp), Tcl_Obj *obj, Tcl_Obj **filterObj, Tcl_Obj **guardObj) { assert(obj); assert(filterObj); Index: library/xotcl/tests/speedtest.xotcl =================================================================== diff -u -r4bc60e16c10fdbbb640b3019d4bdebdc469fdf55 -reea18b07ddfc917545d48ab6a272c0bfb9656f07 --- library/xotcl/tests/speedtest.xotcl (.../speedtest.xotcl) (revision 4bc60e16c10fdbbb640b3019d4bdebdc469fdf55) +++ library/xotcl/tests/speedtest.xotcl (.../speedtest.xotcl) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) @@ -391,17 +391,17 @@ nx::test new \ -count 100 \ - -pre {Class D; Class E; Class X -instmixin {D E}} \ - -cmd {X info instmixin D} \ + -pre {Class D; Class E; Class X1 -instmixin {D E}} \ + -cmd {X1 info instmixin D} \ -expected {::D} \ - -post {foreach o {D E X} {$o destroy}} + -post {foreach o {D E X1} {$o destroy}} nx::test new \ -count 100 \ - -pre {Class D; Class E; Class X -instmixin {D E}} \ - -cmd {X info instmixin E} \ + -pre {Class D; Class E; Class X2 -instmixin {D E}} \ + -cmd {X2 info instmixin E} \ -expected {::E} \ - -post {foreach o {D E X} {$o destroy}} + -post {foreach o {D E X2} {$o destroy}} nx::test new \ -count 100 \ @@ -412,10 +412,10 @@ nx::test new \ -count 100 \ - -pre {Class D; Class E; Class X -instmixin {D E}} \ - -cmd {X info instmixin ::E*} \ + -pre {Class D; Class E; Class X3 -instmixin {D E}} \ + -cmd {X3 info instmixin ::E*} \ -expected {::E} \ - -post {foreach o {D E X} {$o destroy}} + -post {foreach o {D E X3} {$o destroy}} nx::test new \ -count 100 \ Index: tests/interceptor-slot.test =================================================================== diff -u -r994c14a5b0e1d662fc4f903f097ed0ee7a130986 -reea18b07ddfc917545d48ab6a272c0bfb9656f07 --- tests/interceptor-slot.test (.../interceptor-slot.test) (revision 994c14a5b0e1d662fc4f903f097ed0ee7a130986) +++ tests/interceptor-slot.test (.../interceptor-slot.test) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) @@ -553,7 +553,7 @@ # # ... and we register it together with a guard. # - :filter add {loggingFilter { + :filter add {loggingFilter -guard { [current calledmethod] in {enter leave} }} } @@ -625,7 +625,45 @@ ? {d1 info lookup mixins} "::M2 ::M3" } + # +# Test potential confusion in case a class has a space in its name +# when registering methods or mixins. +# + +nx::test case space-in-classname { + nx::Class create M1 { + :public method foo {} {return "[next] [current class]"} + } + + # + # Define a class with a space in its name, containing a method. This + # class will be used as a mixin class later on. + # + nx::Class create "M1 b" -superclass M1 { + :public method foo {} {return next-[current class]} + } + + nx::Class create C { + :public method foo {} {return foo} + :create c1 + } + + # Test the base case + ? {c1 foo} foo + + # Add spacy class as a mixin. Check, if the introspection returns + # sensible values. + ? {C mixin add "M1 b"} "{::M1 b}" + ? {C info mixin classes} "{::M1 b}" + ? {M1 info mixin classes} "" + ? {M1 info mixinof} "" + ? {"M1 b" info mixin classes} "" + + # check the result of the mixin class + ? {c1 foo} "next-::M1 b" +} +# # Local variables: # mode: tcl # tcl-indent-level: 2