Index: generic/nsf.c =================================================================== diff -u -N -r8591bd1707b8a2a8192cc49477440af7b7a57824 -r25212e40eb097eebcf48d54931bde2bc90e94e69 --- generic/nsf.c (.../nsf.c) (revision 8591bd1707b8a2a8192cc49477440af7b7a57824) +++ generic/nsf.c (.../nsf.c) (revision 25212e40eb097eebcf48d54931bde2bc90e94e69) @@ -4624,7 +4624,7 @@ int i, j; /* - * We have a colonLocalVarCache. + * We have a colonLocalVarCache. * * Search the colonVarCache, which is alphabetically sorted to allow e.g. * termination after O(n/2) on failures. @@ -4645,7 +4645,7 @@ */ if (varName[1] < localName[1]) { break; - + } else if (varName[1] == localName[1]) { int cmp; /* @@ -4655,7 +4655,7 @@ if (len != nameLength) { continue; } - + cmp = strcmp(varName, localName); if (cmp == 0) { result = (Tcl_Var) &varFramePtr->compiledLocals[j]; @@ -4674,7 +4674,7 @@ if (result != NULL) { fprintf(stderr, "... <%s> found -> [%d] %p\n", varName, j, (void *)result); } -#endif +#endif } return result; } @@ -14017,7 +14017,8 @@ #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "method %p/%d '%s' type %p <%s>\n", - methodObj, methodObj->refCount, methodName, methodObj->typePtr, (methodObj->typePtr != NULL) ? methodObj->typePtr->name : ""); + methodObj, methodObj->refCount, methodName, methodObj->typePtr, + (methodObj->typePtr != NULL) ? methodObj->typePtr->name : ""); #endif /*fprintf(stderr, "ObjectDispatch obj = %s objc = %d 0=%s methodName=%s shift %d\n", (object != NULL) ? ObjectName(object) : NULL, @@ -15745,320 +15746,359 @@ unsigned int disallowedOptions, Nsf_Param *paramPtr, int unescape) nonnull(1) nonnull(2) nonnull(6); -static int -ParamOptionParse(Tcl_Interp *interp, const char *argString, - size_t start, size_t optionLength, - unsigned int disallowedOptions, Nsf_Param *paramPtr, int unescape) { - const char *dotdot, *option = argString + start; - char firstChar = *option; - int result = TCL_OK; + static int + ParamOptionParse(Tcl_Interp *interp, const char *argString, + size_t start, size_t optionLength, + unsigned int disallowedOptions, Nsf_Param *paramPtr, int unescape) { + const char *dotdot, *option = argString + start; + char firstChar = *option; + int result = TCL_OK; - nonnull_assert(interp != NULL); - nonnull_assert(argString != NULL); - nonnull_assert(paramPtr != NULL); + nonnull_assert(interp != NULL); + nonnull_assert(argString != NULL); + nonnull_assert(paramPtr != NULL); - /*fprintf(stderr, "ParamOptionParse name %s, option '%s' (%ld) disallowed %.6x\n", - paramPtr->name, option, start, disallowedOptions);*/ + /*fprintf(stderr, "ParamOptionParse name %s, option '%s' (%ld) disallowed %.6x\n", + paramPtr->name, option, start, disallowedOptions);*/ - if (firstChar == 'r' && strncmp(option, "required", MAX(3, optionLength)) == 0) { - paramPtr->flags |= NSF_ARG_REQUIRED; + if (firstChar == 'r' && strncmp(option, "required", MAX(3, optionLength)) == 0) { + paramPtr->flags |= NSF_ARG_REQUIRED; - } else if (firstChar == 'o' && strncmp(option, "optional", MAX(3, optionLength)) == 0) { - paramPtr->flags &= ~NSF_ARG_REQUIRED; + } else if (firstChar == 'o' && strncmp(option, "optional", MAX(3, optionLength)) == 0) { + paramPtr->flags &= ~NSF_ARG_REQUIRED; - } else if (firstChar == 's' && strncmp(option, "substdefault", 12) == 0) { - paramPtr->flags |= NSF_ARG_SUBST_DEFAULT; + } else if (firstChar == 's' + && strncmp(option, "substdefault", 12) == 0 + && *(option+12) != 'o' + ) { + paramPtr->flags |= NSF_ARG_SUBST_DEFAULT; + paramPtr->flags |= NSF_ARG_SUBST_DEFAULT_ALL; - } else if (firstChar == 'c' && strncmp(option, "convert", 7) == 0) { - paramPtr->flags |= NSF_ARG_IS_CONVERTER; + } else if (firstChar == 's' && strncmp(option, "substdefaultoptions=", 20) == 0) { - } else if (firstChar == 'i' && strncmp(option, "initcmd", 7) == 0) { - if (unlikely((paramPtr->flags & (NSF_ARG_CMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD)) != 0u)) { - return NsfPrintError(interp, "parameter option 'initcmd' not valid in this option combination"); - } - paramPtr->flags |= NSF_ARG_INITCMD; + if ((paramPtr->flags & NSF_ARG_SUBST_DEFAULT) == 0u) { + return NsfPrintError(interp, "substdefaultoptions only allowes after substdefault'"); - } else if (firstChar == 'c' && strncmp(option, "cmd", 3) == 0) { - if (unlikely((paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD)) != 0u)) { - return NsfPrintError(interp, "parameter option 'cmd' not valid in this option combination"); - } - paramPtr->flags |= NSF_ARG_CMD; + } else { + int result; + Tcl_Obj *ov[2]; - } else if (firstChar == 'a' && strncmp(option, "alias", 5) == 0) { - if (unlikely((paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD|NSF_ARG_FORWARD)) != 0u)) { - return NsfPrintError(interp, "parameter option 'alias' not valid in this option combination"); - } - paramPtr->flags |= NSF_ARG_ALIAS; + ov[0] = NULL; + ov[1] = Tcl_NewStringObj(option + 20, (int)optionLength - 20); + INCR_REF_COUNT(ov[1]); + result = Nsf_ExprObjCmd(NULL, interp, 2, ov); + DECR_REF_COUNT(ov[1]); - } else if (firstChar == 'f' && strncmp(option, "forward", 7) == 0) { - if (unlikely((paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD|NSF_ARG_ALIAS)) != 0u)) { - return NsfPrintError(interp, "parameter option 'forward' not valid in this option combination"); - } - paramPtr->flags |= NSF_ARG_FORWARD; + if (result == TCL_OK) { + int value = 0; + Tcl_Obj *resultObj = Tcl_GetObjResult(interp); - } else if (firstChar == 's' && strncmp(option, "slotset", 7) == 0) { - if (unlikely(paramPtr->slotObj == NULL)) { - return NsfPrintError(interp, "parameter option 'slotset' must follow 'slot='"); - } - paramPtr->flags |= NSF_ARG_SLOTSET; + if ((Tcl_GetIntFromObj(interp, resultObj, &value) != TCL_OK) + || (value < 0) || (value > 7) + ) { + return NsfPrintError(interp, "parameter option 'substdefaultoptions=' must be a value between 0b000 and 0b111"); + } + /* + * Clear old flags + */ + paramPtr->flags &= ~(NSF_ARG_SUBST_DEFAULT_ALL); + /* + * Set new flags. The value is placed in the last 4 bit of a 32bit entity + */ + paramPtr->flags |= (value << 28); + } + } - } else if (firstChar == 's' && strncmp(option, "slotinitialize", 14) == 0) { - if (unlikely(paramPtr->slotObj == NULL)) { - return NsfPrintError(interp, "parameter option 'slotinit' must follow 'slot='"); - } - paramPtr->flags |= NSF_ARG_SLOTINITIALIZE; + } else if (firstChar == 'c' && strncmp(option, "convert", 7) == 0) { + paramPtr->flags |= NSF_ARG_IS_CONVERTER; - } else if ((dotdot = strnstr(option, "..", optionLength-1))) { - /* check lower bound */ - if (*option == '0') { - paramPtr->flags |= NSF_ARG_ALLOW_EMPTY; - } else if (unlikely(*option != '1')) { - return NsfPrintError(interp, "lower bound of multiplicity in %s not supported", argString); - } - /* check upper bound */ - option = dotdot + 2; - if (*option == '*' || *option == 'n') { - if (unlikely((paramPtr->flags & (NSF_ARG_SWITCH)) != 0u)) { - return NsfPrintError(interp, - "upper bound of multiplicity of '%c' not allowed for \"switch\"\n", *option); - } - paramPtr->flags |= NSF_ARG_MULTIVALUED; - } else if (*option != '1') { - return NsfPrintError(interp, "upper bound of multiplicity in %s not supported", argString); - } + } else if (firstChar == 'i' && strncmp(option, "initcmd", 7) == 0) { + if (unlikely((paramPtr->flags & (NSF_ARG_CMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD)) != 0u)) { + return NsfPrintError(interp, "parameter option 'initcmd' not valid in this option combination"); + } + paramPtr->flags |= NSF_ARG_INITCMD; - } else if (firstChar == 'n' && strncmp(option, "noarg", 5) == 0) { - if ((paramPtr->flags & NSF_ARG_ALIAS) == 0u) { - return NsfPrintError(interp, "parameter option \"noarg\" only allowed for parameter type \"alias\""); - } - paramPtr->flags |= NSF_ARG_NOARG; - paramPtr->nrArgs = 0; + } else if (firstChar == 'c' && strncmp(option, "cmd", 3) == 0) { + if (unlikely((paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD)) != 0u)) { + return NsfPrintError(interp, "parameter option 'cmd' not valid in this option combination"); + } + paramPtr->flags |= NSF_ARG_CMD; - } else if (firstChar == 'n' && strncmp(option, "nodashalnum", 11) == 0) { - if (*paramPtr->name == '-') { - return NsfPrintError(interp, "parameter option 'nodashalnum' only allowed for positional parameters"); - } - paramPtr->flags |= NSF_ARG_NODASHALNUM; + } else if (firstChar == 'a' && strncmp(option, "alias", 5) == 0) { + if (unlikely((paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD|NSF_ARG_FORWARD)) != 0u)) { + return NsfPrintError(interp, "parameter option 'alias' not valid in this option combination"); + } + paramPtr->flags |= NSF_ARG_ALIAS; - } else if (firstChar == 'n' && strncmp(option, "noconfig", 8) == 0) { - if (disallowedOptions != NSF_DISALLOWED_ARG_OBJECT_PARAMETER) { - return NsfPrintError(interp, "parameter option 'noconfig' only allowed for object parameters"); - } - paramPtr->flags |= NSF_ARG_NOCONFIG; + } else if (firstChar == 'f' && strncmp(option, "forward", 7) == 0) { + if (unlikely((paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD|NSF_ARG_ALIAS)) != 0u)) { + return NsfPrintError(interp, "parameter option 'forward' not valid in this option combination"); + } + paramPtr->flags |= NSF_ARG_FORWARD; - } else if (firstChar == 'a' && strncmp(option, "args", 4) == 0) { - if ((paramPtr->flags & NSF_ARG_ALIAS) == 0u) { - return NsfPrintError(interp, "parameter option \"args\" only allowed for parameter type \"alias\""); - } - result = ParamOptionSetConverter(interp, paramPtr, "args", ConvertToNothing); + } else if (firstChar == 's' && strncmp(option, "slotset", 7) == 0) { + if (unlikely(paramPtr->slotObj == NULL)) { + return NsfPrintError(interp, "parameter option 'slotset' must follow 'slot='"); + } + paramPtr->flags |= NSF_ARG_SLOTSET; - } else if (firstChar == 'a' && optionLength >= 4 && strncmp(option, "arg=", 4) == 0) { - if (paramPtr->converter != ConvertViaCmd) { - return NsfPrintError(interp, - "parameter option 'arg=' only allowed for user-defined converter"); - } - if (paramPtr->converterArg != NULL) { - DECR_REF_COUNT(paramPtr->converterArg); - } - paramPtr->converterArg = Tcl_NewStringObj(option + 4, (int)optionLength - 4); - /* - * In case, we know that we have to unescape double commas, do it here... - */ - if (unlikely(unescape)) { - Unescape(paramPtr->converterArg); - } - INCR_REF_COUNT(paramPtr->converterArg); + } else if (firstChar == 's' && strncmp(option, "slotinitialize", 14) == 0) { + if (unlikely(paramPtr->slotObj == NULL)) { + return NsfPrintError(interp, "parameter option 'slotinit' must follow 'slot='"); + } + paramPtr->flags |= NSF_ARG_SLOTINITIALIZE; - } else if (firstChar == 's' && strncmp(option, "switch", 6) == 0) { - if (*paramPtr->name != '-') { - return NsfPrintError(interp, - "invalid parameter type \"switch\" for argument \"%s\"; " - "type \"switch\" only allowed for non-positional arguments", - paramPtr->name); - } else if ((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) != 0u) { - return NsfPrintError(interp, "parameter invocation types cannot be used with option 'switch'"); - } - result = ParamOptionSetConverter(interp, paramPtr, "switch", Nsf_ConvertToSwitch); - paramPtr->flags |= NSF_ARG_SWITCH; - paramPtr->nrArgs = 0; - assert(paramPtr->defaultValue == NULL); - paramPtr->defaultValue = Tcl_NewBooleanObj(0); - INCR_REF_COUNT(paramPtr->defaultValue); + } else if ((dotdot = strnstr(option, "..", optionLength-1))) { + /* check lower bound */ + if (*option == '0') { + paramPtr->flags |= NSF_ARG_ALLOW_EMPTY; + } else if (unlikely(*option != '1')) { + return NsfPrintError(interp, "lower bound of multiplicity in %s not supported", argString); + } + /* check upper bound */ + option = dotdot + 2; + if (*option == '*' || *option == 'n') { + if (unlikely((paramPtr->flags & (NSF_ARG_SWITCH)) != 0u)) { + return NsfPrintError(interp, + "upper bound of multiplicity of '%c' not allowed for \"switch\"\n", *option); + } + paramPtr->flags |= NSF_ARG_MULTIVALUED; + } else if (*option != '1') { + return NsfPrintError(interp, "upper bound of multiplicity in %s not supported", argString); + } - } else if (firstChar == 'i' && strncmp(option, "integer", MAX(3, optionLength)) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "integer", Nsf_ConvertToInteger); + } else if (firstChar == 'n' && strncmp(option, "noarg", 5) == 0) { + if ((paramPtr->flags & NSF_ARG_ALIAS) == 0u) { + return NsfPrintError(interp, "parameter option \"noarg\" only allowed for parameter type \"alias\""); + } + paramPtr->flags |= NSF_ARG_NOARG; + paramPtr->nrArgs = 0; - } else if (firstChar == 'i' && strncmp(option, "int32", 5) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "int32", Nsf_ConvertToInt32); + } else if (firstChar == 'n' && strncmp(option, "nodashalnum", 11) == 0) { + if (*paramPtr->name == '-') { + return NsfPrintError(interp, "parameter option 'nodashalnum' only allowed for positional parameters"); + } + paramPtr->flags |= NSF_ARG_NODASHALNUM; - } else if (firstChar == 'b' && strncmp(option, "boolean", 7) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "boolean", Nsf_ConvertToBoolean); + } else if (firstChar == 'n' && strncmp(option, "noconfig", 8) == 0) { + if (disallowedOptions != NSF_DISALLOWED_ARG_OBJECT_PARAMETER) { + return NsfPrintError(interp, "parameter option 'noconfig' only allowed for object parameters"); + } + paramPtr->flags |= NSF_ARG_NOCONFIG; - } else if (firstChar == 'o' && strncmp(option, "object", 6) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "object", Nsf_ConvertToObject); + } else if (firstChar == 'a' && strncmp(option, "args", 4) == 0) { + if ((paramPtr->flags & NSF_ARG_ALIAS) == 0u) { + return NsfPrintError(interp, "parameter option \"args\" only allowed for parameter type \"alias\""); + } + result = ParamOptionSetConverter(interp, paramPtr, "args", ConvertToNothing); - } else if (firstChar == 'c' && strncmp(option, "class", 5) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); + } else if (firstChar == 'a' && optionLength >= 4 && strncmp(option, "arg=", 4) == 0) { + if (paramPtr->converter != ConvertViaCmd) { + return NsfPrintError(interp, + "parameter option 'arg=' only allowed for user-defined converter"); + } + if (paramPtr->converterArg != NULL) { + DECR_REF_COUNT(paramPtr->converterArg); + } + paramPtr->converterArg = Tcl_NewStringObj(option + 4, (int)optionLength - 4); + /* + * In case, we know that we have to unescape double commas, do it here... + */ + if (unlikely(unescape)) { + Unescape(paramPtr->converterArg); + } + INCR_REF_COUNT(paramPtr->converterArg); - } else if (firstChar == 'm' && strncmp(option, "metaclass", 9) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); - paramPtr->flags |= NSF_ARG_METACLASS; + } else if (firstChar == 's' && strncmp(option, "switch", 6) == 0) { + if (*paramPtr->name != '-') { + return NsfPrintError(interp, + "invalid parameter type \"switch\" for argument \"%s\"; " + "type \"switch\" only allowed for non-positional arguments", + paramPtr->name); + } else if ((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) != 0u) { + return NsfPrintError(interp, "parameter invocation types cannot be used with option 'switch'"); + } + result = ParamOptionSetConverter(interp, paramPtr, "switch", Nsf_ConvertToSwitch); + paramPtr->flags |= NSF_ARG_SWITCH; + paramPtr->nrArgs = 0; + assert(paramPtr->defaultValue == NULL); + paramPtr->defaultValue = Tcl_NewBooleanObj(0); + INCR_REF_COUNT(paramPtr->defaultValue); - } else if (firstChar == 'b' && strncmp(option, "baseclass", 9) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); - paramPtr->flags |= NSF_ARG_BASECLASS; + } else if (firstChar == 'i' && strncmp(option, "integer", MAX(3, optionLength)) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "integer", Nsf_ConvertToInteger); - } else if (firstChar == 'm' && strncmp(option, "mixinreg", 8) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "mixinreg", Nsf_ConvertToMixinreg); + } else if (firstChar == 'i' && strncmp(option, "int32", 5) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "int32", Nsf_ConvertToInt32); - } else if (firstChar == 'f' && strncmp(option, "filterreg", 9) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "filterreg", Nsf_ConvertToFilterreg); + } else if (firstChar == 'b' && strncmp(option, "boolean", 7) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "boolean", Nsf_ConvertToBoolean); - } else if (firstChar == 'p' && strncmp(option, "parameter", 9) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "parameter", Nsf_ConvertToParameter); + } else if (firstChar == 'o' && strncmp(option, "object", 6) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "object", Nsf_ConvertToObject); - } else if (firstChar == 't' && optionLength >= 6 && strncmp(option, "type=", 5) == 0) { - if (paramPtr->converter != Nsf_ConvertToObject - && paramPtr->converter != Nsf_ConvertToClass ) { - return NsfPrintError(interp, "parameter option 'type=' only allowed for parameter types 'object' and 'class'"); - } - if (paramPtr->converterArg != NULL) { - DECR_REF_COUNT(paramPtr->converterArg); - } - paramPtr->converterArg = Tcl_NewStringObj(option + 5, (int)optionLength - 5); - if (unlikely(unescape)) { - Unescape(paramPtr->converterArg); - } - INCR_REF_COUNT(paramPtr->converterArg); + } else if (firstChar == 'c' && strncmp(option, "class", 5) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); - } else if (firstChar == 's' && optionLength >= 6 && strncmp(option, "slot=", 5) == 0) { - if (paramPtr->slotObj != NULL) {DECR_REF_COUNT(paramPtr->slotObj);} - paramPtr->slotObj = Tcl_NewStringObj(option + 5, (int)optionLength - 5); - if (unlikely(unescape)) { - Unescape(paramPtr->slotObj); - } - INCR_REF_COUNT(paramPtr->slotObj); + } else if (firstChar == 'm' && strncmp(option, "metaclass", 9) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); + paramPtr->flags |= NSF_ARG_METACLASS; - } else if (firstChar == 'm' && optionLength >= 6 && strncmp(option, "method=", 7) == 0) { - if ((paramPtr->flags & (NSF_ARG_ALIAS|NSF_ARG_FORWARD|NSF_ARG_SLOTSET)) == 0u) { - return NsfPrintError(interp, "parameter option 'method=' only allowed for parameter " - "types 'alias', 'forward' and 'slotset'"); - } - if (paramPtr->method != NULL) {DECR_REF_COUNT(paramPtr->method);} - paramPtr->method = Tcl_NewStringObj(option + 7, (int)optionLength - 7); - if (unlikely(unescape)) { - Unescape(paramPtr->method); - } - INCR_REF_COUNT(paramPtr->method); + } else if (firstChar == 'b' && strncmp(option, "baseclass", 9) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); + paramPtr->flags |= NSF_ARG_BASECLASS; - } else if ((firstChar == 'v') && - ((strncmp(option, "virtualobjectargs", 17) == 0) || - (strncmp(option, "virtualclassargs", 16) == 0))) { - result = ParamOptionSetConverter(interp, paramPtr, option, ConvertToNothing); - } else { - Tcl_DString ds, *dsPtr = &ds; + } else if (firstChar == 'm' && strncmp(option, "mixinreg", 8) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "mixinreg", Nsf_ConvertToMixinreg); - if (option[0] == '\0') { - NsfLog(interp, NSF_LOG_WARN, "empty parameter option ignored"); - return TCL_OK; - } + } else if (firstChar == 'f' && strncmp(option, "filterreg", 9) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "filterreg", Nsf_ConvertToFilterreg); - Tcl_DStringInit(dsPtr); - Tcl_DStringAppend(dsPtr, option, (int)optionLength); + } else if (firstChar == 'p' && strncmp(option, "parameter", 9) == 0) { + result = ParamOptionSetConverter(interp, paramPtr, "parameter", Nsf_ConvertToParameter); - if (unlikely(paramPtr->converter != NULL)) { - NsfPrintError(interp, "parameter option '%s' unknown for parameter type '%s'", - Tcl_DStringValue(dsPtr), paramPtr->type); - Tcl_DStringFree(dsPtr); - return TCL_ERROR; - } + } else if (firstChar == 't' && optionLength >= 6 && strncmp(option, "type=", 5) == 0) { + if (paramPtr->converter != Nsf_ConvertToObject + && paramPtr->converter != Nsf_ConvertToClass ) { + return NsfPrintError(interp, "parameter option 'type=' only allowed for parameter types 'object' and 'class'"); + } + if (paramPtr->converterArg != NULL) { + DECR_REF_COUNT(paramPtr->converterArg); + } + paramPtr->converterArg = Tcl_NewStringObj(option + 5, (int)optionLength - 5); + if (unlikely(unescape)) { + Unescape(paramPtr->converterArg); + } + INCR_REF_COUNT(paramPtr->converterArg); - if (Nsf_PointerTypeLookup(interp, Tcl_DStringValue(dsPtr))) { - /* - * Check, if the option refers to a pointer converter - */ - ParamOptionSetConverter(interp, paramPtr, Tcl_DStringValue(dsPtr), Nsf_ConvertToPointer); - Tcl_DStringFree(dsPtr); + } else if (firstChar == 's' && optionLength >= 6 && strncmp(option, "slot=", 5) == 0) { + if (paramPtr->slotObj != NULL) {DECR_REF_COUNT(paramPtr->slotObj);} + paramPtr->slotObj = Tcl_NewStringObj(option + 5, (int)optionLength - 5); + if (unlikely(unescape)) { + Unescape(paramPtr->slotObj); + } + INCR_REF_COUNT(paramPtr->slotObj); - } else { - int i, found = -1; + } else if (firstChar == 'm' && optionLength >= 6 && strncmp(option, "method=", 7) == 0) { + if ((paramPtr->flags & (NSF_ARG_ALIAS|NSF_ARG_FORWARD|NSF_ARG_SLOTSET)) == 0u) { + return NsfPrintError(interp, "parameter option 'method=' only allowed for parameter " + "types 'alias', 'forward' and 'slotset'"); + } + if (paramPtr->method != NULL) {DECR_REF_COUNT(paramPtr->method);} + paramPtr->method = Tcl_NewStringObj(option + 7, (int)optionLength - 7); + if (unlikely(unescape)) { + Unescape(paramPtr->method); + } + INCR_REF_COUNT(paramPtr->method); - /* - * The option is still unknown, check the Tcl string-is checkers - */ - Tcl_DStringFree(dsPtr); + } else if ((firstChar == 'v') && + ((strncmp(option, "virtualobjectargs", 17) == 0) || + (strncmp(option, "virtualclassargs", 16) == 0))) { + result = ParamOptionSetConverter(interp, paramPtr, option, ConvertToNothing); + } else { + Tcl_DString ds, *dsPtr = &ds; - for (i = 0; stringTypeOpts[i]; i++) { - /* - * Do not allow abbreviations, so the additional strlen checks - * for a full match - */ - if (strncmp(option, stringTypeOpts[i], optionLength) == 0 - && strlen(stringTypeOpts[i]) == optionLength) { - found = i; - break; - } - } + if (option[0] == '\0') { + NsfLog(interp, NSF_LOG_WARN, "empty parameter option ignored"); + return TCL_OK; + } - if (found > -1) { - /* converter is stringType */ - result = ParamOptionSetConverter(interp, paramPtr, "stringtype", Nsf_ConvertToTclobj); - if (paramPtr->converterArg != NULL) { - DECR_REF_COUNT(paramPtr->converterArg); - } - paramPtr->converterArg = Tcl_NewStringObj(stringTypeOpts[i], -1); - INCR_REF_COUNT(paramPtr->converterArg); - } else { + Tcl_DStringInit(dsPtr); + Tcl_DStringAppend(dsPtr, option, (int)optionLength); - /* - * The parameter option is still unknown. We assume that the parameter - * option identifies a user-defined argument checker, implemented as a - * method. - */ - if (paramPtr->converterName != NULL) { - DECR_REF_COUNT2("converterNameObj", paramPtr->converterName); - } - paramPtr->converterName = ParamCheckObj(option, optionLength); - INCR_REF_COUNT2("converterNameObj", paramPtr->converterName); - result = ParamOptionSetConverter(interp, paramPtr, ObjStr(paramPtr->converterName), ConvertViaCmd); - } - } - } + if (unlikely(paramPtr->converter != NULL)) { + NsfPrintError(interp, "parameter option '%s' unknown for parameter type '%s'", + Tcl_DStringValue(dsPtr), paramPtr->type); + Tcl_DStringFree(dsPtr); + return TCL_ERROR; + } - if ((paramPtr->flags & disallowedOptions) != 0u) { - return NsfPrintError(interp, "parameter option '%s' not allowed", option); - } + if (Nsf_PointerTypeLookup(interp, Tcl_DStringValue(dsPtr))) { + /* + * Check, if the option refers to a pointer converter + */ + ParamOptionSetConverter(interp, paramPtr, Tcl_DStringValue(dsPtr), Nsf_ConvertToPointer); + Tcl_DStringFree(dsPtr); - if (unlikely(((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) != 0u) - && ((paramPtr->flags & NSF_ARG_NOCONFIG)) != 0u)) { - return NsfPrintError(interp, "parameter option 'noconfig' cannot used together with this type of object parameter"); - } + } else { + int i, found = -1; - return result; -} + /* + * The option is still unknown, check the Tcl string-is checkers + */ + Tcl_DStringFree(dsPtr); -/* - *---------------------------------------------------------------------- - * ParamParse -- - * - * Parse a a single parameter with a possible default provided in the form - * of an Tcl_Obj. - * - * Results: - * Tcl result code - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ + for (i = 0; stringTypeOpts[i]; i++) { + /* + * Do not allow abbreviations, so the additional strlen checks + * for a full match + */ + if (strncmp(option, stringTypeOpts[i], optionLength) == 0 + && strlen(stringTypeOpts[i]) == optionLength) { + found = i; + break; + } + } -static int ParamParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *arg, unsigned int disallowedFlags, - Nsf_Param *paramPtr, int *possibleUnknowns, int *plainParams, int *nrNonposArgs) - nonnull(1) nonnull(3) nonnull(5) nonnull(6) nonnull(7) nonnull(8); + if (found > -1) { + /* converter is stringType */ + result = ParamOptionSetConverter(interp, paramPtr, "stringtype", Nsf_ConvertToTclobj); + if (paramPtr->converterArg != NULL) { + DECR_REF_COUNT(paramPtr->converterArg); + } + paramPtr->converterArg = Tcl_NewStringObj(stringTypeOpts[i], -1); + INCR_REF_COUNT(paramPtr->converterArg); + } else { + /* + * The parameter option is still unknown. We assume that the parameter + * option identifies a user-defined argument checker, implemented as a + * method. + */ + if (paramPtr->converterName != NULL) { + DECR_REF_COUNT2("converterNameObj", paramPtr->converterName); + } + paramPtr->converterName = ParamCheckObj(option, optionLength); + INCR_REF_COUNT2("converterNameObj", paramPtr->converterName); + result = ParamOptionSetConverter(interp, paramPtr, ObjStr(paramPtr->converterName), ConvertViaCmd); + } + } + } + + if ((paramPtr->flags & disallowedOptions) != 0u) { + return NsfPrintError(interp, "parameter option '%s' not allowed", option); + } + + if (unlikely(((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) != 0u) + && ((paramPtr->flags & NSF_ARG_NOCONFIG)) != 0u)) { + return NsfPrintError(interp, "parameter option 'noconfig' cannot used together with this type of object parameter"); + } + + return result; + } + + /* + *---------------------------------------------------------------------- + * ParamDefinitionParse -- + * + * Parse a a single parameter definiton with a possible default provided in + * the form of an Tcl_Obj. + * + * Results: + * Tcl result code + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + + static int ParamDefinitionParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *arg, unsigned int disallowedFlags, + Nsf_Param *paramPtr, int *possibleUnknowns, int *plainParams, int *nrNonposArgs) + nonnull(1) nonnull(3) nonnull(5) nonnull(6) nonnull(7) nonnull(8); + static int -ParamParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *arg, unsigned int disallowedFlags, +ParamDefinitionParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *arg, unsigned int disallowedFlags, Nsf_Param *paramPtr, int *possibleUnknowns, int *plainParams, int *nrNonposArgs) { const char *argString, *argName; int result, npac, isNonposArgument, parensCount; @@ -16079,7 +16119,7 @@ if (unlikely(result != TCL_OK || npac < 1 || npac > 2)) { if (procNameObj != NULL) { result = NsfPrintError(interp, - "wrong # of elements in parameter definition " + "wrong # of elements in parameter definition " "of method '%s'. " "Should be a list of 1 or 2 elements, but got: '$s'", ObjStr(procNameObj), ObjStr(paramPtr->paramObj)); @@ -16181,7 +16221,9 @@ } } else { - /* no ':', the whole arg is the name, we have no options */ + /* + * No ':', the whole arg is the name, we have no options + */ STRING_NEW(paramPtr->name, argString, length); if (isNonposArgument != 0) { paramPtr->nameObj = Tcl_NewStringObj(argName, (int)length-1); @@ -16192,7 +16234,9 @@ INCR_REF_COUNT(paramPtr->nameObj); } - /* if we have two arguments in the list, the second one is a default value */ + /* + * If we have two arguments in the list, the second one is a default value + */ if (npac == 2) { if ((disallowedFlags & NSF_ARG_HAS_DEFAULT) != 0u) { @@ -16201,7 +16245,9 @@ goto param_error; } - /* if we have for some reason already a default value, free it */ + /* + * If we have for some reason already a default value, free it + */ if (paramPtr->defaultValue != NULL) { DECR_REF_COUNT(paramPtr->defaultValue); } @@ -16398,7 +16444,7 @@ paramPtr = paramsPtr = ParamsNew((size_t)argsc); for (i = 0; i < argsc; i++, paramPtr++) { - result = ParamParse(interp, procNameObj, argsv[i], allowedOptions, + result = ParamDefinitionParse(interp, procNameObj, argsv[i], allowedOptions, paramPtr, &possibleUnknowns, &plainParams, &nrNonposArgs); if (result == TCL_OK && paramPtr->converter == ConvertToNothing && i < argsc-1) { @@ -22203,7 +22249,8 @@ for (pPtr = ifd, i = 0; i < nrParams; pPtr++, i++) { /*fprintf(stderr, "ArgumentDefaults got for arg %s (req %d, nrArgs %d) %p => %p %p, default '%s' \n", pPtr->name, pPtr->flags & NSF_ARG_REQUIRED, pPtr->nrArgs, pPtr, - pcPtr->clientData[i], pcPtr->objv[i], (pPtr->defaultValue != NULL) ? ObjStr(pPtr->defaultValue) : "NONE");*/ + pcPtr->clientData[i], pcPtr->objv[i], + (pPtr->defaultValue != NULL) ? ObjStr(pPtr->defaultValue) : "NONE");*/ if (pcPtr->objv[i] != NULL) { /* @@ -22245,8 +22292,21 @@ * Does the user want to substitute in the default value? */ if (unlikely((pPtr->flags & NSF_ARG_SUBST_DEFAULT) != 0u)) { - Tcl_Obj *obj = Tcl_SubstObj(interp, newValue, TCL_SUBST_ALL); + int tclOptions = 0; + Tcl_Obj *obj; + if ((pPtr->flags & NSF_ARG_SUBST_DEFAULT_VARIABLES) != 0u) { + tclOptions |= TCL_SUBST_VARIABLES; + } + if ((pPtr->flags & NSF_ARG_SUBST_DEFAULT_COMMANDS) != 0u) { + tclOptions |= TCL_SUBST_COMMANDS; + } + if ((pPtr->flags & NSF_ARG_SUBST_DEFAULT_BACKSLASHES) != 0u) { + tclOptions |= TCL_SUBST_BACKSLASHES; + } + /* fprintf(stderr, "SUBST tclOptions %.4x\n", tclOptions);*/ + obj = Tcl_SubstObj(interp, newValue, tclOptions); + if (likely(obj != NULL)) { newValue = obj; } else { @@ -22321,7 +22381,9 @@ INCR_REF_COUNT2("methodPathObj", methodPathObj); - NsfPrintError(interp, "required argument '%s' is missing, should be:\n\t%s%s%s %s", (pPtr->nameObj != NULL) ? ObjStr(pPtr->nameObj) : pPtr->name, (pcPtr->object != NULL) ? ObjectName(pcPtr->object) : "", (pcPtr->object != NULL) ? " " : "", + NsfPrintError(interp, "required argument '%s' is missing, should be:\n\t%s%s%s %s", + (pPtr->nameObj != NULL) ? ObjStr(pPtr->nameObj) : pPtr->name, + (pcPtr->object != NULL) ? ObjectName(pcPtr->object) : "", (pcPtr->object != NULL) ? " " : "", ObjStr(methodPathObj), ObjStr(paramDefsObj)); @@ -26031,7 +26093,7 @@ int result, objc; result = ParamDefsParse(interp, NsfGlobalObjs[NSF_PARSE_ARGS], argspecObj, - NSF_DISALLOWED_ARG_METHOD_PARAMETER, 1 /* force use of param structure, + NSF_DISALLOWED_ARG_METHOD_PARAMETER, 1 /* force use of param structure, even for Tcl-only params */, &parsedParam); @@ -26843,7 +26905,7 @@ int rc, possibleUnknowns = 0, plainParams = 0, nrNonposArgs = 0; setterClientData->paramsPtr = ParamsNew(1u); - rc = ParamParse(interp, NsfGlobalObjs[NSF_SETTER], parameter, + rc = ParamDefinitionParse(interp, NsfGlobalObjs[NSF_SETTER], parameter, NSF_DISALLOWED_ARG_SETTER|NSF_ARG_HAS_DEFAULT, setterClientData->paramsPtr, &possibleUnknowns, &plainParams, &nrNonposArgs); @@ -28634,7 +28696,7 @@ * * Convert the second argument argument (e.g. "x:integer") into the * internal representation of a Tcl_Obj of the type parameter. The - * conversion is performed by the usual ParamParse() function, used + * conversion is performed by the usual ParamDefinitionParse() function, used * e.g. for the parameter passing for arguments. * * Results: @@ -28667,7 +28729,7 @@ Tcl_AppendLimitedToObj(fullParamObj, ObjStr(objPtr), -1, INT_MAX, NULL); INCR_REF_COUNT(fullParamObj); - result = ParamParse(interp, NsfGlobalObjs[NSF_VALUECHECK], fullParamObj, + result = ParamDefinitionParse(interp, NsfGlobalObjs[NSF_VALUECHECK], fullParamObj, (allowParameter == 1) ? NSF_DISALLOWED_ARG_OBJECT_PARAMETER : NSF_DISALLOWED_ARG_VALUECHECK, paramWrapperPtr->paramPtr, &possibleUnknowns, &plainParams, &nrNonposArgs); @@ -29377,10 +29439,10 @@ ov[0] = (paramPtr->method != NULL) ? paramPtr->method : paramPtr->nameObj; ov[1] = newValue; - /*fprintf(stderr, "SLOTSET %s %s %s %s %s idx %d %p\n", ObjectName(slotObject), + fprintf(stderr, "SLOTSET %s %s %s %s %s idx %d %p\n", ObjectName(slotObject), ObjStr(NsfGlobalObjs[NSF_SET]), ObjStr(object->cmdName), ObjStr(paramPtr->nameObj), ObjStr(newValue), - NSF_s_set_idx, methodObj);*/ + NSF_s_set_idx, methodObj); result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, (methodObj != NULL) ? methodObj : NsfGlobalObjs[NSF_SLOT_SET], object->cmdName, 3, ov, NSF_CSC_IMMEDIATE); @@ -29531,12 +29593,13 @@ if (uplevelVarFramePtr != NULL) { Tcl_Interp_varFramePtr(interp) = uplevelVarFramePtr; } - ov[0] = (paramPtr->method != NULL) ? paramPtr->method : paramPtr->nameObj; + ov[0] = (paramPtr->method != NULL) ? paramPtr->method : paramPtr->nameObj; - /*fprintf(stderr, "SLOTGET %s idx %d %p\n", ObjectName(slotObject), - NSF_s_get_idx, methodObj);*/ + /*fprintf(stderr, "SLOTGET %s idx %d %p method %s\n", ObjectName(slotObject), + NSF_s_get_idx, (void *)methodObj, ObjStr(ov[0]));*/ - result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, (methodObj != NULL) ? methodObj : NsfGlobalObjs[NSF_SLOT_GET], + result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, + (methodObj != NULL) ? methodObj : NsfGlobalObjs[NSF_SLOT_GET], object->cmdName, 2, ov, NSF_CSC_IMMEDIATE); goto cget_exit; } Index: generic/nsf.h =================================================================== diff -u -N -r37c87756387c17e0f2f43634c0d452a91f91c844 -r25212e40eb097eebcf48d54931bde2bc90e94e69 --- generic/nsf.h (.../nsf.h) (revision 37c87756387c17e0f2f43634c0d452a91f91c844) +++ generic/nsf.h (.../nsf.h) (revision 25212e40eb097eebcf48d54931bde2bc90e94e69) @@ -311,18 +311,18 @@ Nsf_ConvertToTclobj, Nsf_ConvertToPointer; typedef struct Nsf_Param { - const char *name; - unsigned int flags; - int nrArgs; + const char *name; + unsigned int flags; + int nrArgs; Nsf_TypeConverter *converter; - Tcl_Obj *converterArg; - Tcl_Obj *defaultValue; - CONST char *type; - Tcl_Obj *nameObj; - Tcl_Obj *converterName; - Tcl_Obj *paramObj; - Tcl_Obj *slotObj; - Tcl_Obj *method; + Tcl_Obj *converterArg; + Tcl_Obj *defaultValue; + CONST char *type; + Tcl_Obj *nameObj; + Tcl_Obj *converterName; + Tcl_Obj *paramObj; + Tcl_Obj *slotObj; + Tcl_Obj *method; } Nsf_Param; /* Argument parse processing flags */ @@ -336,31 +336,35 @@ /* flags for NsfParams */ -#define NSF_ARG_REQUIRED 0x00000001u -#define NSF_ARG_MULTIVALUED 0x00000002u -#define NSF_ARG_NOARG 0x00000004u -#define NSF_ARG_NOCONFIG 0x00000008u -#define NSF_ARG_CURRENTLY_UNKNOWN 0x00000010u -#define NSF_ARG_SUBST_DEFAULT 0x00000020u -#define NSF_ARG_ALLOW_EMPTY 0x00000040u -#define NSF_ARG_INITCMD 0x00000080u -#define NSF_ARG_CMD 0x00000100u -#define NSF_ARG_ALIAS 0x00000200u -#define NSF_ARG_FORWARD 0x00000400u -#define NSF_ARG_SWITCH 0x00000800u -#define NSF_ARG_BASECLASS 0x00001000u -#define NSF_ARG_METACLASS 0x00002000u -#define NSF_ARG_HAS_DEFAULT 0x00004000u -#define NSF_ARG_IS_CONVERTER 0x00008000u -#define NSF_ARG_IS_ENUMERATION 0x00010000u -#define NSF_ARG_CHECK_NONPOS 0x00020000u -#define NSF_ARG_SET 0x00040000u -#define NSF_ARG_WARN 0x00080000u -#define NSF_ARG_UNNAMED 0x00100000u -#define NSF_ARG_IS_RETURNVALUE 0x00200000u -#define NSF_ARG_NODASHALNUM 0x00400000u -#define NSF_ARG_SLOTSET 0x00800000u -#define NSF_ARG_SLOTINITIALIZE 0x01000000u +#define NSF_ARG_REQUIRED 0x00000001u +#define NSF_ARG_MULTIVALUED 0x00000002u +#define NSF_ARG_NOARG 0x00000004u +#define NSF_ARG_NOCONFIG 0x00000008u +#define NSF_ARG_CURRENTLY_UNKNOWN 0x00000010u +#define NSF_ARG_SUBST_DEFAULT 0x00000020u +#define NSF_ARG_ALLOW_EMPTY 0x00000040u +#define NSF_ARG_INITCMD 0x00000080u +#define NSF_ARG_CMD 0x00000100u +#define NSF_ARG_ALIAS 0x00000200u +#define NSF_ARG_FORWARD 0x00000400u +#define NSF_ARG_SWITCH 0x00000800u +#define NSF_ARG_BASECLASS 0x00001000u +#define NSF_ARG_METACLASS 0x00002000u +#define NSF_ARG_HAS_DEFAULT 0x00004000u +#define NSF_ARG_IS_CONVERTER 0x00008000u +#define NSF_ARG_IS_ENUMERATION 0x00010000u +#define NSF_ARG_CHECK_NONPOS 0x00020000u +#define NSF_ARG_SET 0x00040000u +#define NSF_ARG_WARN 0x00080000u +#define NSF_ARG_UNNAMED 0x00100000u +#define NSF_ARG_IS_RETURNVALUE 0x00200000u +#define NSF_ARG_NODASHALNUM 0x00400000u +#define NSF_ARG_SLOTSET 0x00800000u +#define NSF_ARG_SLOTINITIALIZE 0x01000000u +#define NSF_ARG_SUBST_DEFAULT_COMMANDS 0x10000000u +#define NSF_ARG_SUBST_DEFAULT_VARIABLES 0x20000000u +#define NSF_ARG_SUBST_DEFAULT_BACKSLASHES 0x40000000u +#define NSF_ARG_SUBST_DEFAULT_ALL 0x70000000u #undef __GNUC_PREREQ #if defined __GNUC__ && defined __GNUC_MINOR__ Index: library/nx/nx.tcl =================================================================== diff -u -N -r5ac8b0931acfe8d0ef93054dafa03f4501868d31 -r25212e40eb097eebcf48d54931bde2bc90e94e69 --- library/nx/nx.tcl (.../nx.tcl) (revision 5ac8b0931acfe8d0ef93054dafa03f4501868d31) +++ library/nx/nx.tcl (.../nx.tcl) (revision 25212e40eb097eebcf48d54931bde2bc90e94e69) @@ -1450,10 +1450,10 @@ # # Get a full object parmeter specification from slot object # - if {[info exists :parameterSpec]} { - } else { + if {![info exists :parameterSpec]} { set prefix [expr {[info exists :positional] && ${:positional} ? "" : "-"}] set options [:getParameterOptions -withMultiplicity true -forObjectParameter true] + #puts stderr [self]================raw:$options if {[info exists :initblock]} { if {[info exists :default]} { @@ -1486,7 +1486,15 @@ # substdefault is allowed via substdefault slot property. # if {${:substdefault}} { - lappend options substdefault + set substOptPos [lsearch -glob $options substdefaultoptions=* ] + #puts stderr "[self]================ [list lsearch substdefaultoptions=* $options] => $substOptPos" + if {$substOptPos > -1} { + set substOpt [lindex $options $substOptPos] + set options [lreplace $options $substOptPos $substOptPos] + lappend options substdefault $substOpt + } else { + lappend options substdefault + } } set :parameterSpec [list [:namedParameterSpec $prefix ${:name} $options] ${:default}] } else { Index: tests/parameters.test =================================================================== diff -u -N -r6ef0a130b8e62c427f66603505b8d1bffe35d12f -r25212e40eb097eebcf48d54931bde2bc90e94e69 --- tests/parameters.test (.../parameters.test) (revision 6ef0a130b8e62c427f66603505b8d1bffe35d12f) +++ tests/parameters.test (.../parameters.test) (revision 25212e40eb097eebcf48d54931bde2bc90e94e69) @@ -924,7 +924,7 @@ return $x } } - ? {s1 foo} 1 + ? {s1 foo} 1 ? {s1 foo 2} 2 ? {S object method foo {a:substdefault} {return 1}} \ @@ -956,7 +956,7 @@ ? {s1 foo} {can't read "aaa": no such variable} ? {s1 public object method foo {{a:substdefault [current]}} {return $a}} ::s1::foo - ? {s1 foo} ::s1 + ? {s1 foo} ::s1 "final test" } ####################################################### @@ -979,7 +979,7 @@ :property -incremental {s3:substdefault "[current]"} } - Bar create ::b + ? {Bar create ::b} ::b "create object" ? {b cget -s0} {[current]} ? {b cget -s1} "::b" ? {b cget -s2} "::b" Index: tests/substdefault.test =================================================================== diff -u -N --- tests/substdefault.test (revision 0) +++ tests/substdefault.test (revision 25212e40eb097eebcf48d54931bde2bc90e94e69) @@ -0,0 +1,208 @@ +# -*- tcl -*- +package req nx::test + +# +# The state of "substdefault" in object and method parameters: +# +# - general +# * [self] is always set correctly +# +# - object-parameter: +# +# * class-defined properties: +# +# - When referring to instance variables, the user has no control +# over the creation order of the variables. So, we cannot +# recommend this praxis. Referring to global or namespaced +# variables, this problem does not exist. +# +# - [current class] executed directly in a subtdefault does not +# return currently an expected result. calling a method that +# returns [current class] works. Defining methods to get +# default values is therefore recommended. +# +# - method-parameter: +# +# * It is currently possibe to refer to instance variables in the +# default scope (without the nsf resolver, without namespace +# prefixes) + + +nx::test case substdefaultoptions { + set ::X 123 + nx::Class create D { + + :property {a {a $::X [set x 4] \t}} + :property {b:substdefault {a $::X [set x 4] \t}} + :property {c:substdefault,substdefaultoptions=0b111 {a $::X [set x 4] \t}} + :property {d:substdefault,substdefaultoptions=0b100 {a $::X [set x 4] \t}} + :property {e:substdefault,substdefaultoptions=0b010 {a $::X [set x 4] \t}} + :property {f:substdefault,substdefaultoptions=0b001 {a $::X [set x 4] \t}} + :property {g:substdefault,substdefaultoptions=0b000 {a $::X [set x 4] \t}} + + :public method show {} { + return ${:a}-${:b}-${:c}-${:d}-${:e}-${:f}-${:g} + } + + } + + ? {D create d1} ::d1 + + #? {::d1 eval :__object_configureparameter} "" + + ? {d1 show} {a $::X [set x 4] \t-a 123 4 -a 123 4 -a $::X [set x 4] -a 123 [set x 4] \t-a $::X 4 \t-a $::X [set x 4] \t} + + unset ::X +} + +####################################################### +# subst default tests for method properties +####################################################### +nx::test case subst-default-method-parameters { + + nx::Class create D { + :method "current" {x} { return [current $x] } + + :property -accessor public {c 1} + :property {d 2} + :property {e 3} + + :create d1 + + :public method bar { + {-s:substdefault "[current]"} + {-literal "[current]"} + {-c:substdefault "[:c get]"} + {-d:integer,substdefault "$d"} + {-e:integer,substdefault "${:e}"} + {-f:substdefault "[current class]"} + {-g:substdefault "[:current class]"} + } { + return $s-$literal-$c-$d-$e-$f-$g + } + } + + # + # Interesting is arg "-d", since it resolves without a further + # namespace resolver to the name of the instance variable. This can + # not easily avoided, but i do not see, why we would have to. + # + # UNEXPECTED: For the value of "f" i would expect ::D. + # + ? {d1 bar -c 1} {::d1-[current]-1-2-3-::nx::test-::D} "substdefault in method parameter" + + # + # the reason that "$d" works is due to the fact, that the method + # frame has to contain the instance variables. + # + D public method i {{-vars:substdefault "[info vars]"}} { + set localVars [info vars] + set locals [info locals] + return [lsort $vars]-[lsort $localVars]-[lsort $locals] + } + + ? {d1 i} "c d e-vars-localVars vars" + + # + # Let us change the intance variables to make sure the resolving is + # dynamic. + # + d1 configure -c 100 -d 200 -e 300 + + ? {d1 bar} {::d1-[current]-100-200-300-::nx::test-::D} "substdefault in method parameter" + + # + # To summarize, we can address instance variables in substdefault + # with little syntax, and as well global variables. For other kind + # of variables, we can use a method, such as e.g. in the following + # case, where we have some modules "my-module" whoch might have some + # configuration variables (here X), and we define a method to access + # it. + # + + namespace eval ::my-module { + set X 1001 + } + + D public method file-scoped {x} { + namespace eval ::my-module [list set $x ] + } + + D public method m {{-x:substdefault "[:file-scoped X]"}} { + return $x + } + + ? {d1 m} "1001" +} + +####################################################### +# subst default tests for object properties +####################################################### +nx::test case subst-default-object-parameters { + + namespace eval ::my-module { + set X 1001 + } + + nx::Class create D { + :object variable Y 1002 + + :property {a "[current]"} + :property {b:substdefault "[current]"} + :property {c:substdefault "[info vars]"} + :property {d:substdefault "[:info vars]"} + + :create d1 + + :public method show {} { + return ${:a}-${:b}-${:c}-${:d} + } + } + + # + # The scope of "current" in the substdefault is "::d1", the same as + # for method parameters. + # + ? {d1 show} {[current]-::d1--} + + + nx::Class create C { + :object variable Y 1002 + :object property -accessor public {Z 1003} + + :method file-scoped {x} { + namespace eval ::my-module [list set $x ] + } + :method "object getvar" {x} { + namespace eval [current class] [list set $x ] + } + :method "current" {x} { return [current $x] } + + :property {e:substdefault "[:file-scoped X]"} + :property {f:substdefault "[:object getvar Y]"} + :property {g:substdefault "[current class]"} + :property {h:substdefault "[:current class]"} + :property {i:substdefault "[[:current class] Z get]"} + + :create c1 + + :public method show {} { + return ${:e}-${:f}-${:g}-${:h}-${:i} + } + } + + # + # One can use methods to access e.h. for method parameters. + # UNEXPECTED: for "g" where i would expect "::C". + # + ? {c1 show} {1001-1002-::nx::Class-::C-1003} + +} + + +# +# Local variables: +# mode: tcl +# tcl-indent-level: 2 +# indent-tabs-mode: nil +# End: