Index: TODO =================================================================== diff -u -r0916c233a044d06a1430c644e1976bfc27f7537c -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- TODO (.../TODO) (revision 0916c233a044d06a1430c644e1976bfc27f7537c) +++ TODO (.../TODO) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -3814,15 +3814,42 @@ method definition". - simplified usage of ObjectName() and ClassName() macros (no caller parenthesis needed) -- added exerpimental object property keepcaller self (currently only evaluated by aliased objects) - +- added exerpimental object property keepcallerself (currently only evaluated by aliased objects) - removed TODOs from keepcallerself in destroy.test; calls were truely recursive, behavior was correct. +- Added exerpimental object property "allowmethoddispatch" for + child-objects to be called from parent objects via method interface. + Background: both, per-object methods and childobjects are + implemented via cmds in the same tcl namespace. Without special + care, both are callable via the standard dispatch. Apparently, this + is not always wanted. +- handled allowmethoddispatch and keepcallerself in copy/move +- set allowmethoddispatch per-default in XOTcl +- removed visablilty of objects with "allowmethoddispatch" false in + "info methods" and "info search methods" +- extended regression test + ======================================================================== TODO: +- also aliasMethods pointing to objects require that these objects + have allowmethoddispatch, since this is an object property + (not an alias property). + * contra: might be unexpected + * pro: if we keep the logic of ensemble methods with the per-object + methods, and/or the keepcallerself, it make sense to avoid mixing + interpretations from the point of view of different objects + - corollary: for complete configurability of method dispatch, we would + need as well a 3rd object-property: restrict-dispatch-to-object-methods + +- pertaining allowmethoddispatch and keepcallerself in serializer +- setting of allowmethoddispatch in XOTcl is weak, since set over constructor, + which migh be overwritten by application classes +- should we allow objects to overwrite procs/methods and vice versa? +- behavior on keepcallerself on ordinary dispatches with implicit/explicit receiver + - Aliases and forwards are not handled by NsfNSCopyCmdsCmd; object cloning/copying remains incomplete; also, there might be object and method properties not handled as well (as the "returns" method Index: generic/nsf.c =================================================================== diff -u -rdfaca66a33107320eac62bde6ac8ea15abbcbe83 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- generic/nsf.c (.../nsf.c) (revision dfaca66a33107320eac62bde6ac8ea15abbcbe83) +++ generic/nsf.c (.../nsf.c) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -3870,12 +3870,9 @@ } } - /*fprintf(stderr, "InterpColonCmdResolver cmdName %s flags %.6x, frame flags %.6x\n", - cmdName, flags, Tcl_CallFrame_isProcCallFrame(varFramePtr));*/ - #if defined(CMD_RESOLVER_TRACE) fprintf(stderr, "InterpColonCmdResolver cmdName %s flags %.6x, frame flags %.6x\n", - cmdName, flags, Tcl_CallFrame_isProcCallFrame(varFramePtr)); + cmdName, flags, frameFlags); #endif if (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_OBJECT|FRAME_IS_NSF_CMETHOD )) { @@ -5085,16 +5082,20 @@ PrimitiveDestroy(object); - if (!(object->flags & NSF_TCL_DELETE)) { + if (object->teardown == NULL) /* (object->flags & NSF_TCL_DELETE)*/ { Tcl_Obj *savedResultObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(savedResultObj); + + assert(object->teardown == NULL); + /*fprintf(stderr, " before DeleteCommandFromToken %p object flags %.6x\n", oid, object->flags);*/ /*fprintf(stderr, "cmd dealloc %p refCount %d dodestroy \n", oid, Tcl_Command_refCount(oid));*/ Tcl_DeleteCommandFromToken(interp, oid); /* this can change the result */ /*fprintf(stderr, " after DeleteCommandFromToken %p %.6x\n", oid, ((Command *)oid)->flags);*/ Tcl_SetObjResult(interp, savedResultObj); DECR_REF_COUNT(savedResultObj); } + NsfCleanupObject(object, "CallStackDoDestroy"); } } @@ -6252,14 +6253,13 @@ for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr; hPtr = Tcl_NextHashEntry(&search)) { NsfObject *inst = (NsfObject *)Tcl_GetHashKey(tablePtr, hPtr); - Command *cmdPtr; + Command *cmdPtr = likely(inst != NULL) ? (Command *)inst->id : NULL; - if (unlikely(inst->flags & NSF_TCL_DELETE)) { + if (unlikely(cmdPtr == NULL || (Tcl_Command_flags(cmdPtr) & CMD_IS_DELETED))) { NsfLog(interp, NSF_LOG_NOTICE, "Object %s is apparently deleted", ObjectName(inst)); continue; } - cmdPtr = (Command *)inst->id; assert(cmdPtr); if (unlikely(cmdPtr && (cmdPtr->nsPtr->flags & NS_DYING))) { @@ -9520,7 +9520,8 @@ */ if (CmdIsNsfObject(cmd)) { /* - * Invoke an aliased object (ensemble object) via method interface. + * Invoke an may be aliased object (ensemble object) via method + * interface. */ NsfObject *invokeObj = (NsfObject *)cp; @@ -9583,8 +9584,8 @@ if (likely(self->nsPtr != NULL)) { cmd = FindMethod(self->nsPtr, methodName); - /*fprintf(stderr, "... objv[0] %s method %p %s csc %p\n", - ObjStr(objv[0]), cmd, methodName, cscPtr); */ + /*fprintf(stderr, "... objv[0] %s cmd %p %s csc %p\n", + ObjStr(objv[0]), cmd, methodName, cscPtr); */ if (likely(cmd != NULL)) { /* @@ -9722,8 +9723,8 @@ CscListAdd(interp, cscPtr); - /* fprintf(stderr, "cmdMethodDispatch %p %s.%s, nothing stacked, objflags %.6x\n", - cmd, ObjectName(object), methodName, object->flags); */ + /*fprintf(stderr, "cmdMethodDispatch %p %s.%s, nothing stacked, objflags %.6x\n", + cmd, ObjectName(object), methodName, object->flags); */ return CmdMethodDispatch(clientData, interp, objc, objv, object, cmd, NULL); } @@ -10164,16 +10165,27 @@ assert(cmd ? ((Command *)cmd)->objProc != NULL : 1); } else { - /* do we have an object-specific proc? */ + /* + * Do we have an object-specific cmd? + */ if (object->nsPtr && (flags & (NSF_CM_NO_OBJECT_METHOD|NSF_CM_SYSTEM_METHOD)) == 0) { cmd = FindMethod(object->nsPtr, methodName); /*fprintf(stderr, "lookup for per-object method in obj %p method %s nsPtr %p" " => %p objProc %p\n", object, methodName, object->nsPtr, cmd, cmd ? ((Command *)cmd)->objProc : NULL);*/ if (cmd) { - if ((flags & (NSF_CM_LOCAL_METHOD|NSF_CM_IGNORE_PERMISSIONS)) == 0 - && (Tcl_Command_flags(cmd) & NSF_CMD_CALL_PRIVATE_METHOD)) { + NsfObject *o; + + /* + * Reject call when + * a) trying to call a private method without the local flag or ignore permssions, or + * b) trying to call an object with no method interface + */ + if (((flags & (NSF_CM_LOCAL_METHOD|NSF_CM_IGNORE_PERMISSIONS)) == 0 + && (Tcl_Command_flags(cmd) & NSF_CMD_CALL_PRIVATE_METHOD)) + || ((o = NsfGetObjectFromCmdPtr(cmd)) && o->id == cmd && (o->flags & NSF_ALLOW_METHOD_DISPATCH) == 0) + ) { cmd = NULL; } else { @@ -10253,6 +10265,7 @@ } } + /* * If we have a command, check the permissions, unless * NSF_CM_IGNORE_PERMISSIONS is set. Note, that NSF_CM_IGNORE_PERMISSIONS is @@ -10315,7 +10328,7 @@ cscPtr->objv = objv+shift; } - /*fprintf(stderr, "MethodDispatch %s.%s %p flags %.6x cscPtr %p\n", + /*fprintf(stderr, "MethodDispatchCsc %s.%s %p flags %.6x cscPtr %p\n", ObjectName(object), methodName, object->mixinStack, cscPtr->flags, cscPtr);*/ @@ -13861,7 +13874,7 @@ NsfObject *object = (NsfObject *)clientData; Tcl_Interp *interp; - object->flags |= NSF_TCL_DELETE; + /*object->flags |= NSF_TCL_DELETE;*/ /*fprintf(stderr, "cmd dealloc %p TclDeletesObject (%d)\n", object->id, Tcl_Command_refCount(object->id));*/ @@ -15348,14 +15361,14 @@ if (unlikely(tcd->verbose)) { Tcl_Obj *cmd = Tcl_NewListObj(objc, objv); - fprintf(stderr, "forwarder calls '%s'\n", ObjStr(cmd)); + /*fprintf(stderr, "forwarder calls '%s'\n", ObjStr(cmd));*/ DECR_REF_COUNT(cmd); } if (tcd->objframe) { Nsf_PushFrameObj(interp, object, framePtr); } if (tcd->objProc) { - /* fprintf(stderr, "CallForwarder Tcl_NRCallObjProc %p\n", clientData);*/ + /*fprintf(stderr, "CallForwarder Tcl_NRCallObjProc %p\n", tcd->clientData);*/ result = Tcl_NRCallObjProc(interp, tcd->objProc, tcd->clientData, objc, objv); } else if (TclObjIsNsfObject(interp, tcd->cmdName, &object)) { /*fprintf(stderr, "CallForwarder NsfObjDispatch object %s, objc=%d\n", @@ -15366,7 +15379,7 @@ result = DispatchDefaultMethod(interp, object, objv[0], NSF_CSC_IMMEDIATE); } } else { - /*fprintf(stderr, "CallForwarder: no nsf object %s\n", ObjStr(tcd->cmdName));*/ + /*fprintf(stderr, "CallForwarder: no nsf object %s [0] %s\n", ObjStr(tcd->cmdName), ObjStr(objv[0]));*/ result = Tcl_EvalObjv(interp, objc, objv, 0); } @@ -15660,10 +15673,10 @@ cmd = Tcl_GetCommandFromObj(interp, targetObj); if (cmd) { cmd = GetOriginalCommand(cmd); - fprintf(stderr, "cmd %p epoch %d deleted %.6x\n", + /*fprintf(stderr, "cmd %p epoch %d deleted %.6x\n", cmd, Tcl_Command_cmdEpoch(cmd), - Tcl_Command_flags(cmd) & CMD_IS_DELETED); + Tcl_Command_flags(cmd) & CMD_IS_DELETED);*/ if (Tcl_Command_flags(cmd) & CMD_IS_DELETED) { cmd = NULL; } @@ -17515,6 +17528,8 @@ */ hPtr = Tcl_CreateHashEntry(tablePtr, pattern, NULL); if (hPtr) { + NsfObject *childObject; + key = Tcl_GetHashKey(tablePtr, hPtr); cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); methodTypeMatch = MethodTypeMatches(interp, methodType, cmd, object, key, @@ -17523,8 +17538,22 @@ if (Tcl_Command_flags(cmd) & NSF_CMD_CLASS_ONLY_METHOD && !NsfObjectIsClass(object)) { return TCL_OK; } - if (isObject && withPath) { - return TCL_OK; + /* + * Aliased objects methods return 1 but lookup from cmd returns + * NULL. Below, we are just interested on true subobjects. + */ + childObject = isObject ? NsfGetObjectFromCmdPtr(cmd) : NULL; + + if (childObject) { + + if (withPath) { + return TCL_OK; + } + + if ((childObject->flags & NSF_ALLOW_METHOD_DISPATCH ) == 0) { + /*fprintf(stderr, "no method dispatch allowed on child %s\n", ObjectName(childObject));*/ + return TCL_OK; + } } if (ProtectionMatches(withCallprotection, cmd) && methodTypeMatch) { @@ -17552,41 +17581,56 @@ for (hPtr = Tcl_FirstHashEntry(tablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { + NsfObject *childObject; + key = Tcl_GetHashKey(tablePtr, hPtr); cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); if (prefixLength) {Tcl_DStringTrunc(prefix, prefixLength);} methodTypeMatch = MethodTypeMatches(interp, methodType, cmd, object, key, withPer_object, &isObject); - if (isObject && withPath) { - Tcl_DString ds, *dsPtr = &ds; - NsfObject *ensembleObject = NsfGetObjectFromCmdPtr(cmd); - Tcl_HashTable *cmdTablePtr = ensembleObject && ensembleObject->nsPtr ? - Tcl_Namespace_cmdTablePtr(ensembleObject->nsPtr) : NULL; + /* + * Aliased objects methods return 1 but lookup from cmd returns + * NULL. Below, we are just interested on true subobjects. + */ + childObject = isObject ? NsfGetObjectFromCmdPtr(cmd) : NULL; - if (cmdTablePtr == NULL || ensembleObject == NULL) { - /* nothing to do */ + if (childObject) { + if (withPath) { + Tcl_DString ds, *dsPtr = &ds; + Tcl_HashTable *cmdTablePtr = childObject->nsPtr ? Tcl_Namespace_cmdTablePtr(childObject->nsPtr) : NULL; + + if (cmdTablePtr == NULL || childObject == NULL) { + /* nothing to do */ + continue; + } + if (childObject->flags & NSF_IS_SLOT_CONTAINER) { + /* Don't report slot container */ + continue; + } + + if (prefix == NULL) { + DSTRING_INIT(dsPtr); + Tcl_DStringAppend(dsPtr, key, -1); + Tcl_DStringAppend(dsPtr, " ", 1); + + ListMethodKeys(interp, cmdTablePtr, dsPtr, pattern, methodType, withCallprotection, + 1, dups, object, withPer_object); + DSTRING_FREE(dsPtr); + } else { + Tcl_DStringAppend(prefix, key, -1); + Tcl_DStringAppend(prefix, " ", 1); + ListMethodKeys(interp, cmdTablePtr, prefix, pattern, methodType, withCallprotection, + 1, dups, object, withPer_object); + } + /* don't list ensembles by themselves */ continue; - } - if (ensembleObject->flags & NSF_IS_SLOT_CONTAINER) { - /* Don't report slot container */ - continue; } - if (prefix == NULL) { - DSTRING_INIT(dsPtr); - Tcl_DStringAppend(dsPtr, key, -1); - Tcl_DStringAppend(dsPtr, " ", 1); - ListMethodKeys(interp, cmdTablePtr, dsPtr, pattern, methodType, withCallprotection, - 1, dups, object, withPer_object); - DSTRING_FREE(dsPtr); - } else { - Tcl_DStringAppend(prefix, key, -1); - Tcl_DStringAppend(prefix, " ", 1); - ListMethodKeys(interp, cmdTablePtr, prefix, pattern, methodType, withCallprotection, - 1, dups, object, withPer_object); + if ((childObject->flags & NSF_ALLOW_METHOD_DISPATCH ) == 0) { + /*fprintf(stderr, "no method dispatch allowed on child %s\n", ObjectName(childObject));*/ + continue; } - /* don't list ensembles by themselves */ - continue; + } if (Tcl_Command_flags(cmd) & NSF_CMD_CLASS_ONLY_METHOD && !NsfObjectIsClass(object)) continue; @@ -17680,9 +17724,10 @@ if (withDefinition) { Tcl_HashEntry *hPtr = pattern ? Tcl_CreateHashEntry(tablePtr, pattern, NULL) : NULL; - /* notice: we don't use pattern for wildcard matching here; - pattern can only contain wildcards when used without - "-definition" */ + /* + * Notice: we don't use pattern for wildcard matching here; pattern can + * only contain wildcards when used without "-definition". + */ if (hPtr) { Tcl_Command cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); ClientData clientData = cmd ? Tcl_Command_objClientData(cmd) : NULL; @@ -18594,8 +18639,8 @@ if (oldCmd != NULL) { oldTargetObject = NsfGetObjectFromCmdPtr(oldCmd); - /*fprintf(stderr, "oldTargetObject %p flags %.6x newTargetObject %p\n", - oldTargetObject, oldTargetObject ? oldTargetObject->flags : 0, newTargetObject);*/ + /* fprintf(stderr, "oldTargetObject %p flags %.6x newTargetObject %p\n", + oldTargetObject, oldTargetObject ? oldTargetObject->flags : 0, newTargetObject);*/ /* * We might have to decrement the reference counter on an previously @@ -18615,12 +18660,21 @@ if (newTargetObject) { /* - * Thew new alias is pointing to an nsf object. Increment the object - * reference counter of the new aliased object only when the new target - * object is different from the old one. Note, that the old target object - * might be NULL in case the object is used here the first time. + * In case the newTargetObject is a child of the object, add redirector to + * allow calls independent from allowmethoddispatch + */ + if (GetObjectFromString(interp, Tcl_Command_nsPtr(cmd)->fullName) == object) { + newObjProc = NsfProcAliasMethod; + } + + /* + * The new alias is pointing to an nsf object. In case no aliasMethod is + * use, increment the object reference counter of the new aliased object + * only when the new target object is different from the old one. Note, + * that the old target object might be NULL in case the object is used + * here the first time. */ - if (oldTargetObject != newTargetObject) { + if (newObjProc == NULL && oldTargetObject != newTargetObject) { NsfObjectRefCountIncr(newTargetObject); } @@ -19305,7 +19359,7 @@ /* cmd "object::property" NsfObjectPropertyCmd { {-argName "objectName" -required 1 -type object} - {-argName "objectproperty" -type "initialized|class|rootmetaclass|rootclass|slotcontainer|keepcallerself" -required 1} + {-argName "objectproperty" -type "initialized|class|rootmetaclass|rootclass|slotcontainer|keepcallerself|allowmethoddispatch" -required 1} {-argName "value" -required 0 -type tclobj} } */ @@ -19321,6 +19375,7 @@ case ObjectpropertyRootclassIdx: flags = NSF_IS_ROOT_CLASS; break; case ObjectpropertySlotcontainerIdx: flags = NSF_IS_SLOT_CONTAINER; break; case ObjectpropertyKeepcallerselfIdx: flags = NSF_KEEP_CALLER_SELF; allowSet = 1; break; + case ObjectpropertyAllowmethoddispatchIdx: flags = NSF_ALLOW_METHOD_DISPATCH; allowSet = 1; break; } if (valueObj) { @@ -22446,7 +22501,10 @@ for (pl = classList; pl; pl = pl->nextPtr) { Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(pl->cl->nsPtr); - if (!MethodSourceMatches(withSource, pl->cl, NULL)) continue; + if (!MethodSourceMatches(withSource, pl->cl, NULL)) { + continue; + } + ListMethodKeys(interp, cmdTablePtr, NULL, pattern, methodType, withCallprotection, withPath, dups, object, withPer_object); Index: generic/nsfAPI.decls =================================================================== diff -u -rdfaca66a33107320eac62bde6ac8ea15abbcbe83 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- generic/nsfAPI.decls (.../nsfAPI.decls) (revision dfaca66a33107320eac62bde6ac8ea15abbcbe83) +++ generic/nsfAPI.decls (.../nsfAPI.decls) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -151,7 +151,7 @@ } {-nxdoc 1} cmd "object::property" NsfObjectPropertyCmd { {-argName "objectName" -required 1 -type object} - {-argName "objectproperty" -type "initialized|class|rootmetaclass|rootclass|slotcontainer|keepcallerself" -required 1} + {-argName "objectproperty" -type "initialized|class|rootmetaclass|rootclass|slotcontainer|keepcallerself|allowmethoddispatch" -required 1} {-argName "value" -required 0 -type tclobj} } {-nxdoc 1} cmd "object::qualify" NsfObjectQualifyCmd { Index: generic/nsfAPI.h =================================================================== diff -u -rdfaca66a33107320eac62bde6ac8ea15abbcbe83 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- generic/nsfAPI.h (.../nsfAPI.h) (revision dfaca66a33107320eac62bde6ac8ea15abbcbe83) +++ generic/nsfAPI.h (.../nsfAPI.h) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -148,12 +148,12 @@ return result; } -enum ObjectpropertyIdx {ObjectpropertyNULL, ObjectpropertyInitializedIdx, ObjectpropertyClassIdx, ObjectpropertyRootmetaclassIdx, ObjectpropertyRootclassIdx, ObjectpropertySlotcontainerIdx, ObjectpropertyKeepcallerselfIdx}; +enum ObjectpropertyIdx {ObjectpropertyNULL, ObjectpropertyInitializedIdx, ObjectpropertyClassIdx, ObjectpropertyRootmetaclassIdx, ObjectpropertyRootclassIdx, ObjectpropertySlotcontainerIdx, ObjectpropertyKeepcallerselfIdx, ObjectpropertyAllowmethoddispatchIdx}; static int ConvertToObjectproperty(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; - static CONST char *opts[] = {"initialized", "class", "rootmetaclass", "rootclass", "slotcontainer", "keepcallerself", NULL}; + static CONST char *opts[] = {"initialized", "class", "rootmetaclass", "rootclass", "slotcontainer", "keepcallerself", "allowmethoddispatch", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "objectproperty", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); @@ -201,7 +201,7 @@ {ConvertToRelationtype, "object-mixin|class-mixin|object-filter|class-filter|class|superclass|rootclass"}, {ConvertToSource, "all|application|baseclasses"}, {ConvertToConfigureoption, "debug|dtrace|filter|profile|softrecreate|objectsystems|keepinitcmd|checkresults|checkarguments"}, - {ConvertToObjectproperty, "initialized|class|rootmetaclass|rootclass|slotcontainer|keepcallerself"}, + {ConvertToObjectproperty, "initialized|class|rootmetaclass|rootclass|slotcontainer|keepcallerself|allowmethoddispatch"}, {ConvertToAssertionsubcmd, "check|object-invar|class-invar"}, {NULL, NULL} }; Index: generic/nsfInt.h =================================================================== diff -u -rdfaca66a33107320eac62bde6ac8ea15abbcbe83 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- generic/nsfInt.h (.../nsfInt.h) (revision dfaca66a33107320eac62bde6ac8ea15abbcbe83) +++ generic/nsfInt.h (.../nsfInt.h) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -380,16 +380,15 @@ #define NSF_IS_ROOT_META_CLASS 0x0080 #define NSF_IS_ROOT_CLASS 0x0100 #define NSF_IS_SLOT_CONTAINER 0x0200 +#define NSF_KEEP_CALLER_SELF 0x0400 +#define NSF_ALLOW_METHOD_DISPATCH 0x0800 /* deletion state */ -#define NSF_TCL_DELETE 0x0400 -#define NSF_DESTROY_CALLED_SUCCESS 0x0800 -#define NSF_DURING_DELETE 0x1000 -#define NSF_DELETED 0x2000 -#define NSF_RECREATE 0x4000 -#define NSF_KEEP_CALLER_SELF 0x8000 +#define NSF_DESTROY_CALLED_SUCCESS 0x1000 +#define NSF_DURING_DELETE 0x2000 +#define NSF_DELETED 0x4000 +#define NSF_RECREATE 0x8000 - /* flags for NsfParams */ #define NSF_ARG_REQUIRED 0x000001 Index: library/nx/nx.tcl =================================================================== diff -u -rdfaca66a33107320eac62bde6ac8ea15abbcbe83 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- library/nx/nx.tcl (.../nx.tcl) (revision dfaca66a33107320eac62bde6ac8ea15abbcbe83) +++ library/nx/nx.tcl (.../nx.tcl) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -147,22 +147,31 @@ if {[string first " " $path] > -1} { set methodName [lindex $path end] set regObject $object + foreach w [lrange $path 0 end-1] { - #puts stderr "check $object info methods $path @ <$w>" set scope [expr {[::nsf::is class $object] && !${per-object} ? "class" : "object"}] - if {[::nsf::directdispatch $object ::nsf::methods::${scope}::info::methods $w] eq ""} { + if {[::nsf::is class $object] && !${per-object}} { + set scope class + set ensembleName [::nx::slotObj ${object} __$w] + } else { + set scope object + set ensembleName ${object}::$w + } + #puts stderr "NX check $scope $object info methods $path @ <$w> cmd=[info command $w] obj?[nsf::object::exists $ensembleName] " + #if {[::nsf::directdispatch $object ::nsf::methods::${scope}::info::methods $w] eq ""} + if {![nsf::object::exists $ensembleName]} { # # Create dispatch/ensemble object and accessor method (if wanted) # + set o [nx::EnsembleObject create $ensembleName] if {$scope eq "class"} { - set o [nx::EnsembleObject create [::nx::slotObj ${object} __$w]] if {$verbose} {puts stderr "... create object $o"} # We are on a class, and have to create an alias to be # accessible for objects ::nsf::method::alias $object $w $o if {$verbose} {puts stderr "... create alias $object $w $o"} } else { - set o [EnsembleObject create ${object}::$w] + #::nsf::object::property ${object}::$w allowmethoddispatch true if {$verbose} {puts stderr "... create object $o"} } set object $o @@ -472,6 +481,9 @@ # :protected method init {} { ::nsf::object::property [self] keepcallerself true + ::nsf::object::property [self] allowmethoddispatch true + # object property "allowmethoddispatch" is just needed for + # per-object ensembles and is set upon this creaton. } :protected method unknown {callInfo args} { set path [lrange $callInfo 1 end-1]; # set path [current methodpath] @@ -2060,6 +2072,8 @@ :copyNSVarsAndCmds ::nsf::classes$origin ::nsf::classes$dest } # copy object -> might be a class obj + ::nsf::object::property $obj keepcallerself [::nsf::object::property $origin keepcallerself] + ::nsf::object::property $obj allowmethoddispatch [::nsf::object::property $origin allowmethoddispatch] ::nsf::method::assertion $obj check [::nsf::method::assertion $origin check] ::nsf::method::assertion $obj object-invar [::nsf::method::assertion $origin object-invar] ::nsf::relation $obj object-filter [::nsf::relation $origin object-filter] Index: library/xotcl/library/xotcl2.tcl =================================================================== diff -u -rdfaca66a33107320eac62bde6ac8ea15abbcbe83 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision dfaca66a33107320eac62bde6ac8ea15abbcbe83) +++ library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -385,6 +385,7 @@ # "init" must exist on Object. per default it is empty. Object instproc init args { + ::nsf::object::property [self] allowmethoddispatch 1 if {![::nsf::current isnextcall] && [llength $args] > 0 && [::nsf::configure debug] > 0} { ::nsf::log Warning "Arguments '$args' to constructor of object [self] are most likely not processed" } @@ -464,6 +465,8 @@ Object create ::xotcl::classInfo ::nsf::object::property ::xotcl::objectInfo keepcallerself true ::nsf::object::property ::xotcl::classInfo keepcallerself true + ::nsf::object::property ::xotcl::objectInfo allowmethoddispatch true + ::nsf::object::property ::xotcl::classInfo allowmethoddispatch true # note, we are using ::xotcl::infoError, defined below #Object instforward info -onerror ::nsf::infoError ::xotcl::objectInfo %1 {%@2 %self} Index: tests/destroy.test =================================================================== diff -u -r0916c233a044d06a1430c644e1976bfc27f7537c -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- tests/destroy.test (.../destroy.test) (revision 0916c233a044d06a1430c644e1976bfc27f7537c) +++ tests/destroy.test (.../destroy.test) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -793,7 +793,7 @@ ? {::nsf::object::exists ::C} 1 ? {::nsf::object::exists ::C::slot} 1 - + set s(C) [C serialize] C destroy Index: tests/info-method.test =================================================================== diff -u -r4f17631ecd74cd12f18168931a93b46908cec01b -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- tests/info-method.test (.../info-method.test) (revision 4f17631ecd74cd12f18168931a93b46908cec01b) +++ tests/info-method.test (.../info-method.test) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -169,22 +169,46 @@ ::nx::configure defaultMethodCallProtection false } -nx::Test case subobj { +# +# Test visability of obj-objects +# + +nx::Test case sub-objects { ::nx::Object create o { ::nx::Object create [::nx::self]::sub { :method foo {} {;} } - :alias subal ::o::sub + :public alias soAlias ::o::sub } - ? {o info methods} "sub subal" + ? {o info methods} "soAlias" + ? {o info method type soAlias} "alias" + + nsf::object::property ::o::sub allowmethoddispatch on + ? {o info methods} "soAlias sub" ? {o info method type sub} "object" ? {o info method definition sub} "::nx::Object create ::o::sub" - ? {o info method type subal} "alias" } -package req nx -package require nx::test +# +# Test visability of aliased Objects +# +nx::Test case sub-objects { + ::nx::Object create ::I + ::nx::Class create C { + :public alias i ::I + :create c1 + } + ? {C info methods i} "" + ? {c1 info lookup methods i} "" + ::nsf::object::property ::I allowmethoddispatch 1 + ? {C info methods i} "i" + ? {c1 info lookup methods i} "i" +} + + +#package require nx::test + # # Introspect the returns method property throught the "info method" # API chunk ... Index: tests/methods.test =================================================================== diff -u -r1d09ffd51baae51ba946eb3b617462d3931a9622 -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- tests/methods.test (.../methods.test) (revision 1d09ffd51baae51ba946eb3b617462d3931a9622) +++ tests/methods.test (.../methods.test) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -413,11 +413,11 @@ :property {a v} # the other methods don't require them as strong :forward b ::o2 bar - :method foo {} {return [self]} + :method foo {} {return [nx::self]} :alias x ::o::foo } nx::Object create o2 { - :public method bar {} {return [self]} + :public method bar {} {return [nx::self]} } # dispatch methods without current object @@ -429,7 +429,7 @@ ? {::o x} "::o" # check, if missing object is still detected ? ::o::x "No current object; x called outside the context of a Next Scripting method" - ? self "No current object; command called outside the context of a Next Scripting method" + ? nx::self "No current object; command called outside the context of a Next Scripting method" } Index: tests/submethods.test =================================================================== diff -u -rc4997e0189bb712287aa53d12bb3e332acfb781d -re7a27ff80df0f9c20be649e1ea1d0bc55f045739 --- tests/submethods.test (.../submethods.test) (revision c4997e0189bb712287aa53d12bb3e332acfb781d) +++ tests/submethods.test (.../submethods.test) (revision e7a27ff80df0f9c20be649e1ea1d0bc55f045739) @@ -520,8 +520,8 @@ } # +# Test current args in ensemble methods # -# nx::Test case current-args { nx::Class create C { :method foo {{-x 1} z:optional} {return [current args]} @@ -533,8 +533,95 @@ ? {c1 foo -x 2} "-x 2" ? {c1 bar foo -x 2} "-x 2" - } +# +# Test forwarding to child object, with respect to settings of the +# object properties keepcallerself and allowmethoddispatch +# +nx::Test case child-obj-delegation { + + nx::Object create obj { + nx::Object create [self]::child { + :public method foo {} {return [self]} + } + :public forward link1 {%[self]::child} + :public forward link2 :child + :public method link3 args {[self]::child {*}$args} + :public alias link4 [self]::child + :public forward link5 [self]::child + } + + # + # Default case + # keepcallerself false + # allowmethoddispatch false + # + + ::nsf::object::property obj::child keepcallerself false + ::nsf::object::property obj::child allowmethoddispatch false + + ? {obj link1 foo} {::obj::child} + ? {obj link2 foo} {::obj: unable to dispatch method 'child'} + ? {obj link3 foo} {::obj::child} + ? {obj link4 foo} {::obj::child} + ? {obj link5 foo} {::obj::child} + + ? {lsort [obj info methods child]} {} + ? {lsort [obj info methods]} {link1 link2 link3 link4 link5} + ? {lsort [obj info lookup methods child]} {} + ? {lsort [obj info lookup methods child*]} {} + + # + # turn on keepcallerself + # + ::nsf::object::property obj::child keepcallerself true + ::nsf::object::property obj::child allowmethoddispatch false + + ? {obj link1 foo} {::obj::child} + ? {obj link2 foo} {::obj: unable to dispatch method 'child'} + ? {obj link3 foo} {::obj::child} + ? {obj link4 foo} {::obj} + ? {obj link5 foo} {::obj::child} + + ? {lsort [obj info methods child]} {} + ? {lsort [obj info methods]} {link1 link2 link3 link4 link5} + ? {lsort [obj info lookup methods child]} {} + ? {lsort [obj info lookup methods child*]} {} + + # + # turn on allowmethoddispatch + # + ::nsf::object::property obj::child keepcallerself false + ::nsf::object::property obj::child allowmethoddispatch true + + ? {obj link1 foo} {::obj::child} + ? {obj link2 foo} {::obj::child} + ? {obj link3 foo} {::obj::child} + ? {obj link4 foo} {::obj::child} + ? {obj link5 foo} {::obj::child} + + ? {lsort [obj info methods child]} {child} + ? {lsort [obj info methods]} {child link1 link2 link3 link4 link5} + ? {lsort [obj info lookup methods child]} {child} + ? {lsort [obj info lookup methods child*]} {child} + + # + # turn on allowmethoddispatch and keepcallerself + # + ::nsf::object::property obj::child keepcallerself true + ::nsf::object::property obj::child allowmethoddispatch true + + ? {obj link1 foo} {::obj::child} + ? {obj link2 foo} {::obj} + ? {obj link3 foo} {::obj::child} + ? {obj link4 foo} {::obj} + ? {obj link5 foo} {::obj::child} + + ? {lsort [obj info methods child]} {child} + ? {lsort [obj info methods]} {child link1 link2 link3 link4 link5} + ? {lsort [obj info lookup methods child]} {child} + ? {lsort [obj info lookup methods child*]} {child} +} \ No newline at end of file