Index: TODO =================================================================== diff -u -N -ra0faba200038c44cda56f3f421361563b4bd95d7 -re367957430bf9246069791785619a5503e166d33 --- TODO (.../TODO) (revision a0faba200038c44cda56f3f421361563b4bd95d7) +++ TODO (.../TODO) (revision e367957430bf9246069791785619a5503e166d33) @@ -5746,6 +5746,16 @@ - extended regression test - bump version number to 2.1 + +- make use of Tcl_SaveInterpState() and friends for saving results + in NsfDStringEval() +- added results in debug exit calls +- changed interface of NsfDStringEval to control behavior via bitflags + (this is after all more readable than a argument list of "0" and "1"s) +- added optional recursion prevention for functions called via NsfDStringEval + (handling NSF_EVAL_DEBUG, NSF_EVAL_LOG, NSF_EVAL_DEPRECATED) +- added regression tests for potential recursive calls + ======================================================================== TODO: - add regression tests for debug and deprecated in methods (behavior) Index: generic/nsf.c =================================================================== diff -u -N -rfd0f246e04ac2b64f1802dfd265bfc588e496c21 -re367957430bf9246069791785619a5503e166d33 --- generic/nsf.c (.../nsf.c) (revision fd0f246e04ac2b64f1802dfd265bfc588e496c21) +++ generic/nsf.c (.../nsf.c) (revision e367957430bf9246069791785619a5503e166d33) @@ -510,38 +510,63 @@ *---------------------------------------------------------------------- */ int -NsfDStringEval(Tcl_Interp *interp, Tcl_DString *dsPtr, const char *context, int safe, int noProfile) { - int result, prevProfileSetting; - Tcl_Obj *savedResultObj; +NsfDStringEval(Tcl_Interp *interp, Tcl_DString *dsPtr, const char *context, + unsigned int traceEvalFlags) { + Tcl_InterpState state; + NsfRuntimeState *rst; + int result, prevDoProfile; nonnull_assert(interp != NULL); nonnull_assert(dsPtr != NULL); nonnull_assert(context != NULL); - if (noProfile) { - prevProfileSetting = RUNTIME_STATE(interp)->doProfile; - RUNTIME_STATE(interp)->doProfile = 0; + rst = RUNTIME_STATE(interp); + + if ((traceEvalFlags & NSF_EVAL_PREVENT_RECURSION) != 0u) { + /* + * We do not want to debug the debug statements, since this would cause an + * inifinite recursion. Check, if we allow execution of the eval call. + */ + if ((rst->preventRecursionFlags & traceEvalFlags) != 0) { + /* + * Recursive case, do NOT execute the cmd and return silently. + */ + return TCL_OK; + } + + rst->preventRecursionFlags |= traceEvalFlags; } - if (safe) { - savedResultObj = Tcl_GetObjResult(interp); - INCR_REF_COUNT(savedResultObj); + if ((traceEvalFlags & NSF_EVAL_NOPROFILE) && rst->doProfile == 1) { + /* + * Profiling should be deactivated for the eval. + */ + prevDoProfile = 1; + rst->doProfile = 0; + } else { + prevDoProfile = 0; } - result = Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), 0); - if (safe) { - if (result == TCL_OK) { - Tcl_SetObjResult(interp, savedResultObj); - } - DECR_REF_COUNT(savedResultObj); + if ((traceEvalFlags & NSF_EVAL_SAVE) != 0u) { + state = Tcl_SaveInterpState(interp, TCL_OK); } - if (noProfile) { - RUNTIME_STATE(interp)->doProfile = prevProfileSetting; - } + result = Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), 0); if (unlikely(result == TCL_ERROR)) { NsfErrorContext(interp, context); } + + if ((traceEvalFlags & NSF_EVAL_SAVE) != 0u) { + Tcl_RestoreInterpState(interp, state); + } + if ((traceEvalFlags & NSF_EVAL_PREVENT_RECURSION) != 0u) { + rst->preventRecursionFlags &= ~traceEvalFlags; + } + + if (prevDoProfile == 1) { + rst->doProfile = 1; + } + return result; } @@ -588,7 +613,7 @@ Tcl_DStringAppendElement(&cmdString, "::nsf::log"); Tcl_DStringAppendElement(&cmdString, level); Tcl_DStringAppendElement(&cmdString, Tcl_DStringValue(&ds)); - NsfDStringEval(interp, &cmdString, "log command", 0, 0); + NsfDStringEval(interp, &cmdString, "log command", (NSF_EVAL_LOG|NSF_EVAL_NOPROFILE)); Tcl_DStringFree(&cmdString); Tcl_DStringFree(&ds); } @@ -629,7 +654,7 @@ Tcl_DStringAppendElement(dsPtr, oldCmd); Tcl_DStringAppendElement(dsPtr, newCmd); - NsfDStringEval(interp, dsPtr, "log command", 0, 1); + NsfDStringEval(interp, dsPtr, "deprecated command", (NSF_EVAL_DEPRECATED|NSF_EVAL_NOPROFILE)); Tcl_DStringFree(dsPtr); } Index: generic/nsfInt.h =================================================================== diff -u -N -ra0faba200038c44cda56f3f421361563b4bd95d7 -re367957430bf9246069791785619a5503e166d33 --- generic/nsfInt.h (.../nsfInt.h) (revision a0faba200038c44cda56f3f421361563b4bd95d7) +++ generic/nsfInt.h (.../nsfInt.h) (revision e367957430bf9246069791785619a5503e166d33) @@ -450,7 +450,19 @@ #define NSF_CMD_CLASS_ONLY_METHOD 0x00100000 #define NSF_CMD_DEPRECATED_METHOD 0x00200000 #define NSF_CMD_DEBUG_METHOD 0x00400000 + /* + * traceEvalFlags controlling NsfDStringEval + */ +#define NSF_EVAL_SAVE 0x01u /* save interp context */ +#define NSF_EVAL_NOPROFILE 0x02u /* no profile below this call */ +#define NSF_EVAL_DEBUG 0x04u /* call is a debug call, prevent recursion */ +#define NSF_EVAL_LOG 0x08u /* call is a log call, prevent recursion */ +#define NSF_EVAL_DEPRECATED 0x10u /* call is a deprecated call, prevent recursion */ + +#define NSF_EVAL_PREVENT_RECURSION (NSF_EVAL_DEBUG|NSF_EVAL_LOG|NSF_EVAL_DEPRECATED) + +/* * object flags ... */ @@ -933,6 +945,7 @@ int doKeepcmds; int doProfile; int doTrace; + unsigned int preventRecursionFlags; int doSoftrecreate; /* keep track of defined filters */ Tcl_HashTable activeFilterTablePtr; @@ -1127,7 +1140,8 @@ const char *methodName) nonnull(1) nonnull(3) returns_nonnull; -EXTERN int NsfDStringEval(Tcl_Interp *interp, Tcl_DString *dsPtr, const char *context, int safe, int noProfile) +EXTERN int NsfDStringEval(Tcl_Interp *interp, Tcl_DString *dsPtr, const char *context, + unsigned int traceEvalFlags) nonnull(1) nonnull(2) nonnull(3); Index: generic/nsfProfile.c =================================================================== diff -u -N -r2771c4c7e0b56fe2a6cde84fdae9b170ff652928 -re367957430bf9246069791785619a5503e166d33 --- generic/nsfProfile.c (.../nsfProfile.c) (revision 2771c4c7e0b56fe2a6cde84fdae9b170ff652928) +++ generic/nsfProfile.c (.../nsfProfile.c) (revision e367957430bf9246069791785619a5503e166d33) @@ -169,7 +169,7 @@ Nsf_DStringPrintf(&ds, " {%s}", ObjStr(listObj)); DECR_REF_COUNT(listObj); - NsfDStringEval(interp, &ds, "debug call", 1, 1); + NsfDStringEval(interp, &ds, "debug call", (NSF_EVAL_DEBUG|NSF_EVAL_SAVE|NSF_EVAL_NOPROFILE)); Tcl_DStringFree(&ds); @@ -205,7 +205,7 @@ Tcl_DStringAppend(dsPtr, " {}", 4); } - NsfDStringEval(interp, &ds, "debug exit", 1, 1); + NsfDStringEval(interp, &ds, "debug exit", (NSF_EVAL_DEBUG|NSF_EVAL_SAVE|NSF_EVAL_NOPROFILE)); Tcl_DStringFree(dsPtr); rst->debugCallingDepth--; Index: generic/nsfStack.c =================================================================== diff -u -N -r8854eeff1e1b2d5fde52ee4f71b1e2b7844b2dea -re367957430bf9246069791785619a5503e166d33 --- generic/nsfStack.c (.../nsfStack.c) (revision 8854eeff1e1b2d5fde52ee4f71b1e2b7844b2dea) +++ generic/nsfStack.c (.../nsfStack.c) (revision e367957430bf9246069791785619a5503e166d33) @@ -1232,9 +1232,11 @@ if ((Tcl_Command_flags(cscPtr->cmdPtr) & NSF_CMD_DEBUG_METHOD) != 0) { #if defined(NSF_PROFILE) || defined(NSF_DTRACE) - NsfProfileDebugExit(interp, cscPtr->self, cscPtr->cl, cscPtr->methodName, cscPtr->startSec, cscPtr->startUsec); + NsfProfileDebugExit(interp, cscPtr->self, cscPtr->cl, cscPtr->methodName, + cscPtr->startSec, cscPtr->startUsec); #else - NsfProfileDebugExit(interp, cscPtr->self, cscPtr->cl, Tcl_GetCommandName(interp, cscPtr->cmdPtr), 0, 0); + NsfProfileDebugExit(interp, cscPtr->self, cscPtr->cl, + Tcl_GetCommandName(interp, cscPtr->cmdPtr), 0, 0); #endif } @@ -1262,6 +1264,7 @@ */ if (unlikely(cscPtr->cl != NULL)) { NsfObject *clObject = &cscPtr->cl->object; + clObject->activationCount --; MEM_COUNT_FREE("class.activationCount", clObject); Index: tests/nsf-cmd.test =================================================================== diff -u -N -ra775d31c0b4770cc7eddc1b4c015bc0dea1fddd2 -re367957430bf9246069791785619a5503e166d33 --- tests/nsf-cmd.test (.../nsf-cmd.test) (revision a775d31c0b4770cc7eddc1b4c015bc0dea1fddd2) +++ tests/nsf-cmd.test (.../nsf-cmd.test) (revision e367957430bf9246069791785619a5503e166d33) @@ -277,7 +277,34 @@ } +# +# test error transparency of "-debug" option +# +nx::test case nsf-debug-error { + nsf::proc foo {} { + set exception [catch {bar} errorMsg options] + if {$exception} { + puts stderr O=$options + puts stderr <<$::errorInfo>> + set result [list $exception $errorMsg [string length $::errorInfo] $::errorCode] + } else { + set result "" + } + return $result + } + nsf::proc bar {} {return -code error -errorcode MyException "exception"} + ? {foo} {1 exception 35 MyException} + + # + # redefine bar with debug flag + # + nsf::proc -debug bar {} {return -code error -errorcode MyException "exception"} + + ? {foo} {1 exception 35 MyException} +} + + # # test virtual arg resolution + filtering # @@ -384,6 +411,83 @@ } # +# recursive debug calls +# +nx::test case nsf-debug-recursive { + set ::count 0 + + set oldCall [nsf::cmd::info definition ::nsf::debug::call] + nsf::proc -debug ::nsf::debug::call args { + incr ::count + #puts "MYDEBUG $args" + } + nsf::proc -debug foo {} {return 1} + + ? {foo} "1" + ? {set ::count} 1 + + # restore original definition of ::nsf::debug::call + eval $oldCall +} + +# +# recursive log calls +# +nx::test case nsf-log-recursive { + + # + # First the case, where the log function calls another Tcl function + # (which might be debugged) + # + set oldCall [nsf::cmd::info definition ::nsf::log] + nsf::proc ::nsf::log args { + incr ::count + #puts "::nsf::log <$args> ... before foo" + foo + #puts "::nsf::log <$args> ... after foo" + return + } + nsf::proc foo {} {return 1} + nsf::proc bar {} {nsf::log notice hello} + + # + # "foo" calls no nsf::log, but "bar" calls it once + # + set ::count 0 + ? {foo} "1" + ? {set ::count} 0 + + set ::count 0 + ? {bar} "" + ? {set ::count} 1 + + # + # now we add the debug flag to foo, therefore "foo" will call + # "nsf::log", which might become a infinite recursion loop. + # + nsf::proc -debug foo {} {return 1} + + # + # "foo" is has now "-debug" set, therefore it calls the log function + # + set ::count 0 + ? {foo} "1" + ? {set ::count} 2 + + # + # "bar" calls "log", which in turn calls a debugged function + # + set ::count 0 + ? {bar} "" + ? {set ::count} 3 + + # restore original definition of ::nsf::log + eval $oldCall + +} + + +# # Local variables: # mode: tcl # tcl-indent-level: 2