Index: generic/nsf.c =================================================================== diff -u -r70d242f602e1807f7ef67cf9d642bb78f059be44 -r5678f49b80ef65fdcfad0d2500f0c06eb1e5e320 --- generic/nsf.c (.../nsf.c) (revision 70d242f602e1807f7ef67cf9d642bb78f059be44) +++ generic/nsf.c (.../nsf.c) (revision 5678f49b80ef65fdcfad0d2500f0c06eb1e5e320) @@ -98,6 +98,7 @@ typedef struct SetterCmdClientData { NsfObject *object; + Tcl_Obj *varNameObj; NsfParam *paramsPtr; } SetterCmdClientData; @@ -10544,7 +10545,7 @@ &flags, &checkedData, &outObjPtr); if (result == TCL_OK) { - result = SetInstVar(interp, object, objv[0], outObjPtr); + result = SetInstVar(interp, object, cd->varNameObj, outObjPtr); if (flags & NSF_PC_MUST_DECR) { DECR_REF_COUNT(outObjPtr); @@ -10553,7 +10554,7 @@ return result; } else { - return SetInstVar(interp, object, objv[0], objc == 2 ? objv[1] : NULL); + return SetInstVar(interp, object, cd->varNameObj, objc == 2 ? objv[1] : NULL); } } @@ -11027,6 +11028,10 @@ SetterCmdDeleteProc(ClientData clientData) { SetterCmdClientData *setterClientData = (SetterCmdClientData *)clientData; + if (setterClientData->varNameObj) { + DECR_REF_COUNT(setterClientData->varNameObj); + } + if (setterClientData->paramsPtr) { ParamsFree(setterClientData->paramsPtr); } @@ -14396,6 +14401,8 @@ setterClientData = NEW(SetterCmdClientData); setterClientData->paramsPtr = NULL; + setterClientData->varNameObj = NULL; + length = strlen(methodName); for (j=0; jparamsPtr->name; + setterClientData->varNameObj = Tcl_NewStringObj(methodName,-1); } else { setterClientData->paramsPtr = NULL; + setterClientData->varNameObj = parameter; } + INCR_REF_COUNT(setterClientData->varNameObj); + if (cl) { result = NsfAddClassMethod(interp, (Nsf_Class *)cl, methodName, (Tcl_ObjCmdProc*)NsfSetterMethod, Index: tests/parameters.test =================================================================== diff -u -r5357e15dadb6bbb59394222187096850742f8c3b -r5678f49b80ef65fdcfad0d2500f0c06eb1e5e320 --- tests/parameters.test (.../parameters.test) (revision 5357e15dadb6bbb59394222187096850742f8c3b) +++ tests/parameters.test (.../parameters.test) (revision 5678f49b80ef65fdcfad0d2500f0c06eb1e5e320) @@ -1335,3 +1335,45 @@ # ... and check, it did not reset the value to the default ? {c1 a} 2 } + +Test case setter-under-coloncmd-and-interpvarresolver { + # There are (at least) three forms of object-namespace alignment in + # NSF: + # 1. Same-named namespace (::omon) predates a same-named object + # (::omon), the namespace is registered as per-object namespace with + # the object upon object construction -> no NsColonVarResolver(), + # InterpColonVarResolver() is responsible! + # 2. an explicit per-object namespace creation using [:require + # namespace] -> NsColonVarResolver() is put in place! + # 3. Object get per-object members (fields, methods) -> + # NsColonVarResolver() is put in place! + # + # The following test covers scenario 1: Called from within + # NsfSetterMethod(), SetInstVar() verifies, whether there is a + # per-object namespace (objPtr->nsPtr); if so, TCL_NAMESPACE_ONLY is + # set ... in this object/ns alignment scenario, + # InterpColonVarResolver() (!) serves the var resolution request. It + # effectively forward-passes the resolution request when sensing + # TCL_NAMESPACE_ONLY by signalling TCL_CONTINUE. This is a consequence + # of handling the "compiled [variable] vs. AVOID_RESOLVERS" case + # InterpColonVarResolver(). As in colon-prefixed calls to the setter + # method (via ColonCmd()), the colon prefix is present in the + # name-carrying Tcl_Obj used to in SetInstVar(). As we set an object + # frame context, we effectively end up with a colon-prefixed object + # variable :( + + Class create Omon + ::nsf::setter Omon a1 + + namespace eval omon {} + Omon create omon + omon a1 "" + ? {omon info vars a1} "a1" + ? {omon info vars :a1} "" + omon eval { + :a1 "" + ? [list [current] info vars a1] "a1" + # Prior to the fix, [:info vars] would have returned ":a1" + ? [list [current] info vars :a1] "" + } +}