Index: TODO =================================================================== diff -u -rf0f51c24f9e80aa5d0b6a20da9afd37339aab02e -r3e2056578f71e9fb14f5c1ee35a9d626747eb285 --- TODO (.../TODO) (revision f0f51c24f9e80aa5d0b6a20da9afd37339aab02e) +++ TODO (.../TODO) (revision 3e2056578f71e9fb14f5c1ee35a9d626747eb285) @@ -3927,6 +3927,14 @@ correctly. - Extended regression test +nsf.c: +- added object parameter option "slotinitialize" +- renamed object parameter option "invokesetter" -> "slotassign" +- call slot.assign instead of setter of object +- removed restriction on nosetter/invokesetter: nosetter can + be used in connection with slotassign +- added regression test for slot.initialize + ======================================================================== TODO: Index: generic/nsf.c =================================================================== diff -u -rf0f51c24f9e80aa5d0b6a20da9afd37339aab02e -r3e2056578f71e9fb14f5c1ee35a9d626747eb285 --- generic/nsf.c (.../nsf.c) (revision f0f51c24f9e80aa5d0b6a20da9afd37339aab02e) +++ generic/nsf.c (.../nsf.c) (revision 3e2056578f71e9fb14f5c1ee35a9d626747eb285) @@ -11598,12 +11598,18 @@ } else if (strncmp(option, "forward", 7) == 0) { paramPtr->flags |= NSF_ARG_FORWARD; - } else if (strncmp(option, "invokesetter", 12) == 0) { + } else if (strncmp(option, "slotassign", 10) == 0) { if (unlikely(paramPtr->slotObj == NULL)) { - return NsfPrintError(interp, "option 'invokesetter' must follow 'slot='"); + return NsfPrintError(interp, "option 'slotassign' must follow 'slot='"); } - paramPtr->flags |= NSF_ARG_INVOKESETTER; + paramPtr->flags |= NSF_ARG_SLOTASSIGN; + } else if (strncmp(option, "slotinitialize", 14) == 0) { + if (unlikely(paramPtr->slotObj == NULL)) { + return NsfPrintError(interp, "option 'slotinit' must follow 'slot='"); + } + paramPtr->flags |= NSF_ARG_SLOTINITIALIZE; + } else if ((dotdot = strnstr(option, "..", optionLength))) { /* check lower bound */ if (*option == '0') { @@ -21213,6 +21219,19 @@ } */ +static NsfObject* +GetSlotObject(Tcl_Interp *interp, Tcl_Obj *slotObj) { + NsfObject *slotObject = NULL; + + assert(slotObj != NULL); + GetObjectFromObj(interp, slotObj, &slotObject); + if (unlikely(slotObject == NULL)) { + NsfPrintError(interp, "couldn't resolve slot object %s", ObjStr(slotObj)); + } + return slotObject; +} + + static int NsfOConfigureMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { int result, i; @@ -21338,6 +21357,25 @@ &(pc.full_objv[i]));*/ /* + * Handling slot initialize + */ + if (paramPtr->flags & NSF_ARG_SLOTINITIALIZE) { + NsfObject *slotObject = GetSlotObject(interp, paramPtr->slotObj); + + if (likely(slotObject != NULL)) { + result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, NsfGlobalObjs[NSF_INITIALIZE], + object->cmdName, 1, NULL, NSF_CSC_IMMEDIATE); + } + if (result != TCL_OK) { + /* + * The error message was set either by GetSlotObject or by ...CallMethod... + */ + Nsf_PopFrameObj(interp, framePtr); + goto configure_exit; + } + } + + /* * Special setter methods for invoking methods calls; handles types * "initcmd", "alias" and "forward". */ @@ -21579,9 +21617,24 @@ * is typically a forwarder to the slot object. */ - if (paramPtr->flags & NSF_ARG_INVOKESETTER) { - result = NsfCallMethodWithArgs(interp, (Nsf_Object *)object, paramPtr->nameObj, - newValue, 1, NULL, NSF_CSC_IMMEDIATE); + if (paramPtr->flags & NSF_ARG_SLOTASSIGN) { + NsfObject *slotObject = GetSlotObject(interp, paramPtr->slotObj); + + if (likely(slotObject != NULL)) { + Tcl_Obj *ov[2]; + + ov[0] = paramPtr->nameObj; + ov[1] = newValue; + result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, NsfGlobalObjs[NSF_ASSIGN], + object->cmdName, 3, ov, NSF_CSC_IMMEDIATE); + } + if (result != TCL_OK) { + /* + * The error message was set either by GetSlotObject or by ...CallMethod... + */ + Nsf_PopFrameObj(interp, framePtr); + goto configure_exit; + } } else { Tcl_ObjSetVar2(interp, paramPtr->nameObj, NULL, newValue, TCL_LEAVE_ERR_MSG|TCL_PARSE_PART1); } Index: generic/nsfInt.h =================================================================== diff -u -ra6e6e5de115f92c579b867bb88323a9916aec4d4 -r3e2056578f71e9fb14f5c1ee35a9d626747eb285 --- generic/nsfInt.h (.../nsfInt.h) (revision a6e6e5de115f92c579b867bb88323a9916aec4d4) +++ generic/nsfInt.h (.../nsfInt.h) (revision 3e2056578f71e9fb14f5c1ee35a9d626747eb285) @@ -415,17 +415,18 @@ #define NSF_ARG_UNNAMED 0x080000 #define NSF_ARG_IS_RETURNVALUE 0x100000 #define NSF_ARG_NOLEADINGDASH 0x200000 -#define NSF_ARG_INVOKESETTER 0x400000 +#define NSF_ARG_SLOTASSIGN 0x400000 +#define NSF_ARG_SLOTINITIALIZE 0x800000 /* method invocations */ #define NSF_ARG_METHOD_INVOCATION (NSF_ARG_ALIAS|NSF_ARG_FORWARD|NSF_ARG_INITCMD) /* Disallowed parameter options */ -#define NSF_DISALLOWED_ARG_METHOD_PARAMETER (NSF_ARG_METHOD_INVOCATION|NSF_ARG_NOCONFIG|NSF_ARG_INVOKESETTER) +#define NSF_DISALLOWED_ARG_METHOD_PARAMETER (NSF_ARG_METHOD_INVOCATION|NSF_ARG_NOCONFIG|NSF_ARG_SLOTASSIGN|NSF_ARG_SLOTINITIALIZE) #define NSF_DISALLOWED_ARG_SETTER (NSF_ARG_SWITCH|NSF_ARG_SUBST_DEFAULT|NSF_DISALLOWED_ARG_METHOD_PARAMETER) /*#define NSF_DISALLOWED_ARG_OBJECT_PARAMETER (NSF_ARG_SWITCH)*/ #define NSF_DISALLOWED_ARG_OBJECT_PARAMETER 0 -#define NSF_DISALLOWED_ARG_VALUECHECK (NSF_ARG_SUBST_DEFAULT|NSF_ARG_METHOD_INVOCATION|NSF_ARG_SWITCH|NSF_ARG_CURRENTLY_UNKNOWN|NSF_ARG_INVOKESETTER) +#define NSF_DISALLOWED_ARG_VALUECHECK (NSF_ARG_SUBST_DEFAULT|NSF_ARG_METHOD_INVOCATION|NSF_ARG_SWITCH|NSF_ARG_CURRENTLY_UNKNOWN|NSF_ARG_SLOTASSIGN|NSF_ARG_SLOTINITIALIZE) /* flags for ParseContext */ @@ -603,7 +604,7 @@ typedef enum { NSF_EMPTY, NSF_ZERO, NSF_ONE, /* methods called internally */ - NSF_CONFIGURE, + NSF_CONFIGURE, NSF_INITIALIZE, NSF_ASSIGN, /* var names */ NSF_AUTONAMES, NSF_DEFAULTMETACLASS, NSF_DEFAULTSUPERCLASS, NSF_ALIAS_ARRAY, @@ -624,7 +625,7 @@ char *NsfGlobalStrings[] = { "", "0", "1", /* methods called internally */ - "configure", + "configure", "initialize", "assign", /* var names */ "__autonames", "__default_metaclass", "__default_superclass", "::nsf::alias", Index: library/nx/nx.tcl =================================================================== diff -u -rb9c5646036aa10f76ce20f8360a1d8c823ef2355 -r3e2056578f71e9fb14f5c1ee35a9d626747eb285 --- library/nx/nx.tcl (.../nx.tcl) (revision b9c5646036aa10f76ce20f8360a1d8c823ef2355) +++ library/nx/nx.tcl (.../nx.tcl) (revision 3e2056578f71e9fb14f5c1ee35a9d626747eb285) @@ -1538,6 +1538,7 @@ {-forObjectParameter 0} } { set options "" + set slotObject "" if {[info exists :type]} { set type ${:type} if {$type eq "switch" && !$forObjectParameter} {set type boolean} @@ -1553,18 +1554,17 @@ "alnum" "alpha" "ascii" "control" "digit" "double" \ "false" "graph" "lower" "print" "punct" "space" "true" \ "wideinteger" "wordchar" "xdigit" ]} { - lappend options slot=[::nsf::self] + lappend options slot=[::nsf::self] } } } elseif {[:info lookup method assign] ne "::nsf::classes::nx::VariableSlot::assign"} { - # In case the "assign method" has changed, forward variable - # setting in configure (e.g. called during initialization of - # object parameters) to the slot. - if {${:accessor} == 0} { - error "parameter ${:name}: option 'noaccessor' cannot be used together with required accessor (assign method)" - } - lappend options slot=[::nsf::self] invokesetter + # In case the "assign method" was provided, ask nsf to call it directly + lappend options slot=[::nsf::self] slotassign } + if {[:info lookup method initialize] ne "" && $forObjectParameter} { + if {"slot=[::nsf::self]" ni $options} {lappend options slot=[::nsf::self]} + lappend options slotinitialize + } if {[info exists :arg]} {lappend options arg=${:arg}} if {${:required}} { lappend options required Index: tests/parameters.test =================================================================== diff -u -r6a3cd1a111aa693839ec3d788262ce18be74bf91 -r3e2056578f71e9fb14f5c1ee35a9d626747eb285 --- tests/parameters.test (.../parameters.test) (revision 6a3cd1a111aa693839ec3d788262ce18be74bf91) +++ tests/parameters.test (.../parameters.test) (revision 3e2056578f71e9fb14f5c1ee35a9d626747eb285) @@ -2347,14 +2347,17 @@ ? [list $o eval {info exists :baz}] 1 ? {set ::slotcalls} 2 ? [list $o baz] "test" + ? {Foo info method exists baz} 1 } # -# Test incompatible forwarding to slot vs. noaccessor +# Test forwarding to slot vs. noaccessor # nx::Test case forward-to-assign { + set ::slotcalls 0 + ? {nx::Class create Foo { :property bar:noaccessor { :public method assign { object property value } { @@ -2365,7 +2368,8 @@ } "::Foo" # call without default, without object parameter value - ? {Foo new} "parameter bar: option 'noaccessor' cannot be used together with required accessor (assign method)" + ? {catch {Foo new}} 0 + ? {set ::slotcalls} 0 # test cases for default nx::Class create Foo { @@ -2378,8 +2382,34 @@ } # call with default, without object parameter value - ? {Foo new} "parameter baz: option 'noaccessor' cannot be used together with required accessor (assign method)" + ? {catch {Foo new}} 0 + ? {set ::slotcalls} 1 # call with default, with object parameter value - ? {Foo new -baz "test"} "parameter baz: option 'noaccessor' cannot be used together with required accessor (assign method)" -} + ? {catch {Foo new -baz "test"}} 0 + ? {set ::slotcalls} 2 + ? {Foo info method exists baz} 0 +} + + +# +# Test slot initialize +# +nx::Test case forward-to-incremental { + set ::slotcalls 0 + + ? {nx::Class create Foo { + :property bar { + :public method initialize { object } { + incr ::slotcalls 1 + } + }} + } "::Foo" + + # initialize is supposed to be called regardless of some default + ? {catch {Foo new}} 0 + ? {set ::slotcalls} 1 + +} + +