Index: TODO =================================================================== diff -u -r4a1e82be06533d5cad2e3a20eaa7edd7cf74a287 -re130899462517e1170d6604b875871ce2476a3bc --- TODO (.../TODO) (revision 4a1e82be06533d5cad2e3a20eaa7edd7cf74a287) +++ TODO (.../TODO) (revision e130899462517e1170d6604b875871ce2476a3bc) @@ -1365,11 +1365,14 @@ - documented behavior of upvar/uplevel with aliases on scripted procs through regression test +- implemented next within ensemble methods +- added regression tests for next within ensembles +- added regression tests for upvar with ensembles + TODO: - subcmd * handle sucmd for other method factories * handle absence of -create flag in resolve_method_path (for introspection) - * next handling - next noargs Index: generic/nsf.c =================================================================== diff -u -ra1b22b426cd57b108235ac5b1879fc780eab25d1 -re130899462517e1170d6604b875871ce2476a3bc --- generic/nsf.c (.../nsf.c) (revision a1b22b426cd57b108235ac5b1879fc780eab25d1) +++ generic/nsf.c (.../nsf.c) (revision e130899462517e1170d6604b875871ce2476a3bc) @@ -6404,6 +6404,12 @@ /* fprintf(stderr, "... method %p %s csc %p\n", cmd, methodName, cscPtr); */ if (cmd) { Tcl_CallFrame frame, *framePtr = &frame; + /* + * In order to allow next to be called on the + * ensemble-method, a call-frame entry is needed. The + * associated calltype is flagged as an ensemble to be + * able to distinguish frames during next. + */ CscInit(cscPtr, object, cl, cmd, frameType); cscPtr->objc = objc; cscPtr->objv = (Tcl_Obj **)objv; @@ -7933,9 +7939,9 @@ int result, frameType = NSF_CSC_TYPE_PLAIN, isMixinEntry = 0, isFilterEntry = 0, endOfFilterChain = 0, decrObjv0 = 0; + CONST char **methodName = &givenMethodName; int nobjc; Tcl_Obj **nobjv; NsfClass **cl = &givenCl; - CONST char **methodName = &givenMethodName; Tcl_CallFrame *framePtr; if (!cscPtr) { @@ -7947,36 +7953,39 @@ */ framePtr = NULL; assert(useCallstackObjs == 0); - /* fprintf(stderr, "NsfNextMethod csc given, use %d, framePtr %p\n", useCallstackObjs, framePtr); */ + /* fprintf(stderr, "NsfNextMethod csc given, use %d, framePtr %p\n", + useCallstackObjs, framePtr); */ } - -#if 1 - /*fprintf(stderr, "NsfNextMethod givenMethod = %s, csc = %p, useCallstackObj %d, objc %d cfp %p\n", - givenMethodName, cscPtr, useCallstackObjs, objc, framePtr); - fprintf(stderr, ".... cmd %p is Object %d csc %p flags %.6x frametype %.6x\n", - cscPtr->cmdPtr, - Tcl_Command_objProc(cscPtr->cmdPtr) == NsfObjDispatch, - cscPtr, - cscPtr->callType,cscPtr->frameType);*/ /* zzzz */ + /* zzzz */ if ((cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE)) { /*tcl85showStack(interp);*/ - /* - fprintf(stderr, ".... objc %d useCallstackObjs %d framePtr %p csc->ov[0] %s\n", - objc, useCallstackObjs, framePtr, - cscPtr->objv ? ObjStr(cscPtr->objv[0]) : NULL); - */ Tcl_CallFrame *framePtr2 = Tcl_CallFrame_callerPtr(framePtr); + assert(framePtr2); + /* + * Search back on the stack for the invocation of this ensemble + * object. The invocations might be nested. All + * ensemble-invocations are CMETHODs, their associated cscPtr has + * a callType of CALL_IS_ENSEMBLE. + */ for (;Tcl_CallFrame_isProcCallFrame(framePtr2) & FRAME_IS_NSF_CMETHOD; framePtr2 = Tcl_CallFrame_callerPtr(framePtr2)) { NsfCallStackContent *cscPtr2 = (NsfCallStackContent *)Tcl_CallFrame_clientData(framePtr2); - /*fprintf(stderr, ".... parent framePtr %p csc->ov[0] %s\n", - framePtr2, cscPtr2->objv ? ObjStr(cscPtr2->objv[0]) : NULL);*/ + assert(cscPtr2); + /*fprintf(stderr, ".... parent framePtr %p frameType %.6x callType %.6x csc->ov[0] %s\n", + framePtr2, cscPtr2->frameType, cscPtr2->callType, + cscPtr2->objv ? ObjStr(cscPtr2->objv[0]) : NULL);*/ + /* + * The test for CALL_IS_ENSEMBLE is just a saftey belt + */ + if ((cscPtr2->callType & NSF_CSC_CALL_IS_ENSEMBLE) == 0) break; + /* + * Remember the method name and the cscPtr + */ *methodName = ObjStr(cscPtr2->objv[0]); cscPtr = cscPtr2; } } -#endif /* if no args are given => use args from stack */ if (objc < 2 && useCallstackObjs && framePtr) { @@ -7991,11 +8000,13 @@ } else { nobjc = objc; nobjv = (Tcl_Obj **)objv; - /* We do not want to have "next" as the procname, since this can - lead to unwanted results e.g. in a forwarder using %proc. So, we - replace the first word with the value from the callstack to be - compatible with the case where next is called without args. - */ + /* + * We do not want to have "next" as the method name, since this + * can lead to unwanted results e.g. in a forwarder using + * %proc. So, we replace the first word with the value from the + * callstack to be compatible with the case where next is called + * without args. + */ if (useCallstackObjs && framePtr) { nobjv[0] = Tcl_CallFrame_objv(framePtr)[0]; INCR_REF_COUNT(nobjv[0]); /* we seem to need this here */ Index: tests/destroytest.tcl =================================================================== diff -u -rd9b42d77f43db84a9983cc3bbc4124cf0b52df29 -re130899462517e1170d6604b875871ce2476a3bc --- tests/destroytest.tcl (.../destroytest.tcl) (revision d9b42d77f43db84a9983cc3bbc4124cf0b52df29) +++ tests/destroytest.tcl (.../destroytest.tcl) (revision e130899462517e1170d6604b875871ce2476a3bc) @@ -435,20 +435,20 @@ Test case deleting-aliased-object { Object create o Object create o2 - ::nsf::alias o x o2 - ? {o x} ::o2 "call object via alias" - ? {o info method type x} alias - ## the forwarded object needs a per-object methods - o2 method info args next - o2 method set args next - ? {o x info vars} "" "call info on aliased object" + ::nsf::alias o a o2 + ? {o a} ::o2 "call object via alias" + ? {o info method type a} alias + ## the ensemble-object needs per-object methods + o2 method info args {:info {*}$args} + o2 method set args {:set {*}$args} + ? {o a info vars} "" "call info on aliased object" ? {o set x 10} 10 "set variable on object" ? {o info vars} x "query vars" - ? {o x info vars} x "query vars via alias" - ? {o x set x} 10 "set var via alias" + ? {o a info vars} x "query vars via alias" + ? {o a set x} 10 "set var via alias" o2 destroy - ? {o x info vars} "Trying to dispatch deleted object via method 'x'" "1st call on deleted object" - ? {o x info vars} "::o: unable to dispatch method 'x'" "2nd call on deleted object" + ? {o a info vars} "Trying to dispatch deleted object via method 'a'" "1st call on deleted object" + ? {o a info vars} "::o: unable to dispatch method 'a'" "2nd call on deleted object" } set case "deleting object with alias to object" @@ -479,7 +479,7 @@ Object create o3 o alias x o3 Object create o3 - o3 method set args next + o3 method set args {:set {*}$args} o set a 13 ? {o x set a} 13 "aliased object works after recreate" } @@ -494,8 +494,10 @@ Object create o3 o alias a o3 C alias b o - o3 method set args next - o method set args next + + o3 method set args {:set {*}$args} + o method set args {:set {*}$args} + C create c1 ? {c1 b set B 2} 2 "call 1st level" ? {c1 b a set A 3} 3 "call 2nd level" Index: tests/parameters.tcl =================================================================== diff -u -r09db17c50360e54441efc1d9db918e215dcec9b6 -re130899462517e1170d6604b875871ce2476a3bc --- tests/parameters.tcl (.../parameters.tcl) (revision 09db17c50360e54441efc1d9db918e215dcec9b6) +++ tests/parameters.tcl (.../parameters.tcl) (revision e130899462517e1170d6604b875871ce2476a3bc) @@ -15,7 +15,7 @@ ####################################################### # parametercheck ####################################################### -Test parameter count 10000 +Test parameter count 1000 Test case parametercheck { Object create o1 @@ -1116,7 +1116,7 @@ ? {c1 c 102} {::c1: unable to dispatch method 'c'} } -Test parameter count 10000 +Test parameter count 1000 Test case check-arguments { Class create Foo { @@ -1209,6 +1209,9 @@ ? {o f13} 1 } +# +# testing namespace resolution in type checkers +# namespace eval foo { nx::Class create C { :create c1 @@ -1250,22 +1253,48 @@ ? {c1 f32} 1 } +# +# testing ensemble objects with next +# +Test parameter count 1 Test case ensemble-next { nx::Class create FOO { + # reduced ensemble :method foo args {lappend :v "FOO.foo//[nx::current method] ([nx::current args])"} - :method "a" {args} {puts FOO-[nx::current method]\n} - :method "b" {x} {puts FOO-[nx::current method]\n} - :method "x" {x} {puts FOO-[nx::current method]\n} - :method "y" {x} {puts FOO-[nx::current method]\n} + + # expanded ensemble + :method "l1 l2 l3a" {x} { + lappend :v "FOO.l1 l2 l3a//[nx::current method] ([nx::current args])" + } + :method "l1 l2 l3b" {x} { + lappend :v "FOO.l1 l2 l3b//[nx::current method] ([nx::current args])" + } + # uplevel + :method "bar x" {varname} {upvar $varname v; return [info exists v]} + :method "baz" {} { + set hugo 1 + return [:bar x hugo] + } } nx::Class create M0 { - :method "a" {args} {puts M0-[nx::current method];nx::next} - :method "x" {x} {puts M0-[nx::current method];nx::next} - :method "b y" {x} {puts M0-[nx::current method];nx::next} - :method "foo b x" {x} {lappend :v "M0.foo b x//[nx::current method] ([nx::current args])";nx::next} - :method "foo b y" {x} {lappend :v "M0.foo b y//[nx::current method] ([nx::current args])";nx::next} - :method "foo a" {x} {lappend :v "M0.foo a//[nx::current method] ([nx::current args])";nx::next} + :method "foo b x" {x} { + lappend :v "M0.foo b x//[nx::current method] ([nx::current args])" + nx::next + } + :method "foo b y" {x} { + lappend :v "M0.foo b y//[nx::current method] ([nx::current args])" + nx::next + } + :method "foo a" {x} { + lappend :v "M0.foo a//[nx::current method] ([nx::current args])" + nx::next + } + + :method "l1 l2" {args} { + lappend :v "l1 l2//[nx::current method] ([nx::current args])" + nx::next + } } nx::Class create M1 { @@ -1281,17 +1310,52 @@ set :v [list "M1.foo b y //[nx::current method] ([nx::current args])"] nx::next } + + :method "l1 l2 l3a" {x} { + set :v [list "M1.l1 l2 l3a//[nx::current method] ([nx::current args])"] + nx::next + } + :method "l1 l2 l3b" {x} { + set :v [list "M1.l1 l2 l3b//[nx::current method] ([nx::current args])"] + nx::next + } } FOO mixin {M1 M0} FOO create f1 - #f1 foo - puts stderr ==== + # + # The last list element shows handling of less deep ensembles + # (longer arg list is passed) + # ? {f1 foo a 1} "{M1.foo a //a (1)} {M0.foo a//a (1)} {FOO.foo//foo (a 1)}" - puts stderr ==== ? {f1 foo b x 1} "{M1.foo b x //x (1)} {M0.foo b x//x (1)} {FOO.foo//foo (b x 1)}" - puts stderr ==== ? {f1 foo b y 1} "{M1.foo b y //y (1)} {M0.foo b y//y (1)} {FOO.foo//foo (b y 1)}" - puts stderr ==== + # + # The middle list element shows shrinking (less deep ensembles), the + # last element shows expansion via mixin (deeper ensemble is reached + # via next) + # + ? {f1 l1 l2 l3a 100} "{M1.l1 l2 l3a//l3a (100)} {l1 l2//l2 (l3a 100)} {FOO.l1 l2 l3a//l3a (100)}" } + + +Test case ensemble-upvar { + + nx::Class create FOO { + :method "bar0 x" {varname} {upvar $varname v; return [info exists v]} + :method "baz0" {} { + set hugo 1 + return [:bar0 x hugo] + } + :method "bar1 x" {varname} {:upvar $varname v; return [info exists v]} + :method "baz1" {} { + set hugo 1 + return [:bar1 x hugo] + } + :create f1 + } + + ? {f1 baz0} 0 + ? {f1 baz1} 1 +}