Index: TODO =================================================================== diff -u -r3e18b80be2883ba647c2110a2e8e2b1980940c30 -r0f3ecd0524a309ace0729dbfeb5f299f8bf7a250 --- TODO (.../TODO) (revision 3e18b80be2883ba647c2110a2e8e2b1980940c30) +++ TODO (.../TODO) (revision 0f3ecd0524a309ace0729dbfeb5f299f8bf7a250) @@ -3378,14 +3378,21 @@ * added "nsf::object::dispatch /obj/ -intrinsic ..." similar to "/obj/ -intrinsic ..." * extended regression test +- nsf.c: + * simplified permission handling + * made private/protected mutual exclusive + * extended regression test for private methods + TODO: - fix interp.test for tcl 8.6 - fix xocomm.test for tcl 8.6 - private: - * make it mutual exclusive with protected. - * more tests + * maybe let private imply protected + * tests for private + mixins + * tests for object-specific private methods + * flag "-local" in dispatch is maybe confusing - nx: * maybe provide a replacement for -attributes, but without the magic variable. Index: generic/nsf.c =================================================================== diff -u -r3a3781c587806733d4e772464fb368475425814a -r0f3ecd0524a309ace0729dbfeb5f299f8bf7a250 --- generic/nsf.c (.../nsf.c) (revision 3a3781c587806733d4e772464fb368475425814a) +++ generic/nsf.c (.../nsf.c) (revision 0f3ecd0524a309ace0729dbfeb5f299f8bf7a250) @@ -9545,25 +9545,30 @@ if (NsfObjectIsClass(regObject)) { cl = (NsfClass *)regObject; } + /* ignore permissions for fully qualified method names */ + flags |= NSF_CM_IGNORE_PERMISSIONS; } else { Tcl_ObjCmdProc *procPtr = Tcl_Command_objProc(cmd); /*fprintf(stderr, "fully qualified lookup of %s returned %p\n", ObjStr(methodObj), cmd);*/ if (procPtr == NsfObjDispatch) { /* * Don't allow to call objects as methods (for the time being) - * via fully qualified names. Otherwise, in line {2} below, ::State + * 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 * wierd things will happen. * - * {1} Class ::State - * {2} Class ::State -parameter x + * [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; } } } @@ -9659,18 +9664,32 @@ } /* - * Check, whether we have a protected method, and whether the - * protected method, called on a different object. In this case, we - * treat it as unknown. + * If we have a command, check the permissions, unless + * NSF_CM_IGNORE_PERMISSIONS is set. Note, that NSF_CM_IGNORE_PERMISSIONS is + * set currently for fully qualified cmd names and in nsf::object::dispatch. */ - if (cmd) { + if (cmd && (flags & NSF_CM_IGNORE_PERMISSIONS) == 0) { int cmdFlags = Tcl_Command_flags(cmd); - if ((cmdFlags & NSF_CMD_CALL_PRIVATE_METHOD) && (flags & NSF_CM_LOCAL_METHOD) == 0) { - /* reset cmd, since it is still unknown */ + + /* + * Private methods can be called when "-local" was used. + * + * Protected methods can be called, when calling object == called object. + */ + + if ((cmdFlags & NSF_CMD_CALL_PRIVATE_METHOD) + && ((flags & NSF_CM_LOCAL_METHOD) == 0) + ) { + /* reset cmd, treat it as unknown */ + //NsfCallStackContent *cscPtr1 = CallStackGetTopFrame0(interp); + + NsfLog(interp, NSF_LOG_WARN, "'%s %s' fails since method %s.%s is private", + ObjectName(object), methodName, + cl ? ClassName(cl) : ObjectName(object), methodName); cmd = NULL; - } else if ((cmdFlags & NSF_CMD_CALL_PROTECTED_METHOD) && - (flags & (NSF_CM_NO_UNKNOWN|NSF_CM_NO_PROTECT)) == 0) { + + } else if ((cmdFlags & NSF_CMD_CALL_PROTECTED_METHOD)) { NsfObject *lastSelf = GetSelfObj(interp); if (unlikely(object != lastSelf)) { @@ -9784,7 +9803,8 @@ tov[0] = obj; tov[1] = methodObj; - result = ObjectDispatch(object, interp, 2, tov, flags|NSF_CM_NO_UNKNOWN); + result = ObjectDispatch(object, interp, 2, tov, + flags|NSF_CM_NO_UNKNOWN|NSF_CM_IGNORE_PERMISSIONS); } return result; @@ -9836,7 +9856,7 @@ if (CallDirectly(interp, object, NSF_o_destroy_idx, &methodObj)) { result = NsfODestroyMethod(interp, object); } else { - result = CallMethod(object, interp, methodObj, 2, 0, NSF_CM_NO_PROTECT|NSF_CSC_IMMEDIATE|flags); + result = CallMethod(object, interp, methodObj, 2, 0, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE|flags); } if (result != TCL_OK) { /* @@ -9904,7 +9924,7 @@ } else { /*fprintf(stderr, "%s init dispatch\n", ObjectName(object));*/ result = CallMethod(object, interp, methodObj, - objc+2, objv, flags|NSF_CM_NO_PROTECT|NSF_CSC_IMMEDIATE); + objc+2, objv, flags|NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); } } else { @@ -9972,7 +9992,8 @@ /*fprintf(stderr, "call unknown via dispatch mustCopy %d delegator %p method %s (%s)\n", mustCopy, delegator, ObjStr(tov[offset]), ObjStr(methodObj));*/ - result = ObjectDispatch(object, interp, objc+2, tov, flags|NSF_CM_NO_UNKNOWN); + result = ObjectDispatch(object, interp, objc+2, tov, + flags|NSF_CM_NO_UNKNOWN|NSF_CM_IGNORE_PERMISSIONS); DECR_REF_COUNT(callInfoObj); FREE_ON_STACK(Tcl_Obj*, tov); @@ -10577,7 +10598,7 @@ /* result = Tcl_EvalObjv(interp, oc, ov, 0); */ GetObjectFromObj(interp, ov[0], &object); - result = ObjectDispatch(object, interp, oc, ov, NSF_CSC_IMMEDIATE|NSF_CM_NO_PROTECT); + result = ObjectDispatch(object, interp, oc, ov, NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); DECR_REF_COUNT(ov[1]); DECR_REF_COUNT(ov[2]); @@ -15139,7 +15160,7 @@ Tcl_ResetResult(interp); INCR_REF_COUNT(methodObj); result = CallMethod(object, interp, methodObj, argc, argv, - NSF_CM_NO_UNKNOWN|NSF_CSC_IMMEDIATE); + NSF_CM_NO_UNKNOWN|NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); DECR_REF_COUNT(methodObj); /*fprintf(stderr, "method '%s' called args: %d o=%p, result=%d %d\n", @@ -17881,21 +17902,37 @@ case MethodpropertyCall_protectedIdx: /* fall through */ case MethodpropertyRedefine_protectedIdx: /* fall through */ { + int clearFlag = 0; + switch (methodproperty) { - case MethodpropertyClass_onlyIdx: flag = NSF_CMD_CLASS_ONLY_METHOD; break; - case MethodpropertyCall_privateIdx: flag = NSF_CMD_CALL_PRIVATE_METHOD; break; - case MethodpropertyCall_protectedIdx: flag = NSF_CMD_CALL_PROTECTED_METHOD; break; - case MethodpropertyRedefine_protectedIdx: flag = NSF_CMD_REDEFINE_PROTECTED_METHOD; break; + case MethodpropertyClass_onlyIdx: + flag = NSF_CMD_CLASS_ONLY_METHOD; + break; + case MethodpropertyCall_privateIdx: + clearFlag = NSF_CMD_CALL_PROTECTED_METHOD; + flag = NSF_CMD_CALL_PRIVATE_METHOD; + break; + case MethodpropertyCall_protectedIdx: + clearFlag = NSF_CMD_CALL_PRIVATE_METHOD; + flag = NSF_CMD_CALL_PROTECTED_METHOD; + break; + case MethodpropertyRedefine_protectedIdx: + flag = NSF_CMD_REDEFINE_PROTECTED_METHOD; + break; default: flag = 0; } if (valueObj) { int bool, result; + result = Tcl_GetBooleanFromObj(interp, valueObj, &bool); if (result != TCL_OK) { return result; } if (bool) { + if (clearFlag) { + Tcl_Command_flags(cmd) &= ~clearFlag; + } Tcl_Command_flags(cmd) |= flag; } else { Tcl_Command_flags(cmd) &= ~flag; @@ -18125,6 +18162,11 @@ * the command. */ + if (withIntrinsic || withSystem || withLocal) { + return NsfPrintError(interp, "cannot use flag '-intrisic', '-local', or '-system'" + " with fully qualified method name"); + } + cmd = Tcl_GetCommandFromObj(interp, command); /* fprintf(stderr, "colon name %s cmd %p\n", methodName, cmd);*/ @@ -18178,7 +18220,7 @@ Tcl_Obj *arg; Tcl_Obj *CONST *objv; - int flags = NSF_CM_NO_UNKNOWN|NSF_CSC_IMMEDIATE; + int flags = NSF_CM_NO_UNKNOWN|NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS; if (withFrame && withFrame != FrameDefaultIdx) { return NsfPrintError(interp, @@ -18197,6 +18239,7 @@ arg = NULL; objv = NULL; } + result = NsfCallMethodWithArgs(interp, (Nsf_Object *)object, command, arg, nobjc, objv, flags); } @@ -19588,7 +19631,7 @@ if (methodObj) { /*fprintf(stderr, "=== calling %s objectparameter\n", ClassName(class));*/ result = CallMethod(class, interp, methodObj, - 2, 0, NSF_CM_NO_PROTECT|NSF_CSC_IMMEDIATE); + 2, 0, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); if (likely(result == TCL_OK)) { rawConfArgs = Tcl_GetObjResult(interp); @@ -20755,7 +20798,7 @@ result = RecreateObject(interp, cl, newObject, objc, nobjv); } else { result = CallMethod(cl, interp, methodObj, - objc+1, nobjv+1, NSF_CM_NO_PROTECT|NSF_CSC_IMMEDIATE); + objc+1, nobjv+1, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); } if (result != TCL_OK) { goto create_method_exit; @@ -20998,7 +21041,7 @@ fprintf(stderr, "RECREATE calls method cleanup for object %p %s OS %s\n", object, ObjectName(object), ObjectName((&osPtr->rootClass->object)));*/ result = CallMethod(object, interp, methodObj, - 2, 0, NSF_CM_NO_PROTECT|NSF_CSC_IMMEDIATE); + 2, 0, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); } } Index: generic/nsfInt.h =================================================================== diff -u -rf67408d8e6f8ba9bdd6e4ec3c54dfa3a23576161 -r0f3ecd0524a309ace0729dbfeb5f299f8bf7a250 --- generic/nsfInt.h (.../nsfInt.h) (revision f67408d8e6f8ba9bdd6e4ec3c54dfa3a23576161) +++ generic/nsfInt.h (.../nsfInt.h) (revision 0f3ecd0524a309ace0729dbfeb5f299f8bf7a250) @@ -720,7 +720,7 @@ /* flags for call method */ #define NSF_CM_NO_UNKNOWN 0x000001 #define NSF_CM_NO_SHIFT 0x000002 -#define NSF_CM_NO_PROTECT 0x000004 +#define NSF_CM_IGNORE_PERMISSIONS 0x000004 #define NSF_CM_NO_OBJECT_METHOD 0x000008 #define NSF_CM_SYSTEM_METHOD 0x000010 #define NSF_CM_LOCAL_METHOD 0x000020 Index: nxsh.in =================================================================== diff -u -r043dc9b94b99894cc9b7ad6b61e469f67023c705 -r0f3ecd0524a309ace0729dbfeb5f299f8bf7a250 --- nxsh.in (.../nxsh.in) (revision 043dc9b94b99894cc9b7ad6b61e469f67023c705) +++ nxsh.in (.../nxsh.in) (revision 0f3ecd0524a309ace0729dbfeb5f299f8bf7a250) @@ -35,5 +35,7 @@ set argv0 [lindex $argv 0] set argv [lreplace $argv 0 0] incr argc -1 - source $argv0 + if {[catch [list source $argv0] errorMsg]} { + return -level 1 $errorMsg + } } Index: tests/protected.test =================================================================== diff -u -ra695cb72c6594bae8b7abb49b76c0ee7b5367b7f -r0f3ecd0524a309ace0729dbfeb5f299f8bf7a250 --- tests/protected.test (.../protected.test) (revision a695cb72c6594bae8b7abb49b76c0ee7b5367b7f) +++ tests/protected.test (.../protected.test) (revision 0f3ecd0524a309ace0729dbfeb5f299f8bf7a250) @@ -122,7 +122,86 @@ {Method 'foo' of ::o cannot be overwritten. Derive e.g. a sub-class!} } + # +# test private +# +nx::Test case private { + nx::Class create B { + :private method p1 {} {return B.p1} + :private method p2 {} {return B.p2} + :public method p3 {} {return B.p3} + :public method p4 {} {return B.p4} + :create b1 + } + nx::Class create C -superclass B { + :private method p1 {} {return "C.p1 [next]"} + :public method p2 {} {return "C.p2 [next]"} + :private method p3 {} {return "C.p3 [next]"} + :public method p4 {} {return "C.p4 [next]"} + :create c1 + } + nx::Class create D -superclass C { + :public method p1 {} {return "D.p1 [next]"} + :public method p2 {} {return "D.p2 [next]"} + :public method p3 {} {return "D.p3 [next]"} + :public method p4 {} {return "D.p4 [next]"} + :create d1 + } + + # called shadowed + # C.p1 private B.p1 private + # C.p2 public B.p2 private + # C.p3 private B.p3 public + # C.p4 public B.p4 public + + ? {c1 p1} "::c1: unable to dispatch method 'p1'" + ? {c1 p2} "C.p2 " + ? {c1 p3} "B.p3" + ? {c1 p4} "C.p4 B.p4" + + # called shadowed shadowed + # D.p1 public C.p1 private B.p1 private + # D.p2 public C.p2 public B.p2 private + # D.p3 public C.p3 private B.p3 public + # D.p4 public C.p4 public B.p4 public + + ? {d1 p1} "D.p1 " + ? {d1 p2} "D.p2 C.p2 " + ? {d1 p3} "D.p3 B.p3" + ? {d1 p4} "D.p4 C.p4 B.p4" + + # add on B calls to local + C eval { + :public method q1 {} {nsf::my -local p1} + :public method q3 {} {nsf::my -local p3} + } + # all chains start with C, since local resolve works + ? {c1 q1} "C.p1 " + ? {c1 q3} "C.p3 B.p3" + + # calls via method handles allows us to dispatch private methods, + # results like "-local" resolves above + ? {c1 [C info method handle p1]} "C.p1 " + ? {c1 [C info method handle p3]} "C.p3 B.p3" + + # calls via method handles allows us to dispatch private methods, + # results like "-local" resolves above + ? {nsf::object::dispatch c1 [C info method handle p1]} "C.p1 " + ? {nsf::object::dispatch c1 [C info method handle p3]} "C.p3 B.p3" + + # we can't call the private method via dispatch, since the private + # methods are removed from the search for methods + ? {nsf::object::dispatch c1 p1} "::c1: unable to dispatch method 'p1'" + ? {nsf::object::dispatch c1 p3} "B.p3" + + # via dispatch, the local flag uses (as always) the context of the + # currently execting class, which is not provided below + ? {nsf::object::dispatch c1 -local p1} "::c1: unable to dispatch method 'p1'" + +} + +# # test "nsf::my -local" on classes # @@ -176,13 +255,13 @@ nx::Test case my+handle-instead-of-my-local { nx::Class create Base { - :protected method privateMethod {a b} { expr {$a + $b} } + :private method privateMethod {a b} { expr {$a + $b} } :public method foo {a b} { nsf::my [Base info method handle privateMethod] $a $b } } nx::Class create Sub -superclass Base { :public method bar {a b} { nsf::my [Sub info method handle privateMethod] $a $b } - :public method privateMethod {a b} { expr {$a * $b} } + :private method privateMethod {a b} { expr {$a * $b} } :create s1 } @@ -197,13 +276,13 @@ nx::Test case dispatch-instead-of-my-local { nx::Class create Base { - :protected method privateMethod {a b} { expr {$a + $b} } + :private method privateMethod {a b} { expr {$a + $b} } :public method foo {a b} { nsf::object::dispatch [self] [Base info method handle privateMethod] $a $b } } nx::Class create Sub -superclass Base { :public method bar {a b} { nsf::object::dispatch [self] [Sub info method handle privateMethod] $a $b } - :public method privateMethod {a b} { expr {$a * $b} } + :private method privateMethod {a b} { expr {$a * $b} } :create s1 }