Index: generic/nsf.c =================================================================== diff -u -re603015e6a5c3e138080ccea8b7c4c9c8e868bef -ra5dfcb547e25f83286793ba9850b988b822adf3e --- generic/nsf.c (.../nsf.c) (revision e603015e6a5c3e138080ccea8b7c4c9c8e868bef) +++ generic/nsf.c (.../nsf.c) (revision a5dfcb547e25f83286793ba9850b988b822adf3e) @@ -10364,44 +10364,44 @@ DECR_REF_COUNT(methodObj); if (likely(cmd != NULL)) { - if (regObject) { - if (NsfObjectIsClass(regObject)) { - cl = (NsfClass *)regObject; + if (CmdIsNsfObject(cmd)) { + /* + * Don't allow for calling objects as methods via fully qualified + * names. Otherwise, in line [2] below, ::State (or any children of + * it, e.g., ::Slot::child) is interpreted as a method candidate. As a + * result, dispatch chaining occurs with ::State or ::State::child + * being the receiver (instead of Class) of the method call + * "-parameter". In such a dispatch chaining, the method "unknown" + * won't be called on Class (in the XOTcl tradition), effectively + * bypassing any unknown-based indirection mechanism (e.g., XOTcl's short-cutting + * of object/class creations). + * + * [1] Class ::State; Class ::State::child + * [2] Class ::State -parameter x; Class ::State::child -parameter x + */ + NsfLog(interp, NSF_LOG_NOTICE, + "Don't invoke object %s this way. Register object via alias ...", + methodName); + cmd = NULL; + } else { + if (regObject) { + if (NsfObjectIsClass(regObject)) { + cl = (NsfClass *)regObject; + } } + /* fprintf(stderr, "fully qualified lookup of %s returned %p\n", ObjStr(methodObj), cmd); */ /* ignore permissions for fully qualified method names */ flags |= NSF_CM_IGNORE_PERMISSIONS; - } else { - /*fprintf(stderr, "fully qualified lookup of %s returned %p\n", ObjStr(methodObj), cmd);*/ - if (CmdIsNsfObject(cmd)) { - /* - * Don't allow to call objects as methods (for the time being) - * via fully qualified names. Otherwise, in line [2] below, ::State - * is interpreted as an ensemble object, and the method - * "unknown" won't be called (in the XOTcl tradition) and - * weird things will happen. - * - * [1] Class ::State - * [2] Class ::State -parameter x - */ - NsfLog(interp, NSF_LOG_NOTICE, - "Don't invoke object %s this way. Register object via alias...", - methodName); - cmd = NULL; - } else { - /* ignore permissions for fully qualified method names */ - flags |= NSF_CM_IGNORE_PERMISSIONS; - } } + /*fprintf(stderr, "ObjectDispatch fully qualified obj %s methodName %s => cl %p cmd %p \n", + object ? ObjectName(object) : NULL, + methodName, cl, cmd);*/ } - - /*fprintf(stderr, "ObjectDispatch fully qualified obj %s methodName %s => cl %p cmd %p \n", - object ? ObjectName(object) : NULL, - methodName, cl, cmd);*/ } - + /*fprintf(stderr, "MixinStackPush check for %p %s.%s objflags %.6x == %d\n", - object, ObjectName(object), methodName, objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID, - (objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) == NSF_MIXIN_ORDER_DEFINED_AND_VALID);*/ + object, ObjectName(object), methodName, objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID, + (objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) == NSF_MIXIN_ORDER_DEFINED_AND_VALID);*/ /* * Check if a mixed in method has to be called. */ Index: tests/methods.test =================================================================== diff -u -r5ce68a42506fcc981cea2431afa1b09b476e667a -ra5dfcb547e25f83286793ba9850b988b822adf3e --- tests/methods.test (.../methods.test) (revision 5ce68a42506fcc981cea2431afa1b09b476e667a) +++ tests/methods.test (.../methods.test) (revision a5dfcb547e25f83286793ba9850b988b822adf3e) @@ -714,4 +714,118 @@ ? {o bar07} ::o "self foo" ? {o bar08} foo ": -system info" #? {o bar09} foo "my -system info" +} + +nx::Test parameter count 1 +nx::Test case fq-obj-dispatch { + # + # Capture the (current) dispatcher rules for fully-qualified + # selectors which resolve to existing objects. + # + nx::Class create C { + set :unknown 0 + :public class method unknown {m args} { + incr :unknown + return unknown-$m + } + } + + nx::Class create D { + set :defaultcalled 0 + :public method defaultmethod args { + [current class] eval [list incr :defaultcalled] + } + :create ::d + } + + ? {::D eval {set :defaultcalled}} 0 + ? {::d} 1 + ? {C eval {set :unknown}} 0 + ? {C ::d} "unknown-::d" + ? {C eval {set :unknown}} 1 + ? {::d} 2; # should not be 3! + ? {C d} "unknown-d" + ? {C eval {set :unknown}} 2 + ? {::d} 3 + + # + # nested-object selector, *not* pre-existing + # + ? {::nsf::object::exists ::d::c} 0 + ? {C ::d::c} "unknown-::d::c" + ? {C eval {set :unknown}} 3 + ? {::nsf::object::exists ::d::c} 0 + + # + # nested-object selector, pre-existing + # + ? {::nsf::object::exists ::d::dd} 0 + D create ::d::dd + ? {::nsf::object::exists ::d::dd} 1 + ? {::D eval {set :defaultcalled}} 3 + ? {::d::dd} 4 + ? {C eval {set :unknown}} 3 + ? {C ::d::dd} "unknown-::d::dd" + ? {C eval {set :unknown}} 4 + ? {C d::dd} "unknown-d::dd" + ? {C eval {set :unknown}} 5 + ? {::D eval {set :defaultcalled}} 4 + + # + # namespaced selector, *not* pre-existing + # + namespace eval ::ns1 {} + ? {::nsf::object::exists ::ns1::c} 0 + ? {C ::ns1::c} "unknown-::ns1::c" + ? {C eval {set :unknown}} 6 + ? {::nsf::object::exists ::ns1::c} 0 + + # + # namespaced selector, pre-existing + # + ? {::nsf::object::exists ::ns1::d} 0 + D create ::ns1::d + ? {::nsf::object::exists ::ns1::d} 1 + ? {::D eval {set :defaultcalled}} 4 + ? {::ns1::d} 5 + ? {C eval {set :unknown}} 6 + ? {C ::ns1::d} "unknown-::ns1::d" + ? {C eval {set :unknown}} 7 + ? {C ns1::d} "unknown-ns1::d" + ? {C eval {set :unknown}} 8 + ? {::D eval {set :defaultcalled}} 5 + + # + # Is XOTcl's creation short-cut operative for nested-object + # selectors, compliant with the XOTcl-specific unknown-(re)create + # protocol? + # + package req XOTcl 2.0 + + ? {::nsf::object::exists ::X} 0 + xotcl::Class ::X -instproc p1 {v} { + [self class] incr [self proc] $v + } -proc unknown args { + my incr [self proc] + next + } -set unknown 0 -proc recreate args { + my incr [self proc] + next + } -set recreate 0 + + ? {::nsf::object::exists ::X} 1 + ? {::X exists p1} 0 + ? {::X set unknown} 0 + + xotcl::Object ::p + ? {::nsf::object::exists ::p::child} 0 + ::X ::p::child -p1 2 + ? {::nsf::object::exists ::p::child} 1 + ? {::X set p1} 2 + ? {::X set unknown} 1 + ? {::X set recreate} 0 + ::X ::p::child -p1 1 + ? {::X set p1} 3 + ? {::X set unknown} 2 + ? {::X set recreate} 1 } \ No newline at end of file