Index: TODO =================================================================== diff -u -r26480a59b14cf250904da0cdc7d895f21b0ed5fd -reae784ccc80b2a18b83fbe631c32d549189f7927 --- TODO (.../TODO) (revision 26480a59b14cf250904da0cdc7d895f21b0ed5fd) +++ TODO (.../TODO) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) @@ -4008,7 +4008,18 @@ .nxd file - adding more comments to examples in migration guide - document private properties in tutorial and migration guide +- improve wording in documenting +- extend regression test +nsfShadow.c +- bump MethodEpoch when a tcl ::rename command happens + on a nsf method (which might be cached in a Tcl_Obj) + This fixes a crash reported by Arthur Schreiber +nsf.c: +- make NsfInstanceMethodEpochIncr() and NsfObjectMethodEpochIncr() + accessible from all files using nsfInt.h +- remove experimental code (backslash escaping for "," in parameter + option parse ======================================================================== TODO: Index: generic/nsf.c =================================================================== diff -u -r844e640084704c4863864f59d2c258b8019cc69f -reae784ccc80b2a18b83fbe631c32d549189f7927 --- generic/nsf.c (.../nsf.c) (revision 844e640084704c4863864f59d2c258b8019cc69f) +++ generic/nsf.c (.../nsf.c) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) @@ -185,21 +185,6 @@ } enumeratorConverterEntry; /* - * Definition of methodEpoch macros - */ -#if defined(METHOD_OBJECT_TRACE) -# define NsfInstanceMethodEpochIncr(msg) \ - RUNTIME_STATE(interp)->instanceMethodEpoch++; \ - fprintf(stderr, "+++ instanceMethodEpoch %d %s\n", RUNTIME_STATE(interp)->instanceMethodEpoch, msg) -# define NsfObjectMethodEpochIncr(msg) \ - RUNTIME_STATE(interp)->objectMethodEpoch++; \ - fprintf(stderr, "+++ objectMethodEpoch %d %s\n", RUNTIME_STATE(interp)->objectMethodEpoch, msg) -#else -# define NsfInstanceMethodEpochIncr(msg) RUNTIME_STATE(interp)->instanceMethodEpoch++ -# define NsfObjectMethodEpochIncr(msg) RUNTIME_STATE(interp)->objectMethodEpoch++ -#endif - -/* * Tcl_Obj Types for Next Scripting Objects */ @@ -12066,7 +12051,7 @@ if (argString[j] == ':') { /* we found a ':' */ size_t l, start, end; - int escaped = 0, unescape = 0; + int unescape = 0; /* get parameter name */ STRING_NEW(paramPtr->name, argString, j); @@ -12078,18 +12063,6 @@ /* search for unescaped ',' */ for (l = start; l < length; l++) { -#if 0 - if (unlikely(escaped == 1)) { - fprintf(stderr, "escaped char %c\n", argString[l]); - escaped = 0; - continue; - } - if (unlikely(argString[l] == '\\')) { - fprintf(stderr, "escape char %c\n", argString[l]); - escaped = 1; - continue; - } -#endif if (unlikely(argString[l] == ',')) { if (likely(argString[l+1]) == ',') { l++; Index: generic/nsfInt.h =================================================================== diff -u -r7d2af5a3b3f0ac8ade700c3cd90a9aac5d00f0fa -reae784ccc80b2a18b83fbe631c32d549189f7927 --- generic/nsfInt.h (.../nsfInt.h) (revision 7d2af5a3b3f0ac8ade700c3cd90a9aac5d00f0fa) +++ generic/nsfInt.h (.../nsfInt.h) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) @@ -939,6 +939,21 @@ EXTERN Tcl_Obj *NsfMethodNamePath(Tcl_Interp *interp, Tcl_Obj *procObj); +/* + * Definition of methodEpoch macros + */ +#if defined(METHOD_OBJECT_TRACE) +# define NsfInstanceMethodEpochIncr(msg) \ + RUNTIME_STATE(interp)->instanceMethodEpoch++; \ + fprintf(stderr, "+++ instanceMethodEpoch %d %s\n", RUNTIME_STATE(interp)->instanceMethodEpoch, msg) +# define NsfObjectMethodEpochIncr(msg) \ + RUNTIME_STATE(interp)->objectMethodEpoch++; \ + fprintf(stderr, "+++ objectMethodEpoch %d %s\n", RUNTIME_STATE(interp)->objectMethodEpoch, msg) +#else +# define NsfInstanceMethodEpochIncr(msg) RUNTIME_STATE(interp)->instanceMethodEpoch++ +# define NsfObjectMethodEpochIncr(msg) RUNTIME_STATE(interp)->objectMethodEpoch++ +#endif + /* * NsfFlag type */ Index: generic/nsfShadow.c =================================================================== diff -u -r7d2af5a3b3f0ac8ade700c3cd90a9aac5d00f0fa -reae784ccc80b2a18b83fbe631c32d549189f7927 --- generic/nsfShadow.c (.../nsfShadow.c) (revision 7d2af5a3b3f0ac8ade700c3cd90a9aac5d00f0fa) +++ generic/nsfShadow.c (.../nsfShadow.c) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) @@ -213,22 +213,28 @@ */ static int Nsf_RenameObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Command cmd; + Tcl_Command cmd, parentCmd; if (objc != 3) { /* wrong # args, let Tcl generate the error */ return NsfCallCommand(interp, NSF_RENAME, objc, objv); } /* if an obj/cl should be renamed => call the Nsf move method */ - cmd = Tcl_FindCommand(interp, ObjStr(objv[1]), (Tcl_Namespace *)NULL,0); + cmd = Tcl_FindCommand(interp, ObjStr(objv[1]), (Tcl_Namespace *)NULL, 0); if (cmd) { NsfObject *object = NsfGetObjectFromCmdPtr(cmd); Tcl_Obj *methodObj = object ? NsfMethodObj(object, NSF_o_move_idx) : NULL; if (object && methodObj) { return NsfCallMethodWithArgs(interp, (Nsf_Object *)object, methodObj, objv[2], 1, 0, NSF_CSC_IMMEDIATE); } + + parentCmd = Tcl_FindCommand(interp, Tcl_Command_nsPtr(cmd)->fullName, + (Tcl_Namespace *)NULL, 0); + if (parentCmd) { + NsfObjectMethodEpochIncr("::rename"); + } } /* Actually rename the cmd using Tcl's rename*/ Index: tests/destroy.test =================================================================== diff -u -r17ad6747e40c1724810371f92f0108b12c1d5284 -reae784ccc80b2a18b83fbe631c32d549189f7927 --- tests/destroy.test (.../destroy.test) (revision 17ad6747e40c1724810371f92f0108b12c1d5284) +++ tests/destroy.test (.../destroy.test) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) @@ -920,4 +920,56 @@ interp delete $i unset i +} + +# +# Exercise renaming of cmds which are used as methods +# +nx::Test case rename-cached-method { + # Create a class with a namespace + nx::Class create A {:public class method foo args {}} + # + # Add a proc named "new" to the namespace of the class. + # This is not recommended, but we can't avoid it. + # + proc ::A::new {} { return "something from A" } + # + # Call the proc via the method interface. After the call the cmd is + # cached in the Tcl_Obj "new". + # + ? {A new} "something from A" + # + # Delete the proc. The rename command has to take care, that the + # cached cmd has to be invalidated. + # + rename ::A::new "" + + # + # We expect that the original method works again. + # + ? {string match ::nsf::__#* [A new]} 1 + + # + # Now try the same with the internal namespace from nsf. Messing + # around there is even less wanted, but still, we can't avoid this. + # We make first a backup of the method. + # + rename ::nsf::classes::nx::Class::new ::nsf::classes::nx::Class::new.orig + proc ::nsf::classes::nx::Class::new {} { return "something" } + + ? {A new} "something" + # + # Delete the proc and call "new" again + # + rename ::nsf::classes::nx::Class::new "" + ? {A new} "method 'new' unknown for ::A; consider '::A create new ' instead of '::A new '" + + # + # Restore the original state + # + rename ::nsf::classes::nx::Class::new.orig ::nsf::classes::nx::Class::new + # + # We expect that the original method works again. + # + ? {string match ::nsf::__#* [A new]} 1 } \ No newline at end of file