Index: generic/nsf.c =================================================================== diff -u -r9124d823b4eb4a8b5969b9fa1b6eab7252ba83b4 -r364a9eda329acd7d20173a4165d71394d3061aae --- generic/nsf.c (.../nsf.c) (revision 9124d823b4eb4a8b5969b9fa1b6eab7252ba83b4) +++ generic/nsf.c (.../nsf.c) (revision 364a9eda329acd7d20173a4165d71394d3061aae) @@ -16412,11 +16412,12 @@ *outObjPtr = objPtr; /* - * If argument checking is turned off, and we do not have an converter, do - * nothing. + * Omit argument checking, provided that ... + * ... argument checking is turned off *and* no converter is specified, or + * ... the ruling parameter option is 'initcmd' */ - if (unlikely(doCheck == 0) && (pPtr->flags & (NSF_ARG_IS_CONVERTER|NSF_ARG_INITCMD)) == 0) { - /*fprintf(stderr, "*** omit argument check for arg %s flags %.6x\n", pPtr->name, pPtr->flags);*/ + if ((unlikely(doCheck == 0) && (pPtr->flags & (NSF_ARG_IS_CONVERTER)) == 0) || (pPtr->flags & (NSF_ARG_INITCMD))) { + /* fprintf(stderr, "*** omit argument check for arg %s flags %.6x\n", pPtr->name, pPtr->flags); */ *clientData = ObjStr(objPtr); return TCL_OK; } Index: library/nx/nx.tcl =================================================================== diff -u -r5ce68a42506fcc981cea2431afa1b09b476e667a -r364a9eda329acd7d20173a4165d71394d3061aae --- library/nx/nx.tcl (.../nx.tcl) (revision 5ce68a42506fcc981cea2431afa1b09b476e667a) +++ library/nx/nx.tcl (.../nx.tcl) (revision 364a9eda329acd7d20173a4165d71394d3061aae) @@ -1243,8 +1243,11 @@ set options [:getParameterOptions -withMultiplicity true -forObjectParameter true] if {[info exists :initcmd]} { lappend options initcmd - set :parameterSpec [list [:namedParameterSpec $prefix ${:name} $options] ${:initcmd}] - + if {[info exists :default]} { + append initcmd "::nsf::var::set \[::nsf::self\] ${:name} ${:default};\n" + } + append initcmd ${:initcmd} + set :parameterSpec [list [:namedParameterSpec $prefix ${:name} $options] $initcmd] } elseif {[info exists :default]} { # deactivated for now: || [string first {$} ${:default}] > -1 if {[string match {*\[*\]*} ${:default}]} { @@ -1540,7 +1543,6 @@ {accessor true} {type} {settername} - valuecmd defaultcmd valuechangedcmd Index: library/xotcl/library/xotcl2.tcl =================================================================== diff -u -ra57782b043b4b1b9b81b0c7534365d36b01457ac -r364a9eda329acd7d20173a4165d71394d3061aae --- library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision a57782b043b4b1b9b81b0c7534365d36b01457ac) +++ library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision 364a9eda329acd7d20173a4165d71394d3061aae) @@ -919,15 +919,38 @@ if {[info exists :parameter]} {my ::nsf::classes::xotcl::Class::parameter ${:parameter}} next } - # provide minimal compatibility + :public forward instproc %self public method :public forward proc %self public class method + # # As NX/XOTcl hybrids, all slot kinds would not inherit the # unknown behaviour of ::xotcl::Class. Therefore, we provide it # explicitly to slots for backward compatibility ... # :public alias unknown ::nsf::classes::xotcl::Class::unknown + :public method objectparameter {} { + set parameterdefinitions [list] + set slots [nsf::directdispatch [self] \ + ::nsf::methods::class::info::slotobjects \ + -closure -type ::nx::Slot] + foreach slot $slots { + # + # Skip any positional object parameters (i.e., __initcmd) + # which are not backward compatible with the XOTcl slots + # interface ... + # + if {[$slot eval { + expr {[info exists :positional] && ${:positional}} + }]} continue; + lappend parameterdefinitions [$slot getParameterSpec] + } + # + # Add the XOTcl-specific handling of residual varargs + # + lappend parameterdefinitions args:alias,method=residualargs,args + return $parameterdefinitions + } } # @@ -944,9 +967,13 @@ return [$object eval [list :isMultivalued]] } } + # provide minimal compatibility + :public forward proc %self public method :public method exists {var} {::nsf::var::exists [self] $var} :public method istype {class} [::nx::Object info method body ::nsf::classes::xotcl::Object::istype] :public alias set -frame object ::set + :public alias residualargs ::nsf::methods::object::residualargs + :public alias instvar ::nsf::methods::object::instvar } # Index: library/xotcl/tests/slottest.xotcl =================================================================== diff -u -r65e384fc5b5fa044c63075f03756da88d316249f -r364a9eda329acd7d20173a4165d71394d3061aae --- library/xotcl/tests/slottest.xotcl (.../slottest.xotcl) (revision 65e384fc5b5fa044c63075f03756da88d316249f) +++ library/xotcl/tests/slottest.xotcl (.../slottest.xotcl) (revision 364a9eda329acd7d20173a4165d71394d3061aae) @@ -452,16 +452,14 @@ ? {p2 salary} 1009 Person slots { - Attribute create sex -type "sex" -convert true { - :public method type=sex {name value} { + Attribute create sex -type "sex" -convert true -proc type=sex {name value} { #puts stderr "[self] slot specific converter" switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } - } } Person p3 -sex male ? {p3 sex} m @@ -522,13 +520,11 @@ nx::Test case assign-via-slots Class create A -slots { - Attribute create foo -default 1 { - :public method assign {domain var value} { - if {$value < 0 || $value > 99} { - error "$value is not in the range of 0 .. 99" - } - $domain set $var $value + Attribute create foo -default 1 -proc assign {domain var value} { + if {$value < 0 || $value > 99} { + error "$value is not in the range of 0 .. 99" } + $domain set $var $value } } @@ -618,6 +614,25 @@ ? {catch {p2 name add BOOM!}} 1 ? {p2 name} "John Doe" +# +# 3) -proc inline statements upon Attribute creation +# (as found in the tutorial) +# + +Class create AA -slots { + Attribute foo -default 1 -proc assign {domain var value} { + if {$value < 0 || $value > 99} { + error "$value is not in the range of 0 .. 99" + } + $domain set $var $value + } +} + +AA create aa1 +? {aa1 foo 10} 10 +? {aa1 foo} 10 +? {catch {aa1 foo -1}} 1 + exit #puts [Person array get __defaults] Index: tests/parameters.test =================================================================== diff -u -r770232a210b63fdafc5e5e4a2caf45fa5097c6fe -r364a9eda329acd7d20173a4165d71394d3061aae --- tests/parameters.test (.../parameters.test) (revision 770232a210b63fdafc5e5e4a2caf45fa5097c6fe) +++ tests/parameters.test (.../parameters.test) (revision 364a9eda329acd7d20173a4165d71394d3061aae) @@ -1337,6 +1337,141 @@ ? {d1 c 5} 999 } +nx::Test case slot-trace-interaction { + # + # 1) Verify the controlled interactions between trace types + # + # per-object: + # + + package req nx::serializer + + Object create o + + ? {o eval {info exists :a}} 0 + ? {o property {a 0} { + set :defaultcmd {set _ 4} + }} "defaultcmd can't be used together with default value" + + ? {o eval {info exists :a}} 0 + + ? {o eval {info exists :b}} 0 + ? {o property {b 0} { + set :valuecmd {set _ 44} + }} "valuecmd can't be used together with default value" + ? {o eval {info exists :b}} 0 + + ? {o eval {info exists :c}} 0 + ? {o property c { + set :defaultcmd {set _ 4} + set :valuecmd {set _ 44} + }} "valuecmd can't be used together with defaultcmd" + ? {o eval {info exists :c}} 0 + + # + # valuechangedcmd + default value are allowed + # + + ? {o eval {info exists :a}} 0 + o property {a 0} { + set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} + } + ? {o eval {info exists :a}} 1 + + ? {o a} 1 + ? {o a 1} 2 + ? {o a} 2 + ? {o a 2} 3 + + # per-class: + + Class create Klass + + ? {Klass property {a 0} { + set :defaultcmd {set _ 4} + }} "defaultcmd can't be used together with default value" + + ? {Klass property {b 0} { + set :valuecmd {set _ 44} + }} "valuecmd can't be used together with default value" + + ? {Klass property c { + set :defaultcmd {set _ 4} + set :valuecmd {set _ 44} + }} "valuecmd can't be used together with defaultcmd" + + Klass property {a 0} { + set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} + } + + # + # TODO: Right now, the per-object traces *do* fire upon setting the + # default value; the per-class traces won't ... this should be + # symmetric, either way ... + # + + Klass create k + ? {k eval {info exists :a}} 1 + ? {k a} 0; # should be 1, reflecting the corresponding per-object case above + ? {k a 1} 2 + ? {k a} 2 + ? {k a 2} 3 + + # + # 2) Have initcmd scripts escaped from C-level argument checking (in + # the per-class check) + # + # a) against scalar checkers (as a simplistic case) + + Klass property b:boolean { + set :valuechangedcmd {set _ tr1e} + } + + ? {catch {Klass create kk}} 0 + + # + # b) Structured trace scripts, containing lists. Check for + # brace balancedness ... + # + # Background: Script blocks passed as initcmds should not be + # subjected to *any* argument checking. This was not guaranteed, + # previously. As a result, for example, upon multivalued argument + # checking (e.g., 0..n) the argument (i.e., the initcmd block) was + # tentatively expanded into a Tcl list. This failed for initcmd + # scripts which do not qualify as valid list structures (a condition + # not to be enforced). + # + # Below, we introduce three tests capturing the unwanted + # interaction, now fixed. Note: This issue only affects + # class-wide initcmds, as in the per-object case, the initcmds are + # evaluated right away and not fiddled through the parameter handling + # infrastructure. + ::nx::Class create CC { + :property a:0..n { + set :defaultcmd { + if 1 { + set _ 4 + } + } + } + + :property b:0..n {set :valuecmd {if 1 {set _ 44}} } + :property c:0..n { + set :valuechangedcmd { + if 1 { + ::nsf::var::set $obj $var 999 + } + } + } + :create ::cc + } + + ? {cc a} 4 + ? {cc b} 44 + ? {cc c 5} 999 + +} + ::nsf::configure checkarguments off nx::Test case check-arguments-nocheck {