Index: TODO =================================================================== diff -u -r68d9af77ff619c859d4fe1d7123326c3cfa70b18 -r496f49d15463c79323454495e356de52137b46bd --- TODO (.../TODO) (revision 68d9af77ff619c859d4fe1d7123326c3cfa70b18) +++ TODO (.../TODO) (revision 496f49d15463c79323454495e356de52137b46bd) @@ -4621,24 +4621,41 @@ nx::test: - deactivate calling overhead calculation, since this is not reliable (sometimes larger than the call). -======================================================================== -TODO: -- handling of recreate (see regression test for class-level properties) +- Old TODO maybe obsolete: -- maybe "::nsf::object::property /obj/ volatile 0|1" to alter - volatile state. + handling of recreate (see regression test for class-level properties) -- document new setable object properties perobjectdispatch and keepcallerself + Could not find refernces about this in the parameters + or properties tests. -- behavior on keepcallerself on ordinary dispatches with implicit/explicit - receiver (currently the flag is ignored, the code just commented out) +nsf.m4: +- nsf.m4 currently unused (SC_PATH_NSFCONFIG and SC_LOAD_NSFCONFIG) + We can delete it from the current version. -- 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 - property before the recent fix) +nsf.c +- fixed a bug in "info methods returns" in cases, where + no returns info was available. +- we can "/obj/ copy" now objects/classes containing + * aliases + * setter + * ensemble methods + * method protections + Instead of handling cmd copying in NsfNSCopyCmdsCmd, + it is replaced now by introspection. +- extended regression test +======================================================================== +TODO: + +- copycmds is obsolete, copyNSVarsAndCmds should be renamed +- methods.test: test case object+class-copy: + after copy, the following command lists "__a" etc as well + #? {lsort [::D info object methods -path]} "{oa b} {oa c} ofoo ofwd set" +- methods.test: when method "exists" is required in test case + object+class-copy, cleanup leads to a crash +- nx.tcl: cleanup of XXX cmds + Stefan: API-related items * the introspection interface uses "-type" for "class" or "instanceof", but the intercession interface refers to "-class", e.g. @@ -4716,9 +4733,6 @@ - maybe optional arg (true) to ::nsf::object::initialized to generalize -noinit -- nsf.m4 seems to be currently unused (SC_PATH_NSFCONFIG and SC_LOAD_NSFCONFIG) - Should we delete it? - - maybe: add a disposition=pipe - MixinComputeOrderFullList() could receive a flag to store source classes in checkList @@ -4915,6 +4929,9 @@ * extend traits (object-specific traits, test cases, etc.) + * add maybe "::nsf::object::property /obj/ volatile 0|1" to alter + volatile state. + * Reduce / remove hard-code names. Right now, "method"/"alias"/ "forward" is returend by "info definition" introspection. This is not serious, since e.g. XOTcl handles this on the @@ -4953,6 +4970,12 @@ - Use parameter syntax in genTclAPI - better handling of "c1 cget -noinit" ? + * experimental features: + - document/extend/generalize/remove the experimental object properties + perobjectdispatch and keepcallerself + - behavior on keepcallerself on ordinary dispatches with implicit/explicit + receiver (currently the flag is ignored, the code just commented out) + * C-Code * Several of the tracing options in nsf.h could be replaced by DTrace * Rework implict namespace completion (NameInNamespaceObj(), Index: generic/nsf.c =================================================================== diff -u -r98775245c7eb6eb2662fbccfd9fea1f30bd5f3d8 -r496f49d15463c79323454495e356de52137b46bd --- generic/nsf.c (.../nsf.c) (revision 98775245c7eb6eb2662fbccfd9fea1f30bd5f3d8) +++ generic/nsf.c (.../nsf.c) (revision 496f49d15463c79323454495e356de52137b46bd) @@ -17932,8 +17932,8 @@ paramDefs = ParamDefsGet(importedCmd); if (paramDefs && paramDefs->returns) { Tcl_SetObjResult(interp, paramDefs->returns); - return TCL_OK; } + return TCL_OK; } case InfomethodsubcmdSyntaxIdx: { @@ -18494,7 +18494,13 @@ /* Don't report slot container */ continue; } + if ((childObject->flags & NSF_KEEP_CALLER_SELF) == 0) { + /* Do only report sub-objects with keep caller self */ + continue; + } + /*fprintf(stderr, "ListMethodKeys key %s append key space flags %.6x\n", + key, childObject->flags);*/ if (prefix == NULL) { DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, key, -1); @@ -20753,6 +20759,9 @@ * Otherwise: do not copy. */ cmd = Tcl_FindCommand(interp, newName, NULL, TCL_GLOBAL_ONLY); + + fprintf(stderr, "wanna copy %s to %p (new cmd %p)\n", oldName, newName, cmd); + if (cmd) { /*fprintf(stderr, "%s already exists\n", newName);*/ if (!GetObjectFromString(interp, newName)) { @@ -20784,6 +20793,7 @@ if (!GetObjectFromString(interp, oldName)) { if (CmdIsProc(cmd)) { +#if 0 Proc *procPtr = (Proc *)Tcl_Command_objClientData(cmd); Tcl_Obj *arglistObj; int result; @@ -20880,13 +20890,17 @@ Tcl_VarEval(interp, "proc ", newName, " {", ObjStr(arglistObj), "} {\n", ObjStr(procPtr->bodyPtr), "}", (char *) NULL); } +#endif } else { /* * Otherwise copy command. */ Tcl_ObjCmdProc *objProc = Tcl_Command_objProc(cmd); Tcl_CmdDeleteProc *deleteProc = Tcl_Command_deleteProc(cmd); ClientData clientData; + + fprintf(stderr, "copy CMD %s objproc %p\n", newName, objProc); + if (objProc) { clientData = Tcl_Command_objClientData(cmd); if (clientData == NULL || clientData == (ClientData)NSF_CMD_NONLEAF_METHOD) { Index: library/nx/nx.tcl =================================================================== diff -u -r3be87f20ac5f89fac33e2db3b95e80c9adfc92d9 -r496f49d15463c79323454495e356de52137b46bd --- library/nx/nx.tcl (.../nx.tcl) (revision 3be87f20ac5f89fac33e2db3b95e80c9adfc92d9) +++ library/nx/nx.tcl (.../nx.tcl) (revision 496f49d15463c79323454495e356de52137b46bd) @@ -2269,6 +2269,13 @@ :property objLength :method makeTargetList {t} { + if {[::nsf::is object,type=::nx::EnsembleObject $t]} { + # + # we do not copy ensemble objects, since method + # introspection/recreation will care about these + # + return + } lappend :targetList $t #puts stderr "COPY makeTargetList $t targetList '${:targetList}'" # if it is an object without namespace, it is a leaf @@ -2298,7 +2305,7 @@ :method copyNSVarsAndCmds {orig dest} { ::nsf::nscopyvars $orig $dest - ::nsf::nscopycmds $orig $dest + #::nsf::nscopycmd $orig $dest } # construct destination obj name from old qualified ns name @@ -2314,15 +2321,29 @@ :method copyTargets {} { #puts stderr "COPY will copy targetList = [set :targetList]" set objs {} + array set cmdMap {alias alias forward forward method create setter setter} foreach origin [set :targetList] { set dest [:getDest $origin] if {[::nsf::object::exists $origin]} { if {$dest eq ""} { set obj [[$origin info class] new -noinit] set dest [set :dest $obj] } else { - set obj [[$origin info class] create $dest -noinit] + if {[::nsf::object::property $origin slotcontainer] && [nsf::is object $dest]} { + # + # We do not want to clean slotcontainer. Assume a target + # list of the form "::C ::C::slot". First ::C is + # created (with e.g. ensemble objects, therefore + # creating itself ::C::slot). If a later creation of + # ::C::slot would clean the slot container it would + # damage ::C. If we would drop ::C::slot from the target + # list, properties and variables would no be copied. + # + } else { + set obj [[$origin info class] create $dest -noinit] + } } + # copy class information if {[::nsf::is class $origin]} { # obj is a class, copy class specific information @@ -2331,7 +2352,26 @@ ::nsf::relation $obj class-filter [::nsf::relation $origin class-filter] ::nsf::relation $obj class-mixin [::nsf::relation $origin class-mixin] :copyNSVarsAndCmds ::nsf::classes$origin ::nsf::classes$dest + + #puts stderr "XXX === class methods: [lsort [$origin ::nsf::methods::class::info::methods -path -callprotection all]]" + foreach m [$origin ::nsf::methods::class::info::methods -path -callprotection all] { + #puts stderr "XXX class [$origin ::nsf::methods::class::info::method definition $m]" + set rest [lassign [$origin ::nsf::methods::class::info::method definition $m] . protection what .] + #puts stderr "XXX class m $m rest <$rest>" + + # remove -returns from reported definitions + set p [lsearch -exact $rest -returns]; if {$p > -1} {set rest [lreplace $rest $p $p+1]} + + array set "" [$obj eval [list :__resolve_method_path $m]] + set r [::nsf::method::$cmdMap($what) $(object) $(methodName) {*}$rest] + #puts stderr "XXX class created $r" + ::nsf::method::property $(object) $r returns [$origin ::nsf::methods::class::info::method returns $m] + ::nsf::method::property $(object) $r call-protected [::nsf::method::property $origin $m call-protected] + ::nsf::method::property $(object) $r call-private [::nsf::method::property $origin $m call-private] + } + #puts stderr "XXX === class target: [lsort [$obj ::nsf::methods::class::info::methods -path -callprotection all]]" } + # copy object -> might be a class obj ::nsf::object::property $obj keepcallerself [::nsf::object::property $origin keepcallerself] ::nsf::object::property $obj perobjectdispatch [::nsf::object::property $origin perobjectdispatch] @@ -2340,7 +2380,7 @@ ::nsf::method::assertion $obj object-invar [::nsf::method::assertion $origin object-invar] ::nsf::relation $obj object-filter [::nsf::relation $origin object-filter] ::nsf::relation $obj object-mixin [::nsf::relation $origin object-mixin] - # reused in XOTcl, no "require" there, so use nsf primitiva + # reused in XOTcl, no "require namespace" there, so use nsf primitiva if {[::nsf::directdispatch $origin ::nsf::methods::object::info::hasnamespace]} { ::nsf::directdispatch $obj ::nsf::methods::object::requirenamespace } @@ -2349,17 +2389,29 @@ } lappend objs $obj :copyNSVarsAndCmds $origin $dest - foreach i [$origin ::nsf::methods::object::info::forward] { - ::nsf::method::forward $dest -per-object $i \ - {*}[$origin ::nsf::methods::object::info::forward -definition $i] + #puts stderr "XXX ??? object methods: [$origin ::nsf::methods::object::info::methods -path -callprotection all]" + foreach m [$origin ::nsf::methods::object::info::methods -path -callprotection all] { + set rest [lassign [$origin ::nsf::methods::object::info::method definition $m] . protection . what .] + #puts "XXX $m what $what rest $rest" + + # remove -returns from reported definitions + set p [lsearch -exact $rest -returns]; if {$p > -1} {set rest [lreplace $rest $p $p+1]} + + array set "" [$obj eval [list :__resolve_method_path -per-object $m]] + #puts "XXX $m create ::nsf::method::$cmdMap($what) $(object) -per-object $(methodName) $rest" + set r [::nsf::method::$cmdMap($what) $(object) -per-object $(methodName) {*}$rest] + ::nsf::method::property $(object) -per-object $r \ + returns \ + [$origin ::nsf::methods::object::info::method returns $m] + ::nsf::method::property $(object) -per-object $r \ + call-protected \ + [::nsf::method::property $origin -per-object $m call-protected] + ::nsf::method::property $(object) -per-object $r \ + call-private \ + [::nsf::method::property $origin -per-object $m call-private] } - if {[::nsf::is class $origin]} { - foreach i [$origin ::nsf::methods::class::info::forward] { - ::nsf::method::forward $dest $i \ - {*}[$origin ::nsf::methods::class::info::forward -definition $i] - } - } + #puts stderr "XXX === object target: [$obj ::nsf::methods::object::info::methods -path -callprotection all]" # # Check, if $origin is a slot container. If yes, set the slot Index: library/xotcl/library/xotcl2.tcl =================================================================== diff -u -rbca161db07ae2fe223f49b5aa5e44e7149cf9e67 -r496f49d15463c79323454495e356de52137b46bd --- library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision bca161db07ae2fe223f49b5aa5e44e7149cf9e67) +++ library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision 496f49d15463c79323454495e356de52137b46bd) @@ -811,6 +811,22 @@ uplevel [list [self] contains -object $slotContainer $cmd] } + # + # provide a stub function to allow reuse of copy of nx + # + ::nsf::method::create Object __resolve_method_path { + -per-object:switch + -verbose:switch + path + } { + set object [::nsf::self] + return [list object $object methodName $path regObject $object] + } + ::nsf::method::property Object __resolve_method_path call-protected true + + + + # assertion handling proc checkoption_xotcl1_to_internal checkoptions { set options [list] Fisheye: Tag 496f49d15463c79323454495e356de52137b46bd refers to a dead (removed) revision in file `nsf.m4'. Fisheye: No comparison available. Pass `N' to diff? Index: tests/methods.test =================================================================== diff -u -r6fd5b9d632efe378ecf7df0ecfb0a2ef6b39b7c1 -r496f49d15463c79323454495e356de52137b46bd --- tests/methods.test (.../methods.test) (revision 6fd5b9d632efe378ecf7df0ecfb0a2ef6b39b7c1) +++ tests/methods.test (.../methods.test) (revision 496f49d15463c79323454495e356de52137b46bd) @@ -1,9 +1,8 @@ # -*- Tcl -*- package require nx -#package require nx::plain-object-method +package require nx::test ::nx::configure defaultMethodCallProtection false -package require nx::test nx::Test parameter count 10 @@ -842,4 +841,134 @@ ? {::X set p1} 3 ? {::X set unknown} 2 ? {::X set recreate} 1 +} + + +# +# object copy +# +nx::Test case object-copy { + nsf::method::provide set {::nsf::method::alias set -frame object ::set} + + nx::Object create o { + :public object method foo {} {return foo} + :public object method "a b" {} {return "a b"} + :public object method "a c" {} {return "a c"} + :protected object method bar {} {return bar} + :private object method baz {} {return baz} + :public object forward fwd %self xxx + :require public object method set + } + ? {lsort [::o info object methods -path]} "{a b} {a c} foo fwd set" + ? {o a b} "a b" + ? {o a c} "a c" + ? {o set x 1} 1 + + ? {o copy p} ::p + ? {lsort [::p info object methods -path]} "{a b} {a c} foo fwd set" + + ? {p a b} "a b" + ? {p a c} "a c" + + #package require nx::serializer + #puts stderr [o serialize] + #puts stderr [p serialize] + ? {p set x} 1 +} + +# +# class copy +# +nx::Test case class-copy { + nsf::method::provide set {::nsf::method::alias set -frame object ::set} + + nx::Class create C { + :public method foo {} {return foo} + :public method "a b" {} {return "a b"} + :public method "a c" {} {return "a c"} + :protected method bar {} {return bar} + :private method baz {} {return baz} + :public forward fwd %self xxx + :require public method set + :create c1 + } + + ? {lsort [::C info methods -path]} "{a b} {a c} foo fwd set" + ? {::c1 a b} "a b" + ? {::c1 a c} "a c" + ? {::c1 set x 1} 1 + + ? {::C copy ::D} ::D + + ? {lsort [::D info methods -path]} "{a b} {a c} foo fwd set" + + #package require nx::serializer + #puts stderr [::C serialize] + #puts stderr [::D serialize] + + ::D create d1 + + ? {::d1 a b} "a b" + ? {::d1 a c} "a c" + + #puts stderr [::c1 serialize] + #puts stderr [::d1 serialize] + ? {::d1 set x 2} 2 +} + + +# +# class copy with class object methods +# +nx::Test case object+class-copy { + nsf::method::provide set {::nsf::method::alias set -frame object ::set} + nsf::method::provide exists {::nsf::method::alias exists ::nsf::methods::object::exists} + + nx::Class create C { + :public method foo {} {return foo} + :public method "a b" {} {return "a b"} + :public method "a c" {} {return "a c"} + :protected method bar {} {return bar} + :private method baz {} {return baz} + :public forward fwd %self xxx + :require public method set + + :public object method ofoo {} {return foo} + :public object method "oa b" {} {return "oa b"} + :public object method "oa c" {} {return "oa c"} + :protected object method obar {} {return bar} + :private object method obaz {} {return baz} + :public object forward ofwd %self xxx + #TODO: the following line leads to a crash + #:require public object method exists + :require public object method set + :create c1 + } + + ? {lsort [::C info methods -path]} "{a b} {a c} foo fwd set" + ? {lsort [::C info object methods -path]} "{oa b} {oa c} ofoo ofwd set" + + ? {::c1 a b} "a b" + ? {::c1 a c} "a c" + ? {::c1 set x 1} 1 + + ? {::C oa b} "oa b" + ? {::C oa c} "oa c" + ? {::C set y 100} "100" + + ? {::C copy ::D} ::D + + ? {lsort [::D info methods -path]} "{a b} {a c} foo fwd set" + #? {lsort [::D info object methods -path]} "{oa b} {oa c} ofoo ofwd set" + + ? {::D oa b} "oa b" + ? {::D oa c} "oa c" + ? {::D set y} "100" + + ::D create d1 + + ? {::d1 a b} "a b" + ? {::d1 a c} "a c" + + ? {::d1 set x 2} 2 } \ No newline at end of file