Index: TODO =================================================================== diff -u -reae784ccc80b2a18b83fbe631c32d549189f7927 -r770232a210b63fdafc5e5e4a2caf45fa5097c6fe --- TODO (.../TODO) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) +++ TODO (.../TODO) (revision 770232a210b63fdafc5e5e4a2caf45fa5097c6fe) @@ -4020,6 +4020,18 @@ accessible from all files using nsfInt.h - remove experimental code (backslash escaping for "," in parameter option parse + +nsf.c: + +- added a SlotContainerCmdResolver() to avoid interaction of slot + names with names of callable tcl commands. Without the + SlotContainerCmdResolver() the call to "list" in a property named + "list" leads to a call to the container object ::Test2::slot::list + instead to the intended ::list. The implementation is not perfect, + since it ignores the namespace path inside the slot container. +- added regression test. + + ======================================================================== TODO: Index: generic/nsf.c =================================================================== diff -u -reae784ccc80b2a18b83fbe631c32d549189f7927 -r770232a210b63fdafc5e5e4a2caf45fa5097c6fe --- generic/nsf.c (.../nsf.c) (revision eae784ccc80b2a18b83fbe631c32d549189f7927) +++ generic/nsf.c (.../nsf.c) (revision 770232a210b63fdafc5e5e4a2caf45fa5097c6fe) @@ -260,6 +260,7 @@ CONST char *name); static Tcl_Namespace *RequireObjNamespace(Tcl_Interp *interp, NsfObject *object); static int NSDeleteCmd(Tcl_Interp *interp, Tcl_Namespace *nsPtr, CONST char *methodName); +static void NSNamespaceDeleteProc(ClientData clientData); static void NSNamespacePreserve(Tcl_Namespace *nsPtr); static void NSNamespaceRelease(Tcl_Namespace *nsPtr); @@ -3879,10 +3880,11 @@ CallFrame *varFramePtr; int frameFlags; - /*fprintf(stderr, "InterpColonCmdResolver %s flags %.6x\n", cmdName, flags);*/ + /* fprintf(stderr, "InterpColonCmdResolver %s flags %.6x\n", cmdName, flags); */ if (likely((*cmdName == ':' && *(cmdName + 1) == ':') || flags & TCL_GLOBAL_ONLY)) { /* fully qualified names and global lookups are not for us */ + /*fprintf(stderr, "... not for us %s flags %.6x\n", cmdName, flags);*/ return TCL_CONTINUE; } @@ -3983,6 +3985,7 @@ * *********************************************************/ + /* *---------------------------------------------------------------------- * NsfNamespaceInit -- @@ -4008,9 +4011,11 @@ * acquiring the namespace. Works for object-scoped commands/procs * and object-only ones (set, unset, ...) */ - Tcl_SetNamespaceResolvers(nsPtr, /*(Tcl_ResolveCmdProc *)NsColonCmdResolver*/ NULL, + Tcl_SetNamespaceResolvers(nsPtr, + (Tcl_ResolveCmdProc *)NULL, NsColonVarResolver, - /*(Tcl_ResolveCompiledVarProc *)NsCompiledColonVarResolver*/NULL); + (Tcl_ResolveCompiledVarProc *)NULL); + #if defined(NSF_WITH_INHERIT_NAMESPACES) /* * In case there is a namespace path set for the parent namespace, @@ -4037,6 +4042,57 @@ /* *---------------------------------------------------------------------- + * SlotContainerCmdResolver -- + * + * This is a specialized cmd resolver for slotcontainer. The command + * resolver should be registered for a namespace and avoids the lookup of + * childobjs for unqualified calls. This way, it is e.g. possible to call + * in a slot-obj a method [list], even in cases, where a a property "list" + * is defined. + * + * Results: + * either TCL_CONTINUE or TCL_OK; + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +SlotContainerCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *nsPtr, int flags, Tcl_Command *cmdPtr) { + + if (*cmdName == ':' || (flags & TCL_GLOBAL_ONLY)) { + /* colon names (InterpColonCmdResolver) and global lookups are not for us */ + return TCL_CONTINUE; + } + /*fprintf(stderr, "DotCmdResolver called with %s ns %s ourNs %d\n", + cmdName, nsPtr->fullName, nsPtr->deleteProc == NSNamespaceDeleteProc);*/ + + /* + * Check, if this already a namespace handled by NSF + */ + if (nsPtr->deleteProc == NSNamespaceDeleteProc && nsPtr->clientData) { + NsfObject *parentObject = (NsfObject *) nsPtr->clientData; + /* + * Make global lookups when the parent is a slotcontainer + */ + /* parentObject = (NsfObject *) GetObjectFromString(interp, nsPtr->fullName);*/ + if ((parentObject->flags & NSF_IS_SLOT_CONTAINER)) { + Tcl_Command cmd = Tcl_FindCommand(interp, cmdName, NULL, TCL_GLOBAL_ONLY); + + if (cmd) { + *cmdPtr = cmd; + return TCL_OK; + } + } + } + + return TCL_CONTINUE; + } + +/* + *---------------------------------------------------------------------- * RequireObjNamespace -- * * Obtain for an object a namespace if necessary and initialize it. @@ -18464,15 +18520,15 @@ */ static int -SetBooleanFlag(Tcl_Interp *interp, unsigned int *flagsPtr, unsigned int flag, Tcl_Obj *valueObj) { - int bool, result; +SetBooleanFlag(Tcl_Interp *interp, unsigned int *flagsPtr, unsigned int flag, Tcl_Obj *valueObj, int *flagValue) { + int result; assert(flagsPtr); - result = Tcl_GetBooleanFromObj(interp, valueObj, &bool); + result = Tcl_GetBooleanFromObj(interp, valueObj, flagValue); if (result != TCL_OK) { return result; } - if (bool) { + if (*flagValue) { *flagsPtr |= flag; } else { *flagsPtr &= ~flag; @@ -19510,10 +19566,26 @@ } flag = NSF_IS_SLOT_CONTAINER; if (valueObj) { - int result = SetBooleanFlag(interp, &containerObject->flags, flag, valueObj); + int flagValue; + int result = SetBooleanFlag(interp, &containerObject->flags, flag, valueObj, &flagValue); + if (result != TCL_OK) { return result; } + assert(containerObject->nsPtr); + if (flagValue) { + /* turn on SlotContainerCmdResolver */ + Tcl_SetNamespaceResolvers(containerObject->nsPtr, + (Tcl_ResolveCmdProc *)SlotContainerCmdResolver, + NsColonVarResolver, + (Tcl_ResolveCompiledVarProc *)NULL); + } else { + /* turn off SlotContainerCmdResolver */ + Tcl_SetNamespaceResolvers(containerObject->nsPtr, + (Tcl_ResolveCmdProc *)NULL, + NsColonVarResolver, + (Tcl_ResolveCompiledVarProc *)NULL); + } } Tcl_SetIntObj(Tcl_GetObjResult(interp), (containerObject->flags & flag) != 0); break; @@ -19859,7 +19931,8 @@ if (valueObj) { if (likely(allowSet)) { - int result = SetBooleanFlag(interp, &object->flags, flags, valueObj); + int flagValue; + int result = SetBooleanFlag(interp, &object->flags, flags, valueObj, &flagValue); if (result != TCL_OK) { return result; } Index: tests/parameters.test =================================================================== diff -u -r5ce68a42506fcc981cea2431afa1b09b476e667a -r770232a210b63fdafc5e5e4a2caf45fa5097c6fe --- tests/parameters.test (.../parameters.test) (revision 5ce68a42506fcc981cea2431afa1b09b476e667a) +++ tests/parameters.test (.../parameters.test) (revision 770232a210b63fdafc5e5e4a2caf45fa5097c6fe) @@ -2460,4 +2460,27 @@ } +# +# Test interaction of name of property with the Tcl command behavior. +# Without the SlotContainerCmdResolver() the call to "list" in a +# property named "list" leads to a call to the container object +# ::Test2::slot::list instead of the intended ::list. +# +nx::Test case slot-container-name-interaction { + nx::Class create Test2 { + :property list { + :public method assign { obj var val } { + nsf::var::set $obj $var [list $obj $var $val] + } + :method unknown { val obj var args } { + return unknown + } + } + } + + ? {Test2 create t2} ::t2 + ? {t2 list 3} {::t2 list 3} + ? {t2 list} {::t2 list 3} + ? {t2 list this should call unknown} "unknown" +} \ No newline at end of file