Index: TODO =================================================================== diff -u -r8e08d180ae5161c0ef4410ba3fda6b4de613a391 -r441c083eb3b4f9084f03aace7d990a1d40521c88 --- TODO (.../TODO) (revision 8e08d180ae5161c0ef4410ba3fda6b4de613a391) +++ TODO (.../TODO) (revision 441c083eb3b4f9084f03aace7d990a1d40521c88) @@ -1393,12 +1393,15 @@ elsewhere) - renamed __qualify to qualify (it is a non-exported cmd) +- handle next in ensemble with arguments +- extended regression test + TODO: - subcmd * handle sucmd for other method factories * handle absence of -create flag in resolve_method_path (for introspection) -- next noargs +- collaps stack frames? - method protection: defined methods (optionally?) per default protected to Index: generic/nsf.c =================================================================== diff -u -r8e08d180ae5161c0ef4410ba3fda6b4de613a391 -r441c083eb3b4f9084f03aace7d990a1d40521c88 --- generic/nsf.c (.../nsf.c) (revision 8e08d180ae5161c0ef4410ba3fda6b4de613a391) +++ generic/nsf.c (.../nsf.c) (revision 441c083eb3b4f9084f03aace7d990a1d40521c88) @@ -247,9 +247,6 @@ static int NextSearchAndInvoke(Tcl_Interp *interp, CONST char *methodName, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent *cscPtr); -static int NextGetArguments(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], - NsfCallStackContent **cscPtrPtr, CONST char **methodNamePtr, - int *outObjc, Tcl_Obj ***outObjv, int *decrObjv0); /* * argv parsing */ @@ -7963,24 +7960,22 @@ static int NextGetArguments(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent **cscPtrPtr, CONST char **methodNamePtr, - int *outObjc, Tcl_Obj ***outObjv, int *decrObjv0) { - int nobjc; + int *outObjc, Tcl_Obj ***outObjv, int *freeArgumentVector) { Tcl_Obj **nobjv; + int nobjc, oc, inEnsemble; Tcl_CallFrame *framePtr; NsfCallStackContent *cscPtr = CallStackGetTopFrame(interp, &framePtr); /* always make sure, we only decrement when necessary */ - *decrObjv0 = 0; + *freeArgumentVector = 0; if (!cscPtr) return NsfVarErrMsg(interp, "next: can't find self", (char *) NULL); if (!cscPtr->cmdPtr) return NsfErrMsg(interp, "next: no executing proc", TCL_STATIC); - /*fprintf(stderr, "NEXT %s.%s (%d) type_ensemble %.6x\n", - objectName(object), givenMethodName, objc, - (cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE));*/ + oc = Tcl_CallFrame_objc(framePtr); if ((cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE)) { /* @@ -7991,33 +7986,49 @@ */ cscPtr = CallStackFindEnsembleCsc(framePtr, &framePtr); assert(cscPtr); - + inEnsemble = 1; *methodNamePtr = ObjStr(cscPtr->objv[0]); } else { + inEnsemble = 0; *methodNamePtr = Tcl_GetCommandName(interp, cscPtr->cmdPtr); } - - /*fprintf(stderr, "... next on %s.%s, objc %d useCallStackObjs %d framePtr %p\n", - objectName(object), - *methodNamePtr, objc, useCallstackObjs,framePtr);*/ if (objc > -1) { + int methodNameLength; /* - * We have arguments. We hve to construct an argument vector with - * the first argument as the method name. + * Arguments were provided. We have to construct an argument + * vector with the first argument(s) as the method name. In an + * ensemble, we have to insert the objs of the full ensemble name. */ - nobjc = objc+1; - nobjv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*)*(objc+1)); + if (inEnsemble) { + methodNameLength = 1 + cscPtr->objc - oc; + nobjc = objc + methodNameLength; + nobjv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *) * nobjc); + /* + * copy the ensemble name + */ + memcpy(nobjv, cscPtr->objv, sizeof(Tcl_Obj *) * methodNameLength); - memcpy(nobjv+1, objv, sizeof(Tcl_Obj *) * (objc)); - - if (cscPtr->objv) { - nobjv[0] = cscPtr->objv[0]; - } else if (Tcl_CallFrame_objv(framePtr)) { - nobjv[0] = Tcl_CallFrame_objv(framePtr)[0]; + } else { + methodNameLength = 1; + nobjc = objc + methodNameLength; + nobjv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *) * nobjc); + /* + * copy the method name + */ + if (cscPtr->objv) { + nobjv[0] = cscPtr->objv[0]; + } else if (Tcl_CallFrame_objv(framePtr)) { + nobjv[0] = Tcl_CallFrame_objv(framePtr)[0]; + } } + /* + * copy the remaining argument vector + */ + memcpy(nobjv + methodNameLength, objv, sizeof(Tcl_Obj *) * objc); + INCR_REF_COUNT(nobjv[0]); /* we seem to need this here */ - *decrObjv0 = 1; + *freeArgumentVector = 1; } else { /* * no arguments were provided @@ -8180,7 +8191,7 @@ */ static int NsfNextCmd(Tcl_Interp *interp, Tcl_Obj *arguments) { - int result, decrObjv0 = 0, oc, nobjc; + int result, freeArgumentVector, oc, nobjc; NsfCallStackContent *cscPtr; CONST char *methodName; Tcl_Obj **nobjv, **ov; @@ -8194,12 +8205,13 @@ oc = -1; } - result = NextGetArguments(interp, oc, ov, &cscPtr, &methodName, &nobjc, &nobjv, &decrObjv0); + result = NextGetArguments(interp, oc, ov, &cscPtr, &methodName, + &nobjc, &nobjv, &freeArgumentVector); if (result == TCL_OK) { result = NextSearchAndInvoke(interp, methodName, nobjc, nobjv, cscPtr); } - if (decrObjv0) { + if (freeArgumentVector) { INCR_REF_COUNT(nobjv[0]); ckfree((char *)nobjv); } @@ -8238,7 +8250,7 @@ */ int NsfNextObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int result, decrObjv0, nobjc; + int result, freeArgumentVector, nobjc; NsfCallStackContent *cscPtr; CONST char *methodName; Tcl_Obj **nobjv; @@ -8254,12 +8266,13 @@ } } - result = NextGetArguments(interp, objc-1, &objv[1], &cscPtr, &methodName, &nobjc, &nobjv, &decrObjv0); + result = NextGetArguments(interp, objc-1, &objv[1], &cscPtr, &methodName, + &nobjc, &nobjv, &freeArgumentVector); if (result == TCL_OK) { result = NextSearchAndInvoke(interp, methodName, nobjc, nobjv, cscPtr); } - if (decrObjv0) { + if (freeArgumentVector) { INCR_REF_COUNT(nobjv[0]); ckfree((char *)nobjv); } Index: tests/submethods.tcl =================================================================== diff -u -r0c7e47e58354106c6810812efe41008ce5ae939b -r441c083eb3b4f9084f03aace7d990a1d40521c88 --- tests/submethods.tcl (.../submethods.tcl) (revision 0c7e47e58354106c6810812efe41008ce5ae939b) +++ tests/submethods.tcl (.../submethods.tcl) (revision 441c083eb3b4f9084f03aace7d990a1d40521c88) @@ -186,15 +186,28 @@ } nx::Object mixin add M nx::Object create o1 + + # call a submethod defined by a mixin, which does a next ? {o1 info has namespace} sometimes + + # call a submethod, which is not defined by the mixin ? {o1 info has type Object} 1 ? {o1 info has type M} 0 + + # 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} + # call a submethod, which is only defined in the mixin ? {o1 info has something else} something + + # call a submethod, which is only defined in the mixin, and which + # does a next (which should not complain) ? {o1 info has something better} better } +# +# Check behavior of upvars in ensemble methods +# Test case ensemble-upvar { nx::Class create FOO { @@ -214,3 +227,39 @@ ? {f1 baz0} 0 ? {f1 baz1} 1 } + +# +# Check behavior of next with arguments within an ensemble +# +Test case ensemble-next-with-args { + nx::Object create o { + :method foo {x} {return $x} + :method "e1 sm" {x} {return $x} + :method "e2 sm1 sm2" {x} {return $x} + :method "e2 e2 e2" {x} {return $x} + :method "e1 e1 e1" args {return $args} + } + nx::Class create M { + :method foo {} {next 1} + :method "e1 sm" {} {next 2} + :method "e2 sm1 sm2" {} {next 3} + :method "e2 e2 e2" {} {next 4} + :method "e1 e1 e1" args {next {e1 e1 e1}} + } + o mixin add M + + # case without ensemble + ? {o foo} 1 + + # ensemble depth 1, 1 arg + ? {o e1 sm} 2 + + # ensemble depth 2, 1 arg + ? {o e2 sm1 sm2} 3 + + # ensemble depth 2, 1 arg, same tcl-objs + ? {o e2 e2 e2} 4 + + # ensemble depth 2, multiple args, same tcl-objs + ? {o e1 e1 e1} {e1 e1 e1} +}