Index: TODO =================================================================== diff -u -rb69a9b8de677b30774419057953a91c96df00e56 -r448c0563adb3705d6686bdb37dc316f37b325474 --- TODO (.../TODO) (revision b69a9b8de677b30774419057953a91c96df00e56) +++ TODO (.../TODO) (revision 448c0563adb3705d6686bdb37dc316f37b325474) @@ -1621,6 +1621,10 @@ - added support for ensemble methods in "info lookup method" - extended regression test +- provide full set of ensemble methods from EnsembleObject.unknown + (i.e. from all classes along the precedence order. It was necessary + to pass calling object as first argument) + TODO: - handing of xo::at_cleanup in serializer @@ -1851,3 +1855,6 @@ * it could be possible to reduce stack frames in ensembles. Just a top ensemble frame could be sufficient. + + * the full names for ensemble methods could be stored in the cmd tables + to make lookup faster. Index: generic/nsf.c =================================================================== diff -u -rb69a9b8de677b30774419057953a91c96df00e56 -r448c0563adb3705d6686bdb37dc316f37b325474 --- generic/nsf.c (.../nsf.c) (revision b69a9b8de677b30774419057953a91c96df00e56) +++ generic/nsf.c (.../nsf.c) (revision 448c0563adb3705d6686bdb37dc316f37b325474) @@ -187,7 +187,7 @@ int objc, Tcl_Obj *CONST objv[], int flags); static int DispatchDestroyMethod(Tcl_Interp *interp, NsfObject *object, int flags); static int DispatchUnknownMethod(ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[], + int objc, Tcl_Obj *CONST objv[], NsfObject *delegator, Tcl_Obj *methodObj, int flags); NSF_INLINE static int ObjectDispatch(ClientData clientData, Tcl_Interp *interp, int objc, @@ -2334,7 +2334,8 @@ *---------------------------------------------------------------------- */ static int -InterpColonCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *nsPtr, int flags, Tcl_Command *cmdPtr) { +InterpColonCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *nsPtr, + int flags, Tcl_Command *cmdPtr) { CallFrame *varFramePtr; int frameFlags; @@ -6471,8 +6472,8 @@ * The method to be called was not part of this ensemble. Call * next to try to call such methods along the next path. */ - fprintf(stderr, "call next instead of unknown %s.%s \n", - objectName(cscPtr->self),methodName); + /*fprintf(stderr, "call next instead of unknown %s.%s \n", + objectName(cscPtr->self),methodName);*/ { Tcl_CallFrame *framePtr1; NsfCallStackContent *cscPtr1 = CallStackGetTopFrame(interp, &framePtr1); @@ -6492,10 +6493,18 @@ cscPtr1->objc, cscPtr1->objv, cscPtr1, 0); } - fprintf(stderr, "==> next %s.%s (obj %s) csc %p returned %d unknown %d\n", - objectName(self),methodName, objectName(object), cscPtr, result, rst->unknown); + /*fprintf(stderr, "==> next %s.%s (obj %s) csc %p returned %d unknown %d\n", + objectName(self),methodName, objectName(object), cscPtr, result, rst->unknown); */ if (rst->unknown) { - result = DispatchUnknownMethod(self, interp, objc-1, objv+1, + /* + * The appropriate unknown class is registered on the + * EnsembleObject class, from where it is currently not + * possible to determine the true calling object. Therefore, + * we pass the object as first argument of the unknown + * handler. + */ + + result = DispatchUnknownMethod(self, interp, objc, objv, object, objv[1], NSF_CM_NO_OBJECT_METHOD|NSF_CSC_IMMEDIATE); } obj_dispatch_ok: @@ -6657,7 +6666,7 @@ || ((cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) && rst->unknown) ) { result = DispatchUnknownMethod(object, interp, - cscPtr->objc, cscPtr->objv, cscPtr->objv[0], + cscPtr->objc, cscPtr->objv, NULL, cscPtr->objv[0], NSF_CSC_IMMEDIATE /*flags&NSF_CSC_IMMEDIATE*/); /* @@ -7071,32 +7080,42 @@ static int DispatchUnknownMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], - Tcl_Obj *methodObj, int flags) { - int result; + NsfObject *delegator, Tcl_Obj *methodObj, int flags) { + int result, offset; NsfObject *object = (NsfObject*)clientData; Tcl_Obj *unknownObj = NsfMethodObj(interp, object, NSF_o_unknown_idx); + /*fprintf(stderr, "compare unknownObj %p with methodObj %p '%s' %p %p %s\n", + unknownObj, methodObj, ObjStr(methodObj), delegator, + delegator?objv[1]:NULL, + delegator?ObjStr(objv[1]) : NULL );*/ + if (unknownObj && methodObj != unknownObj && (flags & NSF_CM_NO_UNKNOWN) == 0) { /* * back off and try unknown; */ - ALLOC_ON_STACK(Tcl_Obj*, objc+2, tov); + ALLOC_ON_STACK(Tcl_Obj*, objc+3, tov); /*fprintf(stderr, "calling unknown for %s %s, flgs=%02x,%02x isClass=%d %p %s objc %d\n", objectName(object), ObjStr(methodObj), flags, NSF_CM_NO_UNKNOWN, NsfObjectIsClass(object), object, objectName(object), objc);*/ tov[0] = object->cmdName; tov[1] = unknownObj; + offset = 2; + if (delegator) { + tov[2] = delegator->cmdName; + offset ++; + } if (objc>0) { - memcpy(tov+2, objv, sizeof(Tcl_Obj *)*(objc)); + memcpy(tov + offset, objv, sizeof(Tcl_Obj *)*(objc)); } flags &= ~NSF_CM_NO_SHIFT; - result = ObjectDispatch(clientData, interp, objc+2, tov, flags|NSF_CM_NO_UNKNOWN); + result = ObjectDispatch(clientData, interp, objc+offset, tov, flags|NSF_CM_NO_UNKNOWN); + FREE_ON_STACK(Tcl_Obj*, tov); - } else { /* no unknown called, this is the built-in unknown handler */ /*fprintf(stderr, "--- No unknown method Name %s objv[%d] %s\n", @@ -7105,6 +7124,7 @@ ": unable to dispatch method '", ObjStr(objv[1]), "'", (char *) NULL); } + return result; } @@ -8466,8 +8486,9 @@ Tcl_Obj **nobjv = data[0]; NsfCallStackContent *cscPtr = data[1]; - /* fprintf(stderr, "***** NextInvokeFinalize cscPtr %p flags %.6x is next %d\n", - cscPtr, cscPtr->flags, cscPtr->flags & NSF_CSC_CALL_IS_NEXT);*/ + /*fprintf(stderr, "***** NextInvokeFinalize cscPtr %p flags %.6x is next %d result %d unk %d\n", + cscPtr, cscPtr->flags, cscPtr->flags & NSF_CSC_CALL_IS_NEXT, result, + RUNTIME_STATE(interp)->unknown);*/ if (cscPtr->flags & NSF_CSC_CALL_IS_NEXT) { /* fprintf(stderr, "..... it was a successful next\n"); */ @@ -8491,6 +8512,7 @@ */ result = TCL_OK; } + return result; } @@ -8621,9 +8643,8 @@ /*fprintf(stderr, "--- no cmd, csc %p frameType %.6x callType %.6x endOfFilterChain %d\n", cscPtr, cscPtr->frameType, cscPtr->flags, endOfFilterChain);*/ - - rst->unknown = endOfFilterChain || (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE); - /*fprintf(stderr, "******** setting unknown to %d\n", rst->unknown );*/ + rst->unknown = endOfFilterChain || (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE); + /*fprintf(stderr, "******** setting unknown to %d\n", rst->unknown );*/ } next_search_and_invoke_cleanup: Index: library/nx/nx.tcl =================================================================== diff -u -rb69a9b8de677b30774419057953a91c96df00e56 -r448c0563adb3705d6686bdb37dc316f37b325474 --- library/nx/nx.tcl (.../nx.tcl) (revision b69a9b8de677b30774419057953a91c96df00e56) +++ library/nx/nx.tcl (.../nx.tcl) (revision 448c0563adb3705d6686bdb37dc316f37b325474) @@ -446,13 +446,17 @@ # # The methods "unknown" and "defaultmethod" are called internally # - :method unknown {m args} { - set self [::nsf::current object] - #puts stderr "UNKNOWN [self] $args" + :method unknown {obj m args} { + set self [::nsf::current object] + #puts stderr "+++ UNKNOWN $self obj $obj '$m' $args" array set "" [$self ::nsf::classes::nx::EnsembleObject::methodPath] - set subcmds [lsort [::nsf::dispatch $self ::nsf::methods::object::info::methods]] - error "unable to dispatch method $(regobj) $(path) $m;\ - valid subcommands of [namespace tail $self]: $subcmds" + + if {[catch {set valid [lsort [$obj ::nsf::methods::object::info::lookupmethods -expand "$(path) *"]]} errorMsg]} { + set valid "" + puts stderr "+++ UNKNOWN has error $errorMsg" + } + set ref "\"[lindex $args 0]\" of $obj $(path)" + error "Unable to dispatch sub-method $ref; valid are:\n[join $valid {, }]" } :method defaultmethod {} { Index: tests/submethods.tcl =================================================================== diff -u -rd1ed482555d4d28dbb41fb9ca2723eabb5e01221 -r448c0563adb3705d6686bdb37dc316f37b325474 --- tests/submethods.tcl (.../submethods.tcl) (revision d1ed482555d4d28dbb41fb9ca2723eabb5e01221) +++ tests/submethods.tcl (.../submethods.tcl) (revision 448c0563adb3705d6686bdb37dc316f37b325474) @@ -35,26 +35,34 @@ ? {o string length 1} length ? {o string tolower 2} tolower ? {o string toupper 2} \ - {unable to dispatch method ::o string toupper; valid subcommands of string: info length tolower} + {Unable to dispatch sub-method "toupper" of ::o string; valid are: +string info, string length, string tolower} ? {o foo a x} "x" ? {o foo a y} "y" - ? {o foo a z} {unable to dispatch method ::o foo a z; valid subcommands of a: defaultmethod subcmdName unknown x y} - + ? {o foo a z} \ + {Unable to dispatch sub-method "z" of ::o foo a; valid are: +foo a defaultmethod, foo a subcmdName, foo a unknown, foo a x, foo a y} + ? {o info method type string} object # the following is a problem, when string has subcmd "info" #? {o::string info class} ::nx::EnsembleObject ? {o string length aaa} "length" ? {o string info class} "info" ? {o string hugo} \ - {unable to dispatch method ::o string hugo; valid subcommands of string: info length tolower} + {Unable to dispatch sub-method "hugo" of ::o string; valid are: +string info, string length, string tolower} Foo create f1 ? {f1 baz a m1 10} m1 - ? {f1 baz a m3 10} {unable to dispatch method baz a m3; valid subcommands of a: m1 m2} -} + ? {f1 baz a m3 10} \ + {Unable to dispatch sub-method "m3" of ::f1 baz a; valid are: +baz a m1, baz a m2} +#unable to dispatch method baz a m3; valid subcommands of a: m1 m2} +# + Test parameter count 1 Test case defaultmethod { Object create o { @@ -174,14 +182,16 @@ Test case ensemble-partial-next { nx::Class create M { :method "info has namespace" {} { - next + nx::next return sometimes } :method "info has something else" {} { return something } :method "info has something better" {} { - next + puts stderr "... better calls NEXT" + nx::next + puts stderr "... better calls NEXT DONE" return better } } @@ -197,7 +207,9 @@ # call a submethod, which is nowhere defined ? {o1 info has typo M} \ - {unable to dispatch method info has typo; valid subcommands of has: namespace something} + {Unable to dispatch sub-method "typo" of ::o1 info has; valid are: +info has mixin, info has namespace, info has something better, info has something else, info has type} + # call a submethod, which is only defined in the mixin ? {o1 info has something else} something