Attachment "inner4.patch" to
ticket [2995655fff]
added by
ferrieux
2010-09-07 05:21:56.
Index: generic/tclBasic.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclBasic.c,v
retrieving revision 1.465
diff -u -p -r1.465 tclBasic.c
--- generic/tclBasic.c 31 Aug 2010 20:48:17 -0000 1.465
+++ generic/tclBasic.c 6 Sep 2010 22:11:47 -0000
@@ -545,6 +545,8 @@ Tcl_CreateInterp(void)
Tcl_IncrRefCount(iPtr->upLiteral);
TclNewLiteralStringObj(iPtr->callLiteral,"CALL");
Tcl_IncrRefCount(iPtr->callLiteral);
+ TclNewLiteralStringObj(iPtr->innerLiteral,"INNER");
+ Tcl_IncrRefCount(iPtr->innerLiteral);
iPtr->errorCode = NULL;
TclNewLiteralStringObj(iPtr->ecVar, "::errorCode");
Tcl_IncrRefCount(iPtr->ecVar);
@@ -1488,6 +1490,7 @@ DeleteInterpProc(
iPtr->errorStack = NULL;
Tcl_DecrRefCount(iPtr->upLiteral);
Tcl_DecrRefCount(iPtr->callLiteral);
+ Tcl_DecrRefCount(iPtr->innerLiteral);
if (iPtr->returnOpts) {
Tcl_DecrRefCount(iPtr->returnOpts);
}
Index: generic/tclCompCmds.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCompCmds.c,v
retrieving revision 1.169
diff -u -p -r1.169 tclCompCmds.c
--- generic/tclCompCmds.c 30 Apr 2010 09:23:06 -0000 1.169
+++ generic/tclCompCmds.c 6 Sep 2010 22:11:50 -0000
@@ -3613,6 +3613,7 @@ TclCompileSyntaxError(
int numBytes;
const char *bytes = TclGetStringFromObj(msg, &numBytes);
+ TclErrorStackResetIf(interp, bytes, numBytes);
TclEmitPush(TclRegisterNewLiteral(envPtr, bytes, numBytes), envPtr);
CompileReturnInternal(envPtr, INST_SYNTAX, TCL_ERROR, 0,
Tcl_GetReturnOptions(interp, TCL_ERROR));
Index: generic/tclCompile.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCompile.c,v
retrieving revision 1.187
diff -u -p -r1.187 tclCompile.c
--- generic/tclCompile.c 22 Aug 2010 18:53:26 -0000 1.187
+++ generic/tclCompile.c 6 Sep 2010 22:11:55 -0000
@@ -4244,6 +4244,104 @@ FormatInstruction(
/*
*----------------------------------------------------------------------
*
+ * TclGetInnerContext --
+ *
+ * If possible, returns a list capturing the inner context. Otherwise
+ * return NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Obj *TclGetInnerContext(Tcl_Interp *interp,
+ const unsigned char *pc,
+ Tcl_Obj **tosPtr)
+{
+ int objc = 0, off = 0;
+ Tcl_Obj *result;
+
+ switch(*pc) {
+
+ case INST_STR_LEN:
+ case INST_LNOT:
+ case INST_BITNOT:
+ case INST_UMINUS:
+ case INST_UPLUS:
+ case INST_TRY_CVT_TO_NUMERIC:
+ case INST_EXPAND_STKTOP:
+ case INST_EXPR_STK:
+
+ objc = 1;
+ break;
+
+ case INST_LIST_IN:
+ case INST_LIST_NOT_IN: /* Basic list containment operators. */
+ case INST_STR_EQ:
+ case INST_STR_NEQ: /* String (in)equality check */
+ case INST_STR_CMP: /* String compare. */
+ case INST_STR_INDEX:
+ case INST_STR_MATCH:
+ case INST_REGEXP:
+ case INST_EQ:
+ case INST_NEQ:
+ case INST_LT:
+ case INST_GT:
+ case INST_LE:
+ case INST_GE:
+ case INST_MOD:
+ case INST_LSHIFT:
+ case INST_RSHIFT:
+ case INST_BITOR:
+ case INST_BITXOR:
+ case INST_BITAND:
+ case INST_EXPON:
+ case INST_ADD:
+ case INST_SUB:
+ case INST_DIV:
+ case INST_MULT:
+ objc = 2;
+ break;
+
+ case INST_RETURN_STK:
+ /* early pop. TODO: dig out opt dict too :/ */
+ objc = 1;
+ break;
+
+ case INST_SYNTAX:
+ case INST_RETURN_IMM:
+ objc = 2;
+ break;
+
+ case INST_INVOKE_STK4:
+ objc = TclGetUInt4AtPtr(pc+1);
+ break;
+
+ case INST_INVOKE_STK1:
+ objc = TclGetUInt1AtPtr(pc+1);
+ break;
+
+
+
+ }
+ result = Tcl_NewListObj(objc + 1, NULL);
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(tclInstructionTable[*pc].name, -1));
+ for(;objc>0;objc--) {
+ Tcl_Obj *ob;
+
+ ob = tosPtr[1 - objc + off];
+ if (!ob) {
+ Tcl_Panic("InnerContext: bad tos -- appending null object");
+ }
+ if (ob->refCount<=0 || ob->refCount==0x61616161) {
+ Tcl_Panic("InnerContext: bad tos -- appending freed object %p",ob);
+ }
+ Tcl_ListObjAppendElement(NULL, result, ob);
+ }
+
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* PrintSourceToObj --
*
* Appends a quoted representation of a string to a Tcl_Obj.
@@ -4349,5 +4447,7 @@ RecordByteCodeStats(
* mode: c
* c-basic-offset: 4
* fill-column: 78
+ * tab-width: 8
+ * indent-tabs-mode: nil
* End:
*/
Index: generic/tclCompile.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCompile.h,v
retrieving revision 1.126
diff -u -p -r1.126 tclCompile.h
--- generic/tclCompile.h 18 Aug 2010 15:44:12 -0000 1.126
+++ generic/tclCompile.h 6 Sep 2010 22:11:57 -0000
@@ -980,6 +980,13 @@ MODULE_SCOPE void TclVerifyLocalLiteralT
#endif
MODULE_SCOPE int TclWordKnownAtCompileTime(Tcl_Token *tokenPtr,
Tcl_Obj *valuePtr);
+MODULE_SCOPE void TclLogCommandInfo(Tcl_Interp *interp,
+ const char *script,
+ const char *command, int length,
+ const unsigned char *pc, Tcl_Obj **tosPtr);
+MODULE_SCOPE Tcl_Obj *TclGetInnerContext(Tcl_Interp *interp,
+ const unsigned char *pc, Tcl_Obj **tosPtr);
+
/*
*----------------------------------------------------------------
Index: generic/tclExecute.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclExecute.c,v
retrieving revision 1.494
diff -u -p -r1.494 tclExecute.c
--- generic/tclExecute.c 1 Sep 2010 20:35:33 -0000 1.494
+++ generic/tclExecute.c 6 Sep 2010 22:12:10 -0000
@@ -199,6 +199,7 @@ typedef struct BottomData {
#define NR_DATA_DIG() \
do { \
pc = BP->pc; \
+ pcBeg = pc; \
codePtr = BP->codePtr; \
cleanup = BP->cleanup; \
TAUX.esPtr = iPtr->execEnvPtr->execStackPtr; \
@@ -1971,6 +1972,8 @@ TclExecuteByteCode(
* stack. */
const unsigned char *pc = NULL;
/* The current program counter. */
+ const unsigned char *pcBeg = NULL;
+ /* The beginning of currently executed INST. */
/*
* Transfer variables - needed only between opcodes, but not while
@@ -2223,6 +2226,8 @@ TclExecuteByteCode(
TCL_DTRACE_INST_NEXT();
+ pcBeg = pc;
+
/*
* These two instructions account for 26% of all instructions (according
* to measurements on tclbench by Ben Vitale
@@ -6376,7 +6381,7 @@ TclExecuteByteCode(
if ((TRESULT == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
bytes = GetSrcInfoForPc(pc, codePtr, &length);
DECACHE_STACK_INFO();
- Tcl_LogCommandInfo(interp, codePtr->source, bytes, bytes ? length : 0);
+ TclLogCommandInfo(interp, codePtr->source, bytes, bytes ? length : 0, pcBeg, tosPtr);
CACHE_STACK_INFO();
}
iPtr->flags &= ~ERR_ALREADY_LOGGED;
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.482
diff -u -p -r1.482 tclInt.h
--- generic/tclInt.h 30 Aug 2010 14:02:10 -0000 1.482
+++ generic/tclInt.h 6 Sep 2010 22:12:10 -0000
@@ -2164,6 +2164,7 @@ typedef struct Interp {
Tcl_Obj *errorStack; /* [info errorstack] value (as a Tcl_Obj). */
Tcl_Obj *upLiteral; /* "UP" literal for [info errorstack] */
Tcl_Obj *callLiteral; /* "CALL" literal for [info errorstack] */
+ Tcl_Obj *innerLiteral; /* "INNER" literal for [info errorstack] */
int resetErrorStack; /* controls cleaning up of ::errorStack */
#ifdef TCL_COMPILE_STATS
@@ -3100,6 +3101,8 @@ MODULE_SCOPE void TclpThreadDeleteKey(vo
MODULE_SCOPE void TclpThreadSetMasterTSD(void *tsdKeyPtr, void *ptr);
MODULE_SCOPE void * TclpThreadGetMasterTSD(void *tsdKeyPtr);
+MODULE_SCOPE void TclErrorStackResetIf(Tcl_Interp *interp, char *msg, int length);
+
/*
*----------------------------------------------------------------
* Command procedures in the generic core:
Index: generic/tclNamesp.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclNamesp.c,v
retrieving revision 1.212
diff -u -p -r1.212 tclNamesp.c
--- generic/tclNamesp.c 30 Aug 2010 14:02:10 -0000 1.212
+++ generic/tclNamesp.c 6 Sep 2010 22:12:14 -0000
@@ -26,7 +26,7 @@
*/
#include "tclInt.h"
-#include "tclCompile.h" /* just for NRCommand */
+#include "tclCompile.h" /* for NRCommand; and TclLogCommandInfo visibility */
/*
* Thread-local storage used to avoid having a global lock on data that is not
@@ -4850,31 +4850,36 @@ TclGetNamespaceChildTable(
/*
*----------------------------------------------------------------------
*
- * Tcl_LogCommandInfo --
+ * TclLogCommandInfo --
*
* This function is invoked after an error occurs in an interpreter. It
- * adds information to iPtr->errorInfo field to describe the command that
- * was being executed when the error occurred.
+ * adds information to iPtr->errorInfo/errorStack fields to describe the
+ * command that was being executed when the error occurred. When pc and
+ * tosPtr are non-NULL, conveying a bytecode execution "inner context",
+ * and the offending instruction is suitable, that inner context is
+ * recorded in errorStack.
*
* Results:
* None.
*
* Side effects:
- * Information about the command is added to errorInfo and the line
- * number stored internally in the interpreter is set.
+ * Information about the command is added to errorInfo/errorStack and the
+ * line number stored internally in the interpreter is set.
*
*----------------------------------------------------------------------
*/
void
-Tcl_LogCommandInfo(
+TclLogCommandInfo(
Tcl_Interp *interp, /* Interpreter in which to log information. */
const char *script, /* First character in script containing
* command (must be <= command). */
const char *command, /* First character in command that generated
* the error. */
- int length) /* Number of bytes in command (-1 means use
+ int length, /* Number of bytes in command (-1 means use
* all bytes up to first null byte). */
+ const unsigned char *pc, /* current pc of bytecode execution context */
+ Tcl_Obj **tosPtr) /* current stack of bytecode execution context */
{
register const char *p;
Interp *iPtr = (Interp *) interp;
@@ -4961,6 +4966,18 @@ Tcl_LogCommandInfo(
Tcl_ListObjLength(interp, iPtr->errorStack, &len);
/* reset while keeping the list intrep as much as possible */
Tcl_ListObjReplace(interp, iPtr->errorStack, 0, len, 0, NULL);
+ if (pc != NULL) {
+ Tcl_Obj *innerContext;
+
+ innerContext = TclGetInnerContext(interp, pc, tosPtr);
+ if (innerContext != NULL) {
+ Tcl_ListObjAppendElement(NULL, iPtr->errorStack, iPtr->innerLiteral);
+ Tcl_ListObjAppendElement(NULL, iPtr->errorStack, innerContext);
+ }
+ } else if (command != NULL) {
+ Tcl_ListObjAppendElement(NULL, iPtr->errorStack, iPtr->innerLiteral);
+ Tcl_ListObjAppendElement(NULL, iPtr->errorStack, Tcl_NewStringObj(command, length));
+ }
}
if (!iPtr->framePtr->objc) {
@@ -4980,6 +4997,80 @@ Tcl_LogCommandInfo(
}
/*
+ *----------------------------------------------------------------------
+ *
+ * TclErrorStackResetIf --
+ *
+ * The TIP 348 reset/no-bc part of TLCI, for specific use by
+ * TclCompileSyntaxError.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Reset errorstack if it needs be, and in that case remember the
+ * passed-in error message as inner context.
+ *
+ *----------------------------------------------------------------------
+ */
+void TclErrorStackResetIf(Tcl_Interp *interp, char *msg, int length)
+{
+ Interp *iPtr = (Interp *) interp;
+
+ if (Tcl_IsShared(iPtr->errorStack)) {
+ Tcl_Obj *newObj;
+
+ newObj = Tcl_DuplicateObj(iPtr->errorStack);
+ Tcl_DecrRefCount(iPtr->errorStack);
+ Tcl_IncrRefCount(newObj);
+ iPtr->errorStack = newObj;
+ }
+ if (iPtr->resetErrorStack) {
+ int len;
+
+ iPtr->resetErrorStack = 0;
+ Tcl_ListObjLength(interp, iPtr->errorStack, &len);
+ /* reset while keeping the list intrep as much as possible */
+ Tcl_ListObjReplace(interp, iPtr->errorStack, 0, len, 0, NULL);
+ Tcl_ListObjAppendElement(NULL, iPtr->errorStack, iPtr->innerLiteral);
+ Tcl_ListObjAppendElement(NULL, iPtr->errorStack, Tcl_NewStringObj(msg, length));
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_LogCommandInfo --
+ *
+ * This function is invoked after an error occurs in an interpreter. It
+ * adds information to iPtr->errorInfo/errorStack fields to describe the
+ * command that was being executed when the error occurred.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information about the command is added to errorInfo/errorStack and the
+ * line number stored internally in the interpreter is set.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_LogCommandInfo(
+ Tcl_Interp *interp, /* Interpreter in which to log information. */
+ const char *script, /* First character in script containing
+ * command (must be <= command). */
+ const char *command, /* First character in command that generated
+ * the error. */
+ int length) /* Number of bytes in command (-1 means use
+ * all bytes up to first null byte). */
+{
+ TclLogCommandInfo(interp, script, command, length, NULL, NULL);
+}
+
+
+/*
* Local Variables:
* mode: c
* c-basic-offset: 4
Index: tests/error.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/error.test,v
retrieving revision 1.33
diff -u -p -r1.33 error.test
--- tests/error.test 2 Jun 2010 23:36:26 -0000 1.33
+++ tests/error.test 6 Sep 2010 22:12:18 -0000
@@ -174,13 +174,13 @@ test error-4.6 {errorstack via info } -b
proc g x {error G:$x}
catch {f 12}
info errorstack
-} -match glob -result {CALL {g 1212} CALL {f 12} UP 1}
+} -match glob -result {INNER * CALL {g 1212} CALL {f 12} UP 1}
test error-4.7 {errorstack via options dict } -body {
proc f x {g $x$x}
proc g x {error G:$x}
catch {f 12} m d
dict get $d -errorstack
-} -match glob -result {CALL {g 1212} CALL {f 12} UP 1}
+} -match glob -result {INNER * CALL {g 1212} CALL {f 12} UP 1}
# Errors in error command itself
@@ -244,7 +244,7 @@ test error-6.10 {catch must reset errors
catch {f 13}
set e2 [info errorstack]
list $e1 $e2
-} -match glob -result {{CALL {g 1212} CALL {f 12} UP 1} {CALL {g 1313} CALL {f 13} UP 1}}
+} -match glob -result {{INNER * CALL {g 1212} CALL {f 12} UP 1} {INNER * CALL {g 1313} CALL {f 13} UP 1}}
test error-7.1 {Bug 1397843} -body {
variable cmds
Index: tests/result.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/result.test,v
retrieving revision 1.19
diff -u -p -r1.19 result.test
--- tests/result.test 2 Jun 2010 23:36:26 -0000 1.19
+++ tests/result.test 6 Sep 2010 22:12:18 -0000
@@ -135,14 +135,14 @@ test result-6.3 {Bug 2383005} {
catch {return -code error -errorcode {{}a} eek} m
set m
} {bad -errorcode value: expected a list but got "{}a"}
-test result-6.4 {non-list -errorstack} {
+test result-6.4 {non-list -errorstack} -body {
catch {return -code error -errorstack {{}a} eek} m o
list $m [dict get $o -errorcode] [dict get $o -errorstack]
-} {{bad -errorstack value: expected a list but got "{}a"} {TCL RESULT NONLIST_ERRORSTACK} {UP 1}}
-test result-6.5 {odd-sized-list -errorstack} {
+} -match glob -result {{bad -errorstack value: expected a list but got "{}a"} {TCL RESULT NONLIST_ERRORSTACK} {INNER * UP 1}}
+test result-6.5 {odd-sized-list -errorstack} -body {
catch {return -code error -errorstack a eek} m o
list $m [dict get $o -errorcode] [dict get $o -errorstack]
-} {{forbidden odd-sized list for -errorstack: "a"} {TCL RESULT ODDSIZEDLIST_ERRORSTACK} {UP 1}}
+} -match glob -result {{forbidden odd-sized list for -errorstack: "a"} {TCL RESULT ODDSIZEDLIST_ERRORSTACK} {INNER * UP 1}}
# cleanup
cleanupTests
return