Index: TODO =================================================================== diff -u -r6d3a6ca439f56b0ba93269b195ff00276859463a -r21686b86d06844eca86086b9f9391d77d54dbc06 --- TODO (.../TODO) (revision 6d3a6ca439f56b0ba93269b195ff00276859463a) +++ TODO (.../TODO) (revision 21686b86d06844eca86086b9f9391d77d54dbc06) @@ -5795,6 +5795,17 @@ - extended serializer to handle "-returns" flag - extended regression test +cmd resolver work +- fix test, when OS specific cmd resolver is used + from a NSF_CSC_CALL_IS_COMPILE frame +- improved output from __db_show_obj: put results into one line + instead of multiple lines +- new debug function __db_get_obj: return into about a tcl_obj in form + of a dict (in general, one should not rely on Tcl_Obj internals, + especially when upgrading over major Tcl versions, but for + testing/understanding behavior etc., such a command is helpful). +- extend regression test + ======================================================================== TODO: Index: generic/nsf.c =================================================================== diff -u -r2cb4ac5c332a18dca2ed00ce3980e8b900a0d2fa -r21686b86d06844eca86086b9f9391d77d54dbc06 --- generic/nsf.c (.../nsf.c) (revision 2cb4ac5c332a18dca2ed00ce3980e8b900a0d2fa) +++ generic/nsf.c (.../nsf.c) (revision 21686b86d06844eca86086b9f9391d77d54dbc06) @@ -5118,8 +5118,8 @@ static int InterpColonCmdResolver(Tcl_Interp *interp, const char *cmdName, Tcl_Namespace *UNUSED(nsPtr), unsigned int flags, Tcl_Command *cmdPtr) { - CallFrame *varFramePtr; - int frameFlags; + CallFrame *varFramePtr; + unsigned int frameFlags; nonnull_assert(interp != NULL); nonnull_assert(cmdName != NULL); @@ -5142,14 +5142,18 @@ * stuff into the call flags of the proc frame during the * compilation step NSF_CSC_CALL_IS_COMPILE. */ - if (frameFlags == 0 && Tcl_CallFrame_callerPtr(varFramePtr)) { + if ((frameFlags == 0u) && (Tcl_CallFrame_callerPtr(varFramePtr) != NULL)) { + ClientData clientData; + varFramePtr = (CallFrame *)Tcl_CallFrame_callerPtr(varFramePtr); frameFlags = Tcl_CallFrame_isProcCallFrame(varFramePtr); + clientData = varFramePtr->clientData; - if ((frameFlags & (FRAME_IS_NSF_METHOD)) == 0u - || (((NsfCallStackContent *)varFramePtr->clientData)->flags & NSF_CSC_CALL_IS_COMPILE) == 0u - ) { - frameFlags = 0; + if ( (frameFlags != 0u) + && (clientData != NULL) + && ((((NsfCallStackContent *)clientData)->flags & NSF_CSC_CALL_IS_COMPILE) == 0u) + ) { + frameFlags = 0u; } else { #if defined(CMD_RESOLVER_TRACE) fprintf(stderr, "InterpColonCmdResolver got parent frame cmdName %s flags %.6x, frame flags %.6x\n", @@ -5197,6 +5201,7 @@ } else { object = NULL; } + if (object != NULL) { Tcl_HashEntry *entryPtr; Tcl_HashTable *cmdTablePtr; @@ -5208,9 +5213,10 @@ entryPtr = Tcl_CreateHashEntry(cmdTablePtr, cmdName, NULL); /*fprintf(stderr, "InterpColonCmdResolver OS specific resolver tried to lookup %s for os %s in ns %s\n", cmdName, ClassName(osPtr->rootClass), ((Command *)cmd)->nsPtr->fullName);*/ + if (entryPtr != NULL) { - /*fprintf(stderr, "InterpColonCmdResolver OS specific resolver found %s::%s\n", - ((Command *)cmd)->nsPtr->fullName, cmdName);*/ + /*fprintf(stderr, "InterpColonCmdResolver OS specific resolver found %s::%s frameFlags %.6x\n", + ((Command *)cmd)->nsPtr->fullName, cmdName, frameFlags);*/ *cmdPtr = Tcl_GetHashValue(entryPtr); return TCL_OK; } @@ -24316,7 +24322,7 @@ nonnull_assert(interp != NULL); nonnull_assert(objPtr != NULL); - fprintf(stderr, "*** obj %p refCount %d type <%s>\n", + fprintf(stderr, "*** obj %p refCount %d type <%s> ", (void *)objPtr, objPtr->refCount, (objPtr->typePtr != NULL) ? objPtr->typePtr->name : ""); if (objPtr->typePtr == &NsfObjectMethodObjType @@ -24328,7 +24334,7 @@ RUNTIME_STATE(interp)->instanceMethodEpoch; Tcl_Command cmd = mcPtr->cmd; - fprintf(stderr, " method epoch %d max %d cmd %p objProc 0x%x flags %.6x\n", + fprintf(stderr, " method epoch %d max %d cmd %p objProc 0x%x flags %.6x", mcPtr->methodEpoch, currentMethodEpoch, (void *)cmd, (cmd != NULL) ? PTR2UINT(((Command *)cmd)->objProc) : 0u, @@ -24346,13 +24352,44 @@ Command *procPtr = (Command *)cmd; char *tail = Tcl_GetHashKey(procPtr->hPtr->tablePtr, procPtr->hPtr); - fprintf(stderr, "... cmd %p flags %.6x name '%s' ns '%s'\n", + fprintf(stderr, "... cmd %p flags %.6x name '%s' ns '%s'", (void *)cmd, Tcl_Command_flags(cmd), tail, procPtr->nsPtr->name); } } + fprintf(stderr, "\n"); + return TCL_OK; } + +/* +cmd __db_get_obj NsfDebugGetDict { + {-argName "obj" -required 1 -type tclobj} +} +*/ +static int NsfDebugGetDict(Tcl_Interp *interp, Tcl_Obj *objPtr) nonnull(1) nonnull(2); + +static int +NsfDebugGetDict(Tcl_Interp *interp, Tcl_Obj *objPtr) { + Tcl_Obj *resultObj; + const char *typeString; + + nonnull_assert(interp != NULL); + nonnull_assert(objPtr != NULL); + + typeString = (objPtr->typePtr != NULL) ? objPtr->typePtr->name : ""; + + resultObj = Tcl_NewListObj(4, NULL); + Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("type", -1)); + Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(typeString, -1)); + Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("refcount", -1)); + Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewIntObj(objPtr->refCount)); + + Tcl_SetObjResult(interp, resultObj); + + return TCL_OK; +} + /* cmd __db_show_stack NsfShowStackCmd {} */ Index: generic/nsfAPI.decls =================================================================== diff -u -re3655407706b60f17780585628f0e16cc0af0631 -r21686b86d06844eca86086b9f9391d77d54dbc06 --- generic/nsfAPI.decls (.../nsfAPI.decls) (revision e3655407706b60f17780585628f0e16cc0af0631) +++ generic/nsfAPI.decls (.../nsfAPI.decls) (revision 21686b86d06844eca86086b9f9391d77d54dbc06) @@ -54,6 +54,9 @@ cmd __db_show_obj NsfDebugShowObj { {-argName "obj" -required 1 -type tclobj} } +cmd __db_get_obj NsfDebugGetDict { + {-argName "obj" -required 1 -type tclobj} +} cmd __profile_clear NsfProfileClearDataStub {} cmd __profile_get NsfProfileGetDataStub {} cmd __profile_trace NsfProfileTraceStub { Index: generic/nsfAPI.h =================================================================== diff -u -r546f8ddb033b81b5a4f9836d4f5541c9f68ac306 -r21686b86d06844eca86086b9f9391d77d54dbc06 --- generic/nsfAPI.h (.../nsfAPI.h) (revision 546f8ddb033b81b5a4f9836d4f5541c9f68ac306) +++ generic/nsfAPI.h (.../nsfAPI.h) (revision 21686b86d06844eca86086b9f9391d77d54dbc06) @@ -295,7 +295,7 @@ /* just to define the symbol */ -static Nsf_methodDefinition method_definitions[114]; +static Nsf_methodDefinition method_definitions[115]; static const char *method_command_namespace_names[] = { "::nsf::methods::object::info", @@ -361,6 +361,8 @@ NSF_nonnull(2) NSF_nonnull(4); static int NsfDebugCompileEpochStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) NSF_nonnull(2) NSF_nonnull(4); +static int NsfDebugGetDictStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) + NSF_nonnull(2) NSF_nonnull(4); static int NsfDebugRunAssertionsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) NSF_nonnull(2) NSF_nonnull(4); static int NsfDebugShowObjStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) @@ -588,6 +590,8 @@ NSF_nonnull(1); static int NsfDebugCompileEpoch(Tcl_Interp *interp) NSF_nonnull(1); +static int NsfDebugGetDict(Tcl_Interp *interp, Tcl_Obj *obj) + NSF_nonnull(1) NSF_nonnull(2); static int NsfDebugRunAssertionsCmd(Tcl_Interp *interp) NSF_nonnull(1); static int NsfDebugShowObj(Tcl_Interp *interp, Tcl_Obj *obj) @@ -787,6 +791,7 @@ NsfConfigureCmdIdx, NsfCurrentCmdIdx, NsfDebugCompileEpochIdx, + NsfDebugGetDictIdx, NsfDebugRunAssertionsCmdIdx, NsfDebugShowObjIdx, NsfDirectDispatchCmdIdx, @@ -1648,6 +1653,22 @@ } static int +NsfDebugGetDictStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { + (void)clientData; + + + + if (objc != 2) { + return NsfArgumentError(interp, "wrong # of arguments:", + method_definitions[NsfDebugGetDictIdx].paramDefs, + NULL, objv[0]); + } + + return NsfDebugGetDict(interp, objv[1]); + +} + +static int NsfDebugRunAssertionsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { (void)clientData; @@ -3502,7 +3523,7 @@ } } -static Nsf_methodDefinition method_definitions[114] = { +static Nsf_methodDefinition method_definitions[115] = { {"::nsf::methods::class::alloc", NsfCAllocMethodStub, 1, { {"objectName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, @@ -3630,6 +3651,9 @@ {"::nsf::__db_compile_epoch", NsfDebugCompileEpochStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, +{"::nsf::__db_get_obj", NsfDebugGetDictStub, 1, { + {"obj", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} +}, {"::nsf::__db_run_assertions", NsfDebugRunAssertionsCmdStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, Index: generic/nsfAPI.nxdocindex =================================================================== diff -u -r96f4410aa6510a664938b6ef6e5d43c3a3a18d68 -r21686b86d06844eca86086b9f9391d77d54dbc06 --- generic/nsfAPI.nxdocindex (.../nsfAPI.nxdocindex) (revision 96f4410aa6510a664938b6ef6e5d43c3a3a18d68) +++ generic/nsfAPI.nxdocindex (.../nsfAPI.nxdocindex) (revision 21686b86d06844eca86086b9f9391d77d54dbc06) @@ -2,6 +2,7 @@ set ::nxdoc::include(::nsf::__db_run_assertions) 0 set ::nxdoc::include(::nsf::__db_show_stack) 0 set ::nxdoc::include(::nsf::__db_show_obj) 0 +set ::nxdoc::include(::nsf::__db_get_obj) 0 set ::nxdoc::include(::nsf::__profile_clear) 0 set ::nxdoc::include(::nsf::__profile_get) 0 set ::nxdoc::include(::nsf::__profile_trace) 0 Index: tests/tcl86.test =================================================================== diff -u -r7f8ee0b360c7829a0ae799db0f66a8191a948c9d -r21686b86d06844eca86086b9f9391d77d54dbc06 --- tests/tcl86.test (.../tcl86.test) (revision 7f8ee0b360c7829a0ae799db0f66a8191a948c9d) +++ tests/tcl86.test (.../tcl86.test) (revision 21686b86d06844eca86086b9f9391d77d54dbc06) @@ -255,21 +255,35 @@ # ? {info commands "::@"} "" + + proc getType {x} {dict get [::nsf::__db_get_obj @] type} + + ? {getType @} "" ;# "@" has no type ? {namespace which @} "" + ? {getType @} "cmdName" ;# "@" is of type "cmdName" + ? {@} {invalid command name "@"} + ? {getType @} "bytecode" ;# "@" is of type "bytecode" + # # 1) Provide @ for interp resolver in NX root namespace # proc ::nx::@ {} { return ::nx::@ } + nx::Object create ::o { :public object method foo {} { @; # Should resolve against ::nx::@ (by interp resolver) } } - ? {::o foo} ::nx::@; # Trigger bc-compilation, resolve & execute + + ? {getType @} "bytecode" ;# "@" is still of type "bytecode" + ::o foo + ? {getType @} "bytecode" ;# "@" is still of type "bytecode" (byte code compilation should not leak) + + ? {::o foo} ::nx::@ ;# "@" is resolved in the nx context, therefore we get nx::@ # # 2) Provide alternative @ @@ -279,22 +293,119 @@ } ? {info commands ::@} "::@" + ? {::@} ::@ + ? {getType @} "bytecode" ;# "@" is still of type "bytecode" + + set x [@] ;# execute "@" in an nx environment ("eval" of the test case) + ? {getType @} "cmdName" ;# "@" is of type "cmdName" + + ? [list $x] ::nx::@ - ? {::@} ::@; # no CmdLiteral -> OK! - ? {@} ::@; # "Oops! CmdLiteral vs. Resolver at work: @ should resolve to ::@, not ::nx::@ !!!" - ? {namespace eval :: ::@} ::@ - ? {namespace eval :: @} ::@ + ? @ ::@ ;# proc "?" interprets "@" as a script and turns "@" + ;# into type "bytecode". The proc leaves the nx context + ;# by using a "namespace eval", therefore we see ::@ + ? {getType @} "bytecode" ;# "@" is of type "bytecode" + ? {namespace eval :: @} ::@ ;# exercise the same "namespace eval" as described above + ? {namespace eval :: ::@} ::@ ;# the same with the global namespace qualifier - ? {namespace origin @} ::@; # Hugh! Now the cmd resolution is reported correctly! + ? {getType @} "bytecode" ;# "@" is of type "bytecode" + ? {getType ::@} "bytecode" ;# "::@" is of type "bytecode" + + ? {namespace origin @} ::@ ;# "namespace origin" converts literal "@" from "bytecode" to "cmdName" + ? {getType @} "cmdName" + ? {namespace origin ::@} ::@ + ? {getType @} "cmdName" + ? {getType ::@} "cmdName" - ? {@} ::@; # Still misguided ... + ? {@} ::@ ;# the result is still the same as everywhere, since we are in an nx context XXX } # -# Local variables: -# mode: tcl -# tcl-indent-level: 2 -# indent-tabs-mode: nil -# End: +# Without nx context +# +nx::test case bug-3418547-no-context +proc getType {x} {dict get [::nsf::__db_get_obj @] type} + +# delete the commands +rename @ "" +rename ::nx::@ "" + +? {info commands "::@"} "" + +? {getType @} "" +? {namespace which @} "" +? {getType @} "cmdName" + +? {@} {invalid command name "@"} + +# +# 1) Provide proc @ +# +proc ::@ {} { + return ::@ +} + +? {@} ::@ +? {getType @} "cmdName" + +# +# 2) Provide @ for interp resolver in NX root namespace +# +proc ::nx::@ {} { + return ::nx::@ +} + +set r [@] ;# "@" is not executed in an nx environment (no testcase eval), therefore resolved globally +? {set r} ::@ +? {getType @} "cmdName" + +nx::Object create ::o { + :public object method foo {} { + @ ; # resolve against ::nx::@ (via interp resolver) + } +} + +set r [::o foo] +? {set r} ::nx::@ +? {getType @} "cmdName" + +? {::o foo} ::nx::@ + +set r [@] ;# "@" is not executed in an nx environment (no testcase eval), therefore resolves globally +? {set r} ::@ +? {@} ::@ ;# "@" is executed in an "namespace eval ::", therefore no nx context + +# cleanup +rename ::nx::@ "" +rename @ "" + + +# +# Try to reconstruct test case of Tcl's resolver.test 1.6 +# +nx::test case resolver-1.6 + +proc ::@@ {} {return ::@@} +proc ::nx::@ {} { + return ::nx::@ +} + +nx::Object create ::o { + :public object method foo {} { + @ ; # resolve against ::nx::@ (via interp resolver) + } +} + +set r [::o foo] +? {set r} ::nx::@ + +interp alias {} ::nx::@ {} ::@@ + +# call the new aliased definition +? {::nx::@} ::@@ + +# see consistent results from method foo +set r [::o foo] +? {set r} ::@@