summaryrefslogtreecommitdiffstats
path: root/generic/tclCompile.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclCompile.c')
-rw-r--r--generic/tclCompile.c5599
1 files changed, 3768 insertions, 1831 deletions
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index 25803a0..347e3f0 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -1,25 +1,25 @@
-/*
+/*
* tclCompile.c --
*
- * This file contains procedures that compile Tcl commands or parts
- * of commands (like quoted strings or nested sub-commands) into a
- * sequence of instructions ("bytecodes").
+ * This file contains procedures that compile Tcl commands or parts of
+ * commands (like quoted strings or nested sub-commands) into a sequence
+ * of instructions ("bytecodes").
*
* Copyright (c) 1996-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tclCompile.c,v 1.15 1999/04/22 22:57:06 stanton Exp $
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tclInt.h"
#include "tclCompile.h"
+#include <assert.h>
/*
* Table of all AuxData types.
*/
-
+
static Tcl_HashTable auxDataTypeTable;
static int auxDataTypeTableInitialized; /* 0 means not yet initialized. */
@@ -34,225 +34,712 @@ TCL_DECLARE_MUTEX(tableMutex)
* This variable is linked to the Tcl variable "tcl_traceCompile".
*/
+#ifdef TCL_COMPILE_DEBUG
int tclTraceCompile = 0;
static int traceInitialized = 0;
-
+#endif
+
/*
* A table describing the Tcl bytecode instructions. Entries in this table
- * must correspond to the instruction opcode definitions in tclCompile.h.
- * The names "op1" and "op4" refer to an instruction's one or four byte
- * first operand. Similarly, "stktop" and "stknext" refer to the topmost
- * and next to topmost stack elements.
+ * must correspond to the instruction opcode definitions in tclCompile.h. The
+ * names "op1" and "op4" refer to an instruction's one or four byte first
+ * operand. Similarly, "stktop" and "stknext" refer to the topmost and next to
+ * topmost stack elements.
*
* Note that the load, store, and incr instructions do not distinguish local
* from global variables; the bytecode interpreter at runtime uses the
* existence of a procedure call frame to distinguish these.
*/
-InstructionDesc instructionTable[] = {
- /* Name Bytes #Opnds Operand types Stack top, next */
- {"done", 1, 0, {OPERAND_NONE}},
- /* Finish ByteCode execution and return stktop (top stack item) */
- {"push1", 2, 1, {OPERAND_UINT1}},
- /* Push object at ByteCode objArray[op1] */
- {"push4", 5, 1, {OPERAND_UINT4}},
- /* Push object at ByteCode objArray[op4] */
- {"pop", 1, 0, {OPERAND_NONE}},
- /* Pop the topmost stack object */
- {"dup", 1, 0, {OPERAND_NONE}},
- /* Duplicate the topmost stack object and push the result */
- {"concat1", 2, 1, {OPERAND_UINT1}},
- /* Concatenate the top op1 items and push result */
- {"invokeStk1", 2, 1, {OPERAND_UINT1}},
- /* Invoke command named objv[0]; <objc,objv> = <op1,top op1> */
- {"invokeStk4", 5, 1, {OPERAND_UINT4}},
- /* Invoke command named objv[0]; <objc,objv> = <op4,top op4> */
- {"evalStk", 1, 0, {OPERAND_NONE}},
- /* Evaluate command in stktop using Tcl_EvalObj. */
- {"exprStk", 1, 0, {OPERAND_NONE}},
- /* Execute expression in stktop using Tcl_ExprStringObj. */
-
- {"loadScalar1", 2, 1, {OPERAND_UINT1}},
- /* Load scalar variable at index op1 <= 255 in call frame */
- {"loadScalar4", 5, 1, {OPERAND_UINT4}},
- /* Load scalar variable at index op1 >= 256 in call frame */
- {"loadScalarStk", 1, 0, {OPERAND_NONE}},
- /* Load scalar variable; scalar's name is stktop */
- {"loadArray1", 2, 1, {OPERAND_UINT1}},
- /* Load array element; array at slot op1<=255, element is stktop */
- {"loadArray4", 5, 1, {OPERAND_UINT4}},
- /* Load array element; array at slot op1 > 255, element is stktop */
- {"loadArrayStk", 1, 0, {OPERAND_NONE}},
- /* Load array element; element is stktop, array name is stknext */
- {"loadStk", 1, 0, {OPERAND_NONE}},
- /* Load general variable; unparsed variable name is stktop */
- {"storeScalar1", 2, 1, {OPERAND_UINT1}},
- /* Store scalar variable at op1<=255 in frame; value is stktop */
- {"storeScalar4", 5, 1, {OPERAND_UINT4}},
- /* Store scalar variable at op1 > 255 in frame; value is stktop */
- {"storeScalarStk", 1, 0, {OPERAND_NONE}},
- /* Store scalar; value is stktop, scalar name is stknext */
- {"storeArray1", 2, 1, {OPERAND_UINT1}},
- /* Store array element; array at op1<=255, value is top then elem */
- {"storeArray4", 5, 1, {OPERAND_UINT4}},
- /* Store array element; array at op1>=256, value is top then elem */
- {"storeArrayStk", 1, 0, {OPERAND_NONE}},
- /* Store array element; value is stktop, then elem, array names */
- {"storeStk", 1, 0, {OPERAND_NONE}},
- /* Store general variable; value is stktop, then unparsed name */
-
- {"incrScalar1", 2, 1, {OPERAND_UINT1}},
- /* Incr scalar at index op1<=255 in frame; incr amount is stktop */
- {"incrScalarStk", 1, 0, {OPERAND_NONE}},
- /* Incr scalar; incr amount is stktop, scalar's name is stknext */
- {"incrArray1", 2, 1, {OPERAND_UINT1}},
- /* Incr array elem; arr at slot op1<=255, amount is top then elem */
- {"incrArrayStk", 1, 0, {OPERAND_NONE}},
- /* Incr array element; amount is top then elem then array names */
- {"incrStk", 1, 0, {OPERAND_NONE}},
- /* Incr general variable; amount is stktop then unparsed var name */
- {"incrScalar1Imm", 3, 2, {OPERAND_UINT1, OPERAND_INT1}},
- /* Incr scalar at slot op1 <= 255; amount is 2nd operand byte */
- {"incrScalarStkImm", 2, 1, {OPERAND_INT1}},
- /* Incr scalar; scalar name is stktop; incr amount is op1 */
- {"incrArray1Imm", 3, 2, {OPERAND_UINT1, OPERAND_INT1}},
- /* Incr array elem; array at slot op1 <= 255, elem is stktop,
+InstructionDesc const tclInstructionTable[] = {
+ /* Name Bytes stackEffect #Opnds Operand types */
+ {"done", 1, -1, 0, {OPERAND_NONE}},
+ /* Finish ByteCode execution and return stktop (top stack item) */
+ {"push1", 2, +1, 1, {OPERAND_UINT1}},
+ /* Push object at ByteCode objArray[op1] */
+ {"push4", 5, +1, 1, {OPERAND_UINT4}},
+ /* Push object at ByteCode objArray[op4] */
+ {"pop", 1, -1, 0, {OPERAND_NONE}},
+ /* Pop the topmost stack object */
+ {"dup", 1, +1, 0, {OPERAND_NONE}},
+ /* Duplicate the topmost stack object and push the result */
+ {"strcat", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Concatenate the top op1 items and push result */
+ {"invokeStk1", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Invoke command named objv[0]; <objc,objv> = <op1,top op1> */
+ {"invokeStk4", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Invoke command named objv[0]; <objc,objv> = <op4,top op4> */
+ {"evalStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Evaluate command in stktop using Tcl_EvalObj. */
+ {"exprStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Execute expression in stktop using Tcl_ExprStringObj. */
+
+ {"loadScalar1", 2, 1, 1, {OPERAND_LVT1}},
+ /* Load scalar variable at index op1 <= 255 in call frame */
+ {"loadScalar4", 5, 1, 1, {OPERAND_LVT4}},
+ /* Load scalar variable at index op1 >= 256 in call frame */
+ {"loadScalarStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Load scalar variable; scalar's name is stktop */
+ {"loadArray1", 2, 0, 1, {OPERAND_LVT1}},
+ /* Load array element; array at slot op1<=255, element is stktop */
+ {"loadArray4", 5, 0, 1, {OPERAND_LVT4}},
+ /* Load array element; array at slot op1 > 255, element is stktop */
+ {"loadArrayStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Load array element; element is stktop, array name is stknext */
+ {"loadStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Load general variable; unparsed variable name is stktop */
+ {"storeScalar1", 2, 0, 1, {OPERAND_LVT1}},
+ /* Store scalar variable at op1<=255 in frame; value is stktop */
+ {"storeScalar4", 5, 0, 1, {OPERAND_LVT4}},
+ /* Store scalar variable at op1 > 255 in frame; value is stktop */
+ {"storeScalarStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Store scalar; value is stktop, scalar name is stknext */
+ {"storeArray1", 2, -1, 1, {OPERAND_LVT1}},
+ /* Store array element; array at op1<=255, value is top then elem */
+ {"storeArray4", 5, -1, 1, {OPERAND_LVT4}},
+ /* Store array element; array at op1>=256, value is top then elem */
+ {"storeArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Store array element; value is stktop, then elem, array names */
+ {"storeStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Store general variable; value is stktop, then unparsed name */
+
+ {"incrScalar1", 2, 0, 1, {OPERAND_LVT1}},
+ /* Incr scalar at index op1<=255 in frame; incr amount is stktop */
+ {"incrScalarStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Incr scalar; incr amount is stktop, scalar's name is stknext */
+ {"incrArray1", 2, -1, 1, {OPERAND_LVT1}},
+ /* Incr array elem; arr at slot op1<=255, amount is top then elem */
+ {"incrArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Incr array element; amount is top then elem then array names */
+ {"incrStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Incr general variable; amount is stktop then unparsed var name */
+ {"incrScalar1Imm", 3, +1, 2, {OPERAND_LVT1, OPERAND_INT1}},
+ /* Incr scalar at slot op1 <= 255; amount is 2nd operand byte */
+ {"incrScalarStkImm", 2, 0, 1, {OPERAND_INT1}},
+ /* Incr scalar; scalar name is stktop; incr amount is op1 */
+ {"incrArray1Imm", 3, 0, 2, {OPERAND_LVT1, OPERAND_INT1}},
+ /* Incr array elem; array at slot op1 <= 255, elem is stktop,
* amount is 2nd operand byte */
- {"incrArrayStkImm", 2, 1, {OPERAND_INT1}},
- /* Incr array element; elem is top then array name, amount is op1 */
- {"incrStkImm", 2, 1, {OPERAND_INT1}},
- /* Incr general variable; unparsed name is top, amount is op1 */
-
- {"jump1", 2, 1, {OPERAND_INT1}},
- /* Jump relative to (pc + op1) */
- {"jump4", 5, 1, {OPERAND_INT4}},
- /* Jump relative to (pc + op4) */
- {"jumpTrue1", 2, 1, {OPERAND_INT1}},
- /* Jump relative to (pc + op1) if stktop expr object is true */
- {"jumpTrue4", 5, 1, {OPERAND_INT4}},
- /* Jump relative to (pc + op4) if stktop expr object is true */
- {"jumpFalse1", 2, 1, {OPERAND_INT1}},
- /* Jump relative to (pc + op1) if stktop expr object is false */
- {"jumpFalse4", 5, 1, {OPERAND_INT4}},
- /* Jump relative to (pc + op4) if stktop expr object is false */
-
- {"lor", 1, 0, {OPERAND_NONE}},
- /* Logical or: push (stknext || stktop) */
- {"land", 1, 0, {OPERAND_NONE}},
- /* Logical and: push (stknext && stktop) */
- {"bitor", 1, 0, {OPERAND_NONE}},
- /* Bitwise or: push (stknext | stktop) */
- {"bitxor", 1, 0, {OPERAND_NONE}},
- /* Bitwise xor push (stknext ^ stktop) */
- {"bitand", 1, 0, {OPERAND_NONE}},
- /* Bitwise and: push (stknext & stktop) */
- {"eq", 1, 0, {OPERAND_NONE}},
- /* Equal: push (stknext == stktop) */
- {"neq", 1, 0, {OPERAND_NONE}},
- /* Not equal: push (stknext != stktop) */
- {"lt", 1, 0, {OPERAND_NONE}},
- /* Less: push (stknext < stktop) */
- {"gt", 1, 0, {OPERAND_NONE}},
- /* Greater: push (stknext || stktop) */
- {"le", 1, 0, {OPERAND_NONE}},
- /* Logical or: push (stknext || stktop) */
- {"ge", 1, 0, {OPERAND_NONE}},
- /* Logical or: push (stknext || stktop) */
- {"lshift", 1, 0, {OPERAND_NONE}},
- /* Left shift: push (stknext << stktop) */
- {"rshift", 1, 0, {OPERAND_NONE}},
- /* Right shift: push (stknext >> stktop) */
- {"add", 1, 0, {OPERAND_NONE}},
- /* Add: push (stknext + stktop) */
- {"sub", 1, 0, {OPERAND_NONE}},
- /* Sub: push (stkext - stktop) */
- {"mult", 1, 0, {OPERAND_NONE}},
- /* Multiply: push (stknext * stktop) */
- {"div", 1, 0, {OPERAND_NONE}},
- /* Divide: push (stknext / stktop) */
- {"mod", 1, 0, {OPERAND_NONE}},
- /* Mod: push (stknext % stktop) */
- {"uplus", 1, 0, {OPERAND_NONE}},
- /* Unary plus: push +stktop */
- {"uminus", 1, 0, {OPERAND_NONE}},
- /* Unary minus: push -stktop */
- {"bitnot", 1, 0, {OPERAND_NONE}},
- /* Bitwise not: push ~stktop */
- {"not", 1, 0, {OPERAND_NONE}},
- /* Logical not: push !stktop */
- {"callBuiltinFunc1", 2, 1, {OPERAND_UINT1}},
- /* Call builtin math function with index op1; any args are on stk */
- {"callFunc1", 2, 1, {OPERAND_UINT1}},
- /* Call non-builtin func objv[0]; <objc,objv>=<op1,top op1> */
- {"tryCvtToNumeric", 1, 0, {OPERAND_NONE}},
- /* Try converting stktop to first int then double if possible. */
-
- {"break", 1, 0, {OPERAND_NONE}},
- /* Abort closest enclosing loop; if none, return TCL_BREAK code. */
- {"continue", 1, 0, {OPERAND_NONE}},
- /* Skip to next iteration of closest enclosing loop; if none,
- * return TCL_CONTINUE code. */
-
- {"foreach_start4", 5, 1, {OPERAND_UINT4}},
- /* Initialize execution of a foreach loop. Operand is aux data index
+ {"incrArrayStkImm", 2, -1, 1, {OPERAND_INT1}},
+ /* Incr array element; elem is top then array name, amount is op1 */
+ {"incrStkImm", 2, 0, 1, {OPERAND_INT1}},
+ /* Incr general variable; unparsed name is top, amount is op1 */
+
+ {"jump1", 2, 0, 1, {OPERAND_INT1}},
+ /* Jump relative to (pc + op1) */
+ {"jump4", 5, 0, 1, {OPERAND_INT4}},
+ /* Jump relative to (pc + op4) */
+ {"jumpTrue1", 2, -1, 1, {OPERAND_INT1}},
+ /* Jump relative to (pc + op1) if stktop expr object is true */
+ {"jumpTrue4", 5, -1, 1, {OPERAND_INT4}},
+ /* Jump relative to (pc + op4) if stktop expr object is true */
+ {"jumpFalse1", 2, -1, 1, {OPERAND_INT1}},
+ /* Jump relative to (pc + op1) if stktop expr object is false */
+ {"jumpFalse4", 5, -1, 1, {OPERAND_INT4}},
+ /* Jump relative to (pc + op4) if stktop expr object is false */
+
+ {"lor", 1, -1, 0, {OPERAND_NONE}},
+ /* Logical or: push (stknext || stktop) */
+ {"land", 1, -1, 0, {OPERAND_NONE}},
+ /* Logical and: push (stknext && stktop) */
+ {"bitor", 1, -1, 0, {OPERAND_NONE}},
+ /* Bitwise or: push (stknext | stktop) */
+ {"bitxor", 1, -1, 0, {OPERAND_NONE}},
+ /* Bitwise xor push (stknext ^ stktop) */
+ {"bitand", 1, -1, 0, {OPERAND_NONE}},
+ /* Bitwise and: push (stknext & stktop) */
+ {"eq", 1, -1, 0, {OPERAND_NONE}},
+ /* Equal: push (stknext == stktop) */
+ {"neq", 1, -1, 0, {OPERAND_NONE}},
+ /* Not equal: push (stknext != stktop) */
+ {"lt", 1, -1, 0, {OPERAND_NONE}},
+ /* Less: push (stknext < stktop) */
+ {"gt", 1, -1, 0, {OPERAND_NONE}},
+ /* Greater: push (stknext > stktop) */
+ {"le", 1, -1, 0, {OPERAND_NONE}},
+ /* Less or equal: push (stknext <= stktop) */
+ {"ge", 1, -1, 0, {OPERAND_NONE}},
+ /* Greater or equal: push (stknext >= stktop) */
+ {"lshift", 1, -1, 0, {OPERAND_NONE}},
+ /* Left shift: push (stknext << stktop) */
+ {"rshift", 1, -1, 0, {OPERAND_NONE}},
+ /* Right shift: push (stknext >> stktop) */
+ {"add", 1, -1, 0, {OPERAND_NONE}},
+ /* Add: push (stknext + stktop) */
+ {"sub", 1, -1, 0, {OPERAND_NONE}},
+ /* Sub: push (stkext - stktop) */
+ {"mult", 1, -1, 0, {OPERAND_NONE}},
+ /* Multiply: push (stknext * stktop) */
+ {"div", 1, -1, 0, {OPERAND_NONE}},
+ /* Divide: push (stknext / stktop) */
+ {"mod", 1, -1, 0, {OPERAND_NONE}},
+ /* Mod: push (stknext % stktop) */
+ {"uplus", 1, 0, 0, {OPERAND_NONE}},
+ /* Unary plus: push +stktop */
+ {"uminus", 1, 0, 0, {OPERAND_NONE}},
+ /* Unary minus: push -stktop */
+ {"bitnot", 1, 0, 0, {OPERAND_NONE}},
+ /* Bitwise not: push ~stktop */
+ {"not", 1, 0, 0, {OPERAND_NONE}},
+ /* Logical not: push !stktop */
+ {"callBuiltinFunc1", 2, 1, 1, {OPERAND_UINT1}},
+ /* Call builtin math function with index op1; any args are on stk */
+ {"callFunc1", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Call non-builtin func objv[0]; <objc,objv>=<op1,top op1> */
+ {"tryCvtToNumeric", 1, 0, 0, {OPERAND_NONE}},
+ /* Try converting stktop to first int then double if possible. */
+
+ {"break", 1, 0, 0, {OPERAND_NONE}},
+ /* Abort closest enclosing loop; if none, return TCL_BREAK code. */
+ {"continue", 1, 0, 0, {OPERAND_NONE}},
+ /* Skip to next iteration of closest enclosing loop; if none, return
+ * TCL_CONTINUE code. */
+
+ {"foreach_start4", 5, 0, 1, {OPERAND_AUX4}},
+ /* Initialize execution of a foreach loop. Operand is aux data index
* of the ForeachInfo structure for the foreach command. */
- {"foreach_step4", 5, 1, {OPERAND_UINT4}},
- /* "Step" or begin next iteration of foreach loop. Push 0 if to
- * terminate loop, else push 1. */
-
- {"beginCatch4", 5, 1, {OPERAND_UINT4}},
- /* Record start of catch with the operand's exception index.
- * Push the current stack depth onto a special catch stack. */
- {"endCatch", 1, 0, {OPERAND_NONE}},
- /* End of last catch. Pop the bytecode interpreter's catch stack. */
- {"pushResult", 1, 0, {OPERAND_NONE}},
- /* Push the interpreter's object result onto the stack. */
- {"pushReturnCode", 1, 0, {OPERAND_NONE}},
- /* Push interpreter's return code (e.g. TCL_OK or TCL_ERROR) as
- * a new object onto the stack. */
- {0}
-};
+ {"foreach_step4", 5, +1, 1, {OPERAND_AUX4}},
+ /* "Step" or begin next iteration of foreach loop. Push 0 if to
+ * terminate loop, else push 1. */
+
+ {"beginCatch4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Record start of catch with the operand's exception index. Push the
+ * current stack depth onto a special catch stack. */
+ {"endCatch", 1, 0, 0, {OPERAND_NONE}},
+ /* End of last catch. Pop the bytecode interpreter's catch stack. */
+ {"pushResult", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the interpreter's object result onto the stack. */
+ {"pushReturnCode", 1, +1, 0, {OPERAND_NONE}},
+ /* Push interpreter's return code (e.g. TCL_OK or TCL_ERROR) as a new
+ * object onto the stack. */
+
+ {"streq", 1, -1, 0, {OPERAND_NONE}},
+ /* Str Equal: push (stknext eq stktop) */
+ {"strneq", 1, -1, 0, {OPERAND_NONE}},
+ /* Str !Equal: push (stknext neq stktop) */
+ {"strcmp", 1, -1, 0, {OPERAND_NONE}},
+ /* Str Compare: push (stknext cmp stktop) */
+ {"strlen", 1, 0, 0, {OPERAND_NONE}},
+ /* Str Length: push (strlen stktop) */
+ {"strindex", 1, -1, 0, {OPERAND_NONE}},
+ /* Str Index: push (strindex stknext stktop) */
+ {"strmatch", 2, -1, 1, {OPERAND_INT1}},
+ /* Str Match: push (strmatch stknext stktop) opnd == nocase */
+
+ {"list", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* List: push (stk1 stk2 ... stktop) */
+ {"listIndex", 1, -1, 0, {OPERAND_NONE}},
+ /* List Index: push (listindex stknext stktop) */
+ {"listLength", 1, 0, 0, {OPERAND_NONE}},
+ /* List Len: push (listlength stktop) */
+
+ {"appendScalar1", 2, 0, 1, {OPERAND_LVT1}},
+ /* Append scalar variable at op1<=255 in frame; value is stktop */
+ {"appendScalar4", 5, 0, 1, {OPERAND_LVT4}},
+ /* Append scalar variable at op1 > 255 in frame; value is stktop */
+ {"appendArray1", 2, -1, 1, {OPERAND_LVT1}},
+ /* Append array element; array at op1<=255, value is top then elem */
+ {"appendArray4", 5, -1, 1, {OPERAND_LVT4}},
+ /* Append array element; array at op1>=256, value is top then elem */
+ {"appendArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Append array element; value is stktop, then elem, array names */
+ {"appendStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Append general variable; value is stktop, then unparsed name */
+ {"lappendScalar1", 2, 0, 1, {OPERAND_LVT1}},
+ /* Lappend scalar variable at op1<=255 in frame; value is stktop */
+ {"lappendScalar4", 5, 0, 1, {OPERAND_LVT4}},
+ /* Lappend scalar variable at op1 > 255 in frame; value is stktop */
+ {"lappendArray1", 2, -1, 1, {OPERAND_LVT1}},
+ /* Lappend array element; array at op1<=255, value is top then elem */
+ {"lappendArray4", 5, -1, 1, {OPERAND_LVT4}},
+ /* Lappend array element; array at op1>=256, value is top then elem */
+ {"lappendArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Lappend array element; value is stktop, then elem, array names */
+ {"lappendStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Lappend general variable; value is stktop, then unparsed name */
+
+ {"lindexMulti", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Lindex with generalized args, operand is number of stacked objs
+ * used: (operand-1) entries from stktop are the indices; then list to
+ * process. */
+ {"over", 5, +1, 1, {OPERAND_UINT4}},
+ /* Duplicate the arg-th element from top of stack (TOS=0) */
+ {"lsetList", 1, -2, 0, {OPERAND_NONE}},
+ /* Four-arg version of 'lset'. stktop is old value; next is new
+ * element value, next is the index list; pushes new value */
+ {"lsetFlat", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Three- or >=5-arg version of 'lset', operand is number of stacked
+ * objs: stktop is old value, next is new element value, next come
+ * (operand-2) indices; pushes the new value.
+ */
+
+ {"returnImm", 9, -1, 2, {OPERAND_INT4, OPERAND_UINT4}},
+ /* Compiled [return], code, level are operands; options and result
+ * are on the stack. */
+ {"expon", 1, -1, 0, {OPERAND_NONE}},
+ /* Binary exponentiation operator: push (stknext ** stktop) */
+
+ /*
+ * NOTE: the stack effects of expandStkTop and invokeExpanded are wrong -
+ * but it cannot be done right at compile time, the stack effect is only
+ * known at run time. The value for invokeExpanded is estimated better at
+ * compile time.
+ * See the comments further down in this file, where INST_INVOKE_EXPANDED
+ * is emitted.
+ */
+ {"expandStart", 1, 0, 0, {OPERAND_NONE}},
+ /* Start of command with {*} (expanded) arguments */
+ {"expandStkTop", 5, 0, 1, {OPERAND_UINT4}},
+ /* Expand the list at stacktop: push its elements on the stack */
+ {"invokeExpanded", 1, 0, 0, {OPERAND_NONE}},
+ /* Invoke the command marked by the last 'expandStart' */
+
+ {"listIndexImm", 5, 0, 1, {OPERAND_IDX4}},
+ /* List Index: push (lindex stktop op4) */
+ {"listRangeImm", 9, 0, 2, {OPERAND_IDX4, OPERAND_IDX4}},
+ /* List Range: push (lrange stktop op4 op4) */
+ {"startCommand", 9, 0, 2, {OPERAND_INT4,OPERAND_UINT4}},
+ /* Start of bytecoded command: op is the length of the cmd's code, op2
+ * is number of commands here */
+
+ {"listIn", 1, -1, 0, {OPERAND_NONE}},
+ /* List containment: push [lsearch stktop stknext]>=0) */
+ {"listNotIn", 1, -1, 0, {OPERAND_NONE}},
+ /* List negated containment: push [lsearch stktop stknext]<0) */
+
+ {"pushReturnOpts", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the interpreter's return option dictionary as an object on the
+ * stack. */
+ {"returnStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Compiled [return]; options and result are on the stack, code and
+ * level are in the options. */
+
+ {"dictGet", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* The top op4 words (min 1) are a key path into the dictionary just
+ * below the keys on the stack, and all those values are replaced by
+ * the value read out of that key-path (like [dict get]).
+ * Stack: ... dict key1 ... keyN => ... value */
+ {"dictSet", 9, INT_MIN, 2, {OPERAND_UINT4, OPERAND_LVT4}},
+ /* Update a dictionary value such that the keys are a path pointing to
+ * the value. op4#1 = numKeys, op4#2 = LVTindex
+ * Stack: ... key1 ... keyN value => ... newDict */
+ {"dictUnset", 9, INT_MIN, 2, {OPERAND_UINT4, OPERAND_LVT4}},
+ /* Update a dictionary value such that the keys are not a path pointing
+ * to any value. op4#1 = numKeys, op4#2 = LVTindex
+ * Stack: ... key1 ... keyN => ... newDict */
+ {"dictIncrImm", 9, 0, 2, {OPERAND_INT4, OPERAND_LVT4}},
+ /* Update a dictionary value such that the value pointed to by key is
+ * incremented by some value (or set to it if the key isn't in the
+ * dictionary at all). op4#1 = incrAmount, op4#2 = LVTindex
+ * Stack: ... key => ... newDict */
+ {"dictAppend", 5, -1, 1, {OPERAND_LVT4}},
+ /* Update a dictionary value such that the value pointed to by key has
+ * some value string-concatenated onto it. op4 = LVTindex
+ * Stack: ... key valueToAppend => ... newDict */
+ {"dictLappend", 5, -1, 1, {OPERAND_LVT4}},
+ /* Update a dictionary value such that the value pointed to by key has
+ * some value list-appended onto it. op4 = LVTindex
+ * Stack: ... key valueToAppend => ... newDict */
+ {"dictFirst", 5, +2, 1, {OPERAND_LVT4}},
+ /* Begin iterating over the dictionary, using the local scalar
+ * indicated by op4 to hold the iterator state. The local scalar
+ * should not refer to a named variable as the value is not wholly
+ * managed correctly.
+ * Stack: ... dict => ... value key doneBool */
+ {"dictNext", 5, +3, 1, {OPERAND_LVT4}},
+ /* Get the next iteration from the iterator in op4's local scalar.
+ * Stack: ... => ... value key doneBool */
+ {"dictDone", 5, 0, 1, {OPERAND_LVT4}},
+ /* Terminate the iterator in op4's local scalar. Use unsetScalar
+ * instead (with 0 for flags). */
+ {"dictUpdateStart", 9, 0, 2, {OPERAND_LVT4, OPERAND_AUX4}},
+ /* Create the variables (described in the aux data referred to by the
+ * second immediate argument) to mirror the state of the dictionary in
+ * the variable referred to by the first immediate argument. The list
+ * of keys (top of the stack, not poppsed) must be the same length as
+ * the list of variables.
+ * Stack: ... keyList => ... keyList */
+ {"dictUpdateEnd", 9, -1, 2, {OPERAND_LVT4, OPERAND_AUX4}},
+ /* Reflect the state of local variables (described in the aux data
+ * referred to by the second immediate argument) back to the state of
+ * the dictionary in the variable referred to by the first immediate
+ * argument. The list of keys (popped from the stack) must be the same
+ * length as the list of variables.
+ * Stack: ... keyList => ... */
+ {"jumpTable", 5, -1, 1, {OPERAND_AUX4}},
+ /* Jump according to the jump-table (in AuxData as indicated by the
+ * operand) and the argument popped from the list. Always executes the
+ * next instruction if no match against the table's entries was found.
+ * Stack: ... value => ...
+ * Note that the jump table contains offsets relative to the PC when
+ * it points to this instruction; the code is relocatable. */
+ {"upvar", 5, -1, 1, {OPERAND_LVT4}},
+ /* finds level and otherName in stack, links to local variable at
+ * index op1. Leaves the level on stack. */
+ {"nsupvar", 5, -1, 1, {OPERAND_LVT4}},
+ /* finds namespace and otherName in stack, links to local variable at
+ * index op1. Leaves the namespace on stack. */
+ {"variable", 5, -1, 1, {OPERAND_LVT4}},
+ /* finds namespace and otherName in stack, links to local variable at
+ * index op1. Leaves the namespace on stack. */
+ {"syntax", 9, -1, 2, {OPERAND_INT4, OPERAND_UINT4}},
+ /* Compiled bytecodes to signal syntax error. Equivalent to returnImm
+ * except for the ERR_ALREADY_LOGGED flag in the interpreter. */
+ {"reverse", 5, 0, 1, {OPERAND_UINT4}},
+ /* Reverse the order of the arg elements at the top of stack */
+
+ {"regexp", 2, -1, 1, {OPERAND_INT1}},
+ /* Regexp: push (regexp stknext stktop) opnd == nocase */
+
+ {"existScalar", 5, 1, 1, {OPERAND_LVT4}},
+ /* Test if scalar variable at index op1 in call frame exists */
+ {"existArray", 5, 0, 1, {OPERAND_LVT4}},
+ /* Test if array element exists; array at slot op1, element is
+ * stktop */
+ {"existArrayStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Test if array element exists; element is stktop, array name is
+ * stknext */
+ {"existStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Test if general variable exists; unparsed variable name is stktop*/
+
+ {"nop", 1, 0, 0, {OPERAND_NONE}},
+ /* Do nothing */
+ {"returnCodeBranch", 1, -1, 0, {OPERAND_NONE}},
+ /* Jump to next instruction based on the return code on top of stack
+ * ERROR: +1; RETURN: +3; BREAK: +5; CONTINUE: +7;
+ * Other non-OK: +9
+ */
+ {"unsetScalar", 6, 0, 2, {OPERAND_UINT1, OPERAND_LVT4}},
+ /* Make scalar variable at index op2 in call frame cease to exist;
+ * op1 is 1 for errors on problems, 0 otherwise */
+ {"unsetArray", 6, -1, 2, {OPERAND_UINT1, OPERAND_LVT4}},
+ /* Make array element cease to exist; array at slot op2, element is
+ * stktop; op1 is 1 for errors on problems, 0 otherwise */
+ {"unsetArrayStk", 2, -2, 1, {OPERAND_UINT1}},
+ /* Make array element cease to exist; element is stktop, array name is
+ * stknext; op1 is 1 for errors on problems, 0 otherwise */
+ {"unsetStk", 2, -1, 1, {OPERAND_UINT1}},
+ /* Make general variable cease to exist; unparsed variable name is
+ * stktop; op1 is 1 for errors on problems, 0 otherwise */
+
+ {"dictExpand", 1, -1, 0, {OPERAND_NONE}},
+ /* Probe into a dict and extract it (or a subdict of it) into
+ * variables with matched names. Produces list of keys bound as
+ * result. Part of [dict with].
+ * Stack: ... dict path => ... keyList */
+ {"dictRecombineStk", 1, -3, 0, {OPERAND_NONE}},
+ /* Map variable contents back into a dictionary in a variable. Part of
+ * [dict with].
+ * Stack: ... dictVarName path keyList => ... */
+ {"dictRecombineImm", 5, -2, 1, {OPERAND_LVT4}},
+ /* Map variable contents back into a dictionary in the local variable
+ * indicated by the LVT index. Part of [dict with].
+ * Stack: ... path keyList => ... */
+ {"dictExists", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* The top op4 words (min 1) are a key path into the dictionary just
+ * below the keys on the stack, and all those values are replaced by a
+ * boolean indicating whether it is possible to read out a value from
+ * that key-path (like [dict exists]).
+ * Stack: ... dict key1 ... keyN => ... boolean */
+ {"verifyDict", 1, -1, 0, {OPERAND_NONE}},
+ /* Verifies that the word on the top of the stack is a dictionary,
+ * popping it if it is and throwing an error if it is not.
+ * Stack: ... value => ... */
+
+ {"strmap", 1, -2, 0, {OPERAND_NONE}},
+ /* Simplified version of [string map] that only applies one change
+ * string, and only case-sensitively.
+ * Stack: ... from to string => ... changedString */
+ {"strfind", 1, -1, 0, {OPERAND_NONE}},
+ /* Find the first index of a needle string in a haystack string,
+ * producing the index (integer) or -1 if nothing found.
+ * Stack: ... needle haystack => ... index */
+ {"strrfind", 1, -1, 0, {OPERAND_NONE}},
+ /* Find the last index of a needle string in a haystack string,
+ * producing the index (integer) or -1 if nothing found.
+ * Stack: ... needle haystack => ... index */
+ {"strrangeImm", 9, 0, 2, {OPERAND_IDX4, OPERAND_IDX4}},
+ /* String Range: push (string range stktop op4 op4) */
+ {"strrange", 1, -2, 0, {OPERAND_NONE}},
+ /* String Range with non-constant arguments.
+ * Stack: ... string idxA idxB => ... substring */
+
+ {"yield", 1, 0, 0, {OPERAND_NONE}},
+ /* Makes the current coroutine yield the value at the top of the
+ * stack, and places the response back on top of the stack when it
+ * resumes.
+ * Stack: ... valueToYield => ... resumeValue */
+ {"coroName", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the name of the interpreter's current coroutine as an object
+ * on the stack. */
+ {"tailcall", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Do a tailcall with the opnd items on the stack as the thing to
+ * tailcall to; opnd must be greater than 0 for the semantics to work
+ * right. */
+
+ {"currentNamespace", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the name of the interpreter's current namespace as an object
+ * on the stack. */
+ {"infoLevelNumber", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the stack depth (i.e., [info level]) of the interpreter as an
+ * object on the stack. */
+ {"infoLevelArgs", 1, 0, 0, {OPERAND_NONE}},
+ /* Push the argument words to a stack depth (i.e., [info level <n>])
+ * of the interpreter as an object on the stack.
+ * Stack: ... depth => ... argList */
+ {"resolveCmd", 1, 0, 0, {OPERAND_NONE}},
+ /* Resolves the command named on the top of the stack to its fully
+ * qualified version, or produces the empty string if no such command
+ * exists. Never generates errors.
+ * Stack: ... cmdName => ... fullCmdName */
+
+ {"tclooSelf", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the identity of the current TclOO object (i.e., the name of
+ * its current public access command) on the stack. */
+ {"tclooClass", 1, 0, 0, {OPERAND_NONE}},
+ /* Push the class of the TclOO object named at the top of the stack
+ * onto the stack.
+ * Stack: ... object => ... class */
+ {"tclooNamespace", 1, 0, 0, {OPERAND_NONE}},
+ /* Push the namespace of the TclOO object named at the top of the
+ * stack onto the stack.
+ * Stack: ... object => ... namespace */
+ {"tclooIsObject", 1, 0, 0, {OPERAND_NONE}},
+ /* Push whether the value named at the top of the stack is a TclOO
+ * object (i.e., a boolean). Can corrupt the interpreter result
+ * despite not throwing, so not safe for use in a post-exception
+ * context.
+ * Stack: ... value => ... boolean */
+
+ {"arrayExistsStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Looks up the element on the top of the stack and tests whether it
+ * is an array. Pushes a boolean describing whether this is the
+ * case. Also runs the whole-array trace on the named variable, so can
+ * throw anything.
+ * Stack: ... varName => ... boolean */
+ {"arrayExistsImm", 5, +1, 1, {OPERAND_UINT4}},
+ /* Looks up the variable indexed by opnd and tests whether it is an
+ * array. Pushes a boolean describing whether this is the case. Also
+ * runs the whole-array trace on the named variable, so can throw
+ * anything.
+ * Stack: ... => ... boolean */
+ {"arrayMakeStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Forces the element on the top of the stack to be the name of an
+ * array.
+ * Stack: ... varName => ... */
+ {"arrayMakeImm", 5, 0, 1, {OPERAND_UINT4}},
+ /* Forces the variable indexed by opnd to be an array. Does not touch
+ * the stack. */
+
+ {"invokeReplace", 6, INT_MIN, 2, {OPERAND_UINT4,OPERAND_UINT1}},
+ /* Invoke command named objv[0], replacing the first two words with
+ * the word at the top of the stack;
+ * <objc,objv> = <op4,top op4 after popping 1> */
+
+ {"listConcat", 1, -1, 0, {OPERAND_NONE}},
+ /* Concatenates the two lists at the top of the stack into a single
+ * list and pushes that resulting list onto the stack.
+ * Stack: ... list1 list2 => ... [lconcat list1 list2] */
+
+ {"expandDrop", 1, 0, 0, {OPERAND_NONE}},
+ /* Drops an element from the auxiliary stack, popping stack elements
+ * until the matching stack depth is reached. */
+
+ /* New foreach implementation */
+ {"foreach_start", 5, +2, 1, {OPERAND_AUX4}},
+ /* Initialize execution of a foreach loop. Operand is aux data index
+ * of the ForeachInfo structure for the foreach command. It pushes 2
+ * elements which hold runtime params for foreach_step, they are later
+ * dropped by foreach_end together with the value lists. NOTE that the
+ * iterator-tracker and info reference must not be passed to bytecodes
+ * that handle normal Tcl values. NOTE that this instruction jumps to
+ * the foreach_step instruction paired with it; the stack info below
+ * is only nominal.
+ * Stack: ... listObjs... => ... listObjs... iterTracker info */
+ {"foreach_step", 1, 0, 0, {OPERAND_NONE}},
+ /* "Step" or begin next iteration of foreach loop. Assigns to foreach
+ * iteration variables. May jump to straight after the foreach_start
+ * that pushed the iterTracker and info values. MUST be followed
+ * immediately by a foreach_end.
+ * Stack: ... listObjs... iterTracker info =>
+ * ... listObjs... iterTracker info */
+ {"foreach_end", 1, 0, 0, {OPERAND_NONE}},
+ /* Clean up a foreach loop by dropping the info value, the tracker
+ * value and the lists that were being iterated over.
+ * Stack: ... listObjs... iterTracker info => ... */
+ {"lmap_collect", 1, -1, 0, {OPERAND_NONE}},
+ /* Appends the value at the top of the stack to the list located on
+ * the stack the "other side" of the foreach-related values.
+ * Stack: ... collector listObjs... iterTracker info value =>
+ * ... collector listObjs... iterTracker info */
+
+ {"strtrim", 1, -1, 0, {OPERAND_NONE}},
+ /* [string trim] core: removes the characters (designated by the value
+ * at the top of the stack) from both ends of the string and pushes
+ * the resulting string.
+ * Stack: ... string charset => ... trimmedString */
+ {"strtrimLeft", 1, -1, 0, {OPERAND_NONE}},
+ /* [string trimleft] core: removes the characters (designated by the
+ * value at the top of the stack) from the left of the string and
+ * pushes the resulting string.
+ * Stack: ... string charset => ... trimmedString */
+ {"strtrimRight", 1, -1, 0, {OPERAND_NONE}},
+ /* [string trimright] core: removes the characters (designated by the
+ * value at the top of the stack) from the right of the string and
+ * pushes the resulting string.
+ * Stack: ... string charset => ... trimmedString */
+
+ {"concatStk", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Wrapper round Tcl_ConcatObj(), used for [concat] and [eval]. opnd
+ * is number of values to concatenate.
+ * Operation: push concat(stk1 stk2 ... stktop) */
+
+ {"strcaseUpper", 1, 0, 0, {OPERAND_NONE}},
+ /* [string toupper] core: converts whole string to upper case using
+ * the default (extended "C" locale) rules.
+ * Stack: ... string => ... newString */
+ {"strcaseLower", 1, 0, 0, {OPERAND_NONE}},
+ /* [string tolower] core: converts whole string to upper case using
+ * the default (extended "C" locale) rules.
+ * Stack: ... string => ... newString */
+ {"strcaseTitle", 1, 0, 0, {OPERAND_NONE}},
+ /* [string totitle] core: converts whole string to upper case using
+ * the default (extended "C" locale) rules.
+ * Stack: ... string => ... newString */
+ {"strreplace", 1, -3, 0, {OPERAND_NONE}},
+ /* [string replace] core: replaces a non-empty range of one string
+ * with the contents of another.
+ * Stack: ... string fromIdx toIdx replacement => ... newString */
+
+ {"originCmd", 1, 0, 0, {OPERAND_NONE}},
+ /* Reports which command was the origin (via namespace import chain)
+ * of the command named on the top of the stack.
+ * Stack: ... cmdName => ... fullOriginalCmdName */
+
+ {"tclooNext", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Call the next item on the TclOO call chain, passing opnd arguments
+ * (min 1, max 255, *includes* "next"). The result of the invoked
+ * method implementation will be pushed on the stack in place of the
+ * arguments (similar to invokeStk).
+ * Stack: ... "next" arg2 arg3 -- argN => ... result */
+ {"tclooNextClass", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Call the following item on the TclOO call chain defined by class
+ * className, passing opnd arguments (min 2, max 255, *includes*
+ * "nextto" and the class name). The result of the invoked method
+ * implementation will be pushed on the stack in place of the
+ * arguments (similar to invokeStk).
+ * Stack: ... "nextto" className arg3 arg4 -- argN => ... result */
+
+ {"yieldToInvoke", 1, 0, 0, {OPERAND_NONE}},
+ /* Makes the current coroutine yield the value at the top of the
+ * stack, invoking the given command/args with resolution in the given
+ * namespace (all packed into a list), and places the list of values
+ * that are the response back on top of the stack when it resumes.
+ * Stack: ... [list ns cmd arg1 ... argN] => ... resumeList */
+
+ {"numericType", 1, 0, 0, {OPERAND_NONE}},
+ /* Pushes the numeric type code of the word at the top of the stack.
+ * Stack: ... value => ... typeCode */
+ {"tryCvtToBoolean", 1, +1, 0, {OPERAND_NONE}},
+ /* Try converting stktop to boolean if possible. No errors.
+ * Stack: ... value => ... value isStrictBool */
+ {"strclass", 2, 0, 1, {OPERAND_SCLS1}},
+ /* See if all the characters of the given string are a member of the
+ * specified (by opnd) character class. Note that an empty string will
+ * satisfy the class check (standard definition of "all").
+ * Stack: ... stringValue => ... boolean */
+
+ {NULL, 0, 0, 0, {OPERAND_NONE}}
+};
+
/*
* Prototypes for procedures defined later in this file:
*/
-static void DupByteCodeInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
- Tcl_Obj *copyPtr));
-static unsigned char * EncodeCmdLocMap _ANSI_ARGS_((
- CompileEnv *envPtr, ByteCode *codePtr,
- unsigned char *startPtr));
-static void EnterCmdExtentData _ANSI_ARGS_((
- CompileEnv *envPtr, int cmdNumber,
- int numSrcBytes, int numCodeBytes));
-static void EnterCmdStartData _ANSI_ARGS_((
- CompileEnv *envPtr, int cmdNumber,
- int srcOffset, int codeOffset));
-static void FreeByteCodeInternalRep _ANSI_ARGS_((
- Tcl_Obj *objPtr));
-static int GetCmdLocEncodingSize _ANSI_ARGS_((
- CompileEnv *envPtr));
-static void LogCompilationInfo _ANSI_ARGS_((Tcl_Interp *interp,
- char *script, char *command, int length));
+static ByteCode * CompileSubstObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int flags);
+static void DupByteCodeInternalRep(Tcl_Obj *srcPtr,
+ Tcl_Obj *copyPtr);
+static unsigned char * EncodeCmdLocMap(CompileEnv *envPtr,
+ ByteCode *codePtr, unsigned char *startPtr);
+static void EnterCmdExtentData(CompileEnv *envPtr,
+ int cmdNumber, int numSrcBytes, int numCodeBytes);
+static void EnterCmdStartData(CompileEnv *envPtr,
+ int cmdNumber, int srcOffset, int codeOffset);
+static void FreeByteCodeInternalRep(Tcl_Obj *objPtr);
+static void FreeSubstCodeInternalRep(Tcl_Obj *objPtr);
+static int GetCmdLocEncodingSize(CompileEnv *envPtr);
+static int IsCompactibleCompileEnv(Tcl_Interp *interp,
+ CompileEnv *envPtr);
#ifdef TCL_COMPILE_STATS
-static void RecordByteCodeStats _ANSI_ARGS_((
- ByteCode *codePtr));
+static void RecordByteCodeStats(ByteCode *codePtr);
#endif /* TCL_COMPILE_STATS */
-static int SetByteCodeFromAny _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr));
+static void RegisterAuxDataType(const AuxDataType *typePtr);
+static int SetByteCodeFromAny(Tcl_Interp *interp,
+ Tcl_Obj *objPtr);
+static void StartExpanding(CompileEnv *envPtr);
+static int FormatInstruction(ByteCode *codePtr,
+ const unsigned char *pc, Tcl_Obj *bufferObj);
+static void PrintSourceToObj(Tcl_Obj *appendObj,
+ const char *stringPtr, int maxChars);
+static void UpdateStringOfInstName(Tcl_Obj *objPtr);
+
+/*
+ * TIP #280: Helper for building the per-word line information of all compiled
+ * commands.
+ */
+static void EnterCmdWordData(ExtCmdLoc *eclPtr, int srcOffset,
+ Tcl_Token *tokenPtr, const char *cmd, int len,
+ int numWords, int line, int *clNext, int **lines,
+ CompileEnv *envPtr);
+static void ReleaseCmdWordData(ExtCmdLoc *eclPtr);
/*
- * The structure below defines the bytecode Tcl object type by
- * means of procedures that can be invoked by generic object code.
+ * The structure below defines the bytecode Tcl object type by means of
+ * procedures that can be invoked by generic object code.
*/
-Tcl_ObjType tclByteCodeType = {
- "bytecode", /* name */
- FreeByteCodeInternalRep, /* freeIntRepProc */
- DupByteCodeInternalRep, /* dupIntRepProc */
- (Tcl_UpdateStringProc *) NULL, /* updateStringProc */
- SetByteCodeFromAny /* setFromAnyProc */
+const Tcl_ObjType tclByteCodeType = {
+ "bytecode", /* name */
+ FreeByteCodeInternalRep, /* freeIntRepProc */
+ DupByteCodeInternalRep, /* dupIntRepProc */
+ NULL, /* updateStringProc */
+ SetByteCodeFromAny /* setFromAnyProc */
};
+
+/*
+ * The structure below defines a bytecode Tcl object type to hold the
+ * compiled bytecode for the [subst]itution of Tcl values.
+ */
+
+static const Tcl_ObjType substCodeType = {
+ "substcode", /* name */
+ FreeSubstCodeInternalRep, /* freeIntRepProc */
+ DupByteCodeInternalRep, /* dupIntRepProc - shared with bytecode */
+ NULL, /* updateStringProc */
+ NULL, /* setFromAnyProc */
+};
+
+/*
+ * The structure below defines an instruction name Tcl object to allow
+ * reporting of inner contexts in errorstack without string allocation.
+ */
+
+static const Tcl_ObjType tclInstNameType = {
+ "instname", /* name */
+ NULL, /* freeIntRepProc */
+ NULL, /* dupIntRepProc */
+ UpdateStringOfInstName, /* updateStringProc */
+ NULL, /* setFromAnyProc */
+};
+
+/*
+ * Helper macros.
+ */
+
+#define TclIncrUInt4AtPtr(ptr, delta) \
+ TclStoreInt4AtPtr(TclGetUInt4AtPtr(ptr)+(delta), (ptr));
/*
*----------------------------------------------------------------------
@@ -261,124 +748,148 @@ Tcl_ObjType tclByteCodeType = {
*
* Part of the bytecode Tcl object type implementation. Attempts to
* generate an byte code internal form for the Tcl object "objPtr" by
- * compiling its string representation. This function also takes
- * a hook procedure that will be invoked to perform any needed post
- * processing on the compilation results before generating byte
- * codes.
+ * compiling its string representation. This function also takes a hook
+ * procedure that will be invoked to perform any needed post processing
+ * on the compilation results before generating byte codes. interp is
+ * compilation context and may not be NULL.
*
* Results:
* The return value is a standard Tcl object result. If an error occurs
* during compilation, an error message is left in the interpreter's
- * result unless "interp" is NULL.
+ * result.
*
* Side effects:
* Frees the old internal representation. If no error occurs, then the
- * compiled code is stored as "objPtr"s bytecode representation.
- * Also, if debugging, initializes the "tcl_traceCompile" Tcl variable
- * used to trace compilations.
+ * compiled code is stored as "objPtr"s bytecode representation. Also, if
+ * debugging, initializes the "tcl_traceCompile" Tcl variable used to
+ * trace compilations.
*
*----------------------------------------------------------------------
*/
int
-TclSetByteCodeFromAny(interp, objPtr, hookProc, clientData)
- Tcl_Interp *interp; /* The interpreter for which the code is
- * being compiled. Must not be NULL. */
- Tcl_Obj *objPtr; /* The object to make a ByteCode object. */
- CompileHookProc *hookProc; /* Procedure to invoke after compilation. */
- ClientData clientData; /* Hook procedure private data. */
+TclSetByteCodeFromAny(
+ Tcl_Interp *interp, /* The interpreter for which the code is being
+ * compiled. Must not be NULL. */
+ Tcl_Obj *objPtr, /* The object to make a ByteCode object. */
+ CompileHookProc *hookProc, /* Procedure to invoke after compilation. */
+ ClientData clientData) /* Hook procedure private data. */
{
Interp *iPtr = (Interp *) interp;
- CompileEnv compEnv; /* Compilation environment structure
- * allocated in frame. */
- LiteralTable *localTablePtr = &(compEnv.localLitTable);
- register AuxData *auxDataPtr;
- LiteralEntry *entryPtr;
- register int i;
- int length, nested, result;
- char *string;
+ CompileEnv compEnv; /* Compilation environment structure allocated
+ * in frame. */
+ int length, result = TCL_OK;
+ const char *stringPtr;
+ Proc *procPtr = iPtr->compiledProcPtr;
+ ContLineLoc *clLocPtr;
+#ifdef TCL_COMPILE_DEBUG
if (!traceInitialized) {
- if (Tcl_LinkVar(interp, "tcl_traceCompile",
- (char *) &tclTraceCompile, TCL_LINK_INT) != TCL_OK) {
- panic("SetByteCodeFromAny: unable to create link for tcl_traceCompile variable");
- }
- traceInitialized = 1;
+ if (Tcl_LinkVar(interp, "tcl_traceCompile",
+ (char *) &tclTraceCompile, TCL_LINK_INT) != TCL_OK) {
+ Tcl_Panic("SetByteCodeFromAny: unable to create link for tcl_traceCompile variable");
+ }
+ traceInitialized = 1;
}
+#endif
- if (iPtr->evalFlags & TCL_BRACKET_TERM) {
- nested = 1;
- } else {
- nested = 0;
+ stringPtr = TclGetStringFromObj(objPtr, &length);
+
+ /*
+ * TIP #280: Pick up the CmdFrame in which the BC compiler was invoked and
+ * use to initialize the tracking in the compiler. This information was
+ * stored by TclCompEvalObj and ProcCompileProc.
+ */
+
+ TclInitCompileEnv(interp, &compEnv, stringPtr, length,
+ iPtr->invokeCmdFramePtr, iPtr->invokeWord);
+
+ /*
+ * Now we check if we have data about invisible continuation lines for the
+ * script, and make it available to the compile environment, if so.
+ *
+ * It is not clear if the script Tcl_Obj* can be free'd while the compiler
+ * is using it, leading to the release of the associated ContLineLoc
+ * structure as well. To ensure that the latter doesn't happen we set a
+ * lock on it. We release this lock in the function TclFreeCompileEnv(),
+ * found in this file. The "lineCLPtr" hashtable is managed in the file
+ * "tclObj.c".
+ */
+
+ clLocPtr = TclContinuationsGet(objPtr);
+ if (clLocPtr) {
+ compEnv.clNext = &clLocPtr->loc[0];
}
- string = Tcl_GetStringFromObj(objPtr, &length);
- TclInitCompileEnv(interp, &compEnv, string, length);
- result = TclCompileScript(interp, string, length, nested, &compEnv);
- if (result == TCL_OK) {
- /*
- * Successful compilation. Add a "done" instruction at the end.
- */
+ TclCompileScript(interp, stringPtr, length, &compEnv);
- compEnv.numSrcBytes = iPtr->termOffset;
- TclEmitOpcode(INST_DONE, &compEnv);
+ /*
+ * Successful compilation. Add a "done" instruction at the end.
+ */
- /*
- * Invoke the compilation hook procedure if one exists.
- */
+ TclEmitOpcode(INST_DONE, &compEnv);
- if (hookProc) {
- result = (*hookProc)(interp, &compEnv, clientData);
+ /*
+ * Check for optimizations!
+ *
+ * Test if the generated code is free of most hazards; if so, recompile
+ * but with generation of INST_START_CMD disabled. This produces somewhat
+ * faster code in some cases, and more compact code in more.
+ */
+
+ if (Tcl_GetMaster(interp) == NULL &&
+ !Tcl_LimitTypeEnabled(interp, TCL_LIMIT_COMMANDS|TCL_LIMIT_TIME)
+ && IsCompactibleCompileEnv(interp, &compEnv)) {
+ TclFreeCompileEnv(&compEnv);
+ iPtr->compiledProcPtr = procPtr;
+ TclInitCompileEnv(interp, &compEnv, stringPtr, length,
+ iPtr->invokeCmdFramePtr, iPtr->invokeWord);
+ if (clLocPtr) {
+ compEnv.clNext = &clLocPtr->loc[0];
}
+ compEnv.atCmdStart = 2; /* The disabling magic. */
+ TclCompileScript(interp, stringPtr, length, &compEnv);
+ assert (compEnv.atCmdStart > 1);
+ TclEmitOpcode(INST_DONE, &compEnv);
+ assert (compEnv.atCmdStart > 1);
+ }
+
+ /*
+ * Apply some peephole optimizations that can cross specific/generic
+ * instruction generator boundaries.
+ */
+
+ if (iPtr->extra.optimizer) {
+ (iPtr->extra.optimizer)(&compEnv);
+ }
+
+ /*
+ * Invoke the compilation hook procedure if one exists.
+ */
+
+ if (hookProc) {
+ result = hookProc(interp, &compEnv, clientData);
+ }
+
+ /*
+ * Change the object into a ByteCode object. Ownership of the literal
+ * objects and aux data items is given to the ByteCode object.
+ */
- /*
- * Change the object into a ByteCode object. Ownership of the literal
- * objects and aux data items is given to the ByteCode object.
- */
-
#ifdef TCL_COMPILE_DEBUG
- TclVerifyLocalLiteralTable(&compEnv);
+ TclVerifyLocalLiteralTable(&compEnv);
#endif /*TCL_COMPILE_DEBUG*/
+ if (result == TCL_OK) {
TclInitByteCodeObj(objPtr, &compEnv);
#ifdef TCL_COMPILE_DEBUG
- if (tclTraceCompile == 2) {
+ if (tclTraceCompile >= 2) {
TclPrintByteCodeObj(interp, objPtr);
+ fflush(stdout);
}
#endif /* TCL_COMPILE_DEBUG */
}
-
- if (result != TCL_OK) {
- /*
- * Compilation errors.
- */
-
- entryPtr = compEnv.literalArrayPtr;
- for (i = 0; i < compEnv.literalArrayNext; i++) {
- TclReleaseLiteral(interp, entryPtr->objPtr);
- entryPtr++;
- }
-#ifdef TCL_COMPILE_DEBUG
- TclVerifyGlobalLiteralTable(iPtr);
-#endif /*TCL_COMPILE_DEBUG*/
-
- auxDataPtr = compEnv.auxDataArrayPtr;
- for (i = 0; i < compEnv.auxDataArrayNext; i++) {
- if (auxDataPtr->type->freeProc != NULL) {
- auxDataPtr->type->freeProc(auxDataPtr->clientData);
- }
- auxDataPtr++;
- }
- }
-
- /*
- * Free storage allocated during compilation.
- */
-
- if (localTablePtr->buckets != localTablePtr->staticBuckets) {
- ckfree((char *) localTablePtr->buckets);
- }
TclFreeCompileEnv(&compEnv);
return result;
}
@@ -399,21 +910,23 @@ TclSetByteCodeFromAny(interp, objPtr, hookProc, clientData)
*
* Side effects:
* Frees the old internal representation. If no error occurs, then the
- * compiled code is stored as "objPtr"s bytecode representation.
- * Also, if debugging, initializes the "tcl_traceCompile" Tcl variable
- * used to trace compilations.
+ * compiled code is stored as "objPtr"s bytecode representation. Also, if
+ * debugging, initializes the "tcl_traceCompile" Tcl variable used to
+ * trace compilations.
*
*----------------------------------------------------------------------
*/
static int
-SetByteCodeFromAny(interp, objPtr)
- Tcl_Interp *interp; /* The interpreter for which the code is
- * being compiled. Must not be NULL. */
- Tcl_Obj *objPtr; /* The object to make a ByteCode object. */
+SetByteCodeFromAny(
+ Tcl_Interp *interp, /* The interpreter for which the code is being
+ * compiled. Must not be NULL. */
+ Tcl_Obj *objPtr) /* The object to make a ByteCode object. */
{
- return TclSetByteCodeFromAny(interp, objPtr,
- (CompileHookProc *) NULL, (ClientData) NULL);
+ if (interp == NULL) {
+ return TCL_ERROR;
+ }
+ return TclSetByteCodeFromAny(interp, objPtr, NULL, NULL);
}
/*
@@ -421,8 +934,8 @@ SetByteCodeFromAny(interp, objPtr)
*
* DupByteCodeInternalRep --
*
- * Part of the bytecode Tcl object type implementation. However, it
- * does not copy the internal representation of a bytecode Tcl_Obj, but
+ * Part of the bytecode Tcl object type implementation. However, it does
+ * not copy the internal representation of a bytecode Tcl_Obj, but
* instead leaves the new object untyped (with a NULL type pointer).
* Code will be compiled for the new object only if necessary.
*
@@ -436,9 +949,9 @@ SetByteCodeFromAny(interp, objPtr)
*/
static void
-DupByteCodeInternalRep(srcPtr, copyPtr)
- Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
- Tcl_Obj *copyPtr; /* Object with internal rep to set. */
+DupByteCodeInternalRep(
+ Tcl_Obj *srcPtr, /* Object with internal rep to copy. */
+ Tcl_Obj *copyPtr) /* Object with internal rep to set. */
{
return;
}
@@ -448,35 +961,32 @@ DupByteCodeInternalRep(srcPtr, copyPtr)
*
* FreeByteCodeInternalRep --
*
- * Part of the bytecode Tcl object type implementation. Frees the
- * storage associated with a bytecode object's internal representation
- * unless its code is actively being executed.
+ * Part of the bytecode Tcl object type implementation. Frees the storage
+ * associated with a bytecode object's internal representation unless its
+ * code is actively being executed.
*
* Results:
* None.
*
* Side effects:
- * The bytecode object's internal rep is marked invalid and its
- * code gets freed unless the code is actively being executed.
- * In that case the cleanup is delayed until the last execution
- * of the code completes.
+ * The bytecode object's internal rep is marked invalid and its code gets
+ * freed unless the code is actively being executed. In that case the
+ * cleanup is delayed until the last execution of the code completes.
*
*----------------------------------------------------------------------
*/
static void
-FreeByteCodeInternalRep(objPtr)
- register Tcl_Obj *objPtr; /* Object whose internal rep to free. */
+FreeByteCodeInternalRep(
+ register Tcl_Obj *objPtr) /* Object whose internal rep to free. */
{
- register ByteCode *codePtr =
- (ByteCode *) objPtr->internalRep.otherValuePtr;
+ register ByteCode *codePtr = objPtr->internalRep.twoPtrValue.ptr1;
+ objPtr->typePtr = NULL;
codePtr->refCount--;
if (codePtr->refCount <= 0) {
TclCleanupByteCode(codePtr);
}
- objPtr->typePtr = NULL;
- objPtr->internalRep.otherValuePtr = NULL;
}
/*
@@ -492,22 +1002,22 @@ FreeByteCodeInternalRep(objPtr)
* None.
*
* Side effects:
- * Frees objPtr's bytecode internal representation and sets its type
- * and objPtr->internalRep.otherValuePtr NULL. Also releases its
- * literals and frees its auxiliary data items.
+ * Frees objPtr's bytecode internal representation and sets its type NULL
+ * Also releases its literals and frees its auxiliary data items.
*
*----------------------------------------------------------------------
*/
void
-TclCleanupByteCode(codePtr)
- register ByteCode *codePtr; /* Points to the ByteCode to free. */
+TclCleanupByteCode(
+ register ByteCode *codePtr) /* Points to the ByteCode to free. */
{
Tcl_Interp *interp = (Tcl_Interp *) *codePtr->interpHandle;
+ Interp *iPtr = (Interp *) interp;
int numLitObjects = codePtr->numLitObjects;
int numAuxDataItems = codePtr->numAuxDataItems;
- register Tcl_Obj **objArrayPtr;
- register AuxData *auxDataPtr;
+ register Tcl_Obj **objArrayPtr, *objPtr;
+ register const AuxData *auxDataPtr;
int i;
#ifdef TCL_COMPILE_STATS
@@ -516,29 +1026,29 @@ TclCleanupByteCode(codePtr)
Tcl_Time destroyTime;
int lifetimeSec, lifetimeMicroSec, log2;
- statsPtr = &((Interp *) interp)->stats;
+ statsPtr = &iPtr->stats;
statsPtr->numByteCodesFreed++;
statsPtr->currentSrcBytes -= (double) codePtr->numSrcBytes;
statsPtr->currentByteCodeBytes -= (double) codePtr->structureSize;
- statsPtr->currentInstBytes -= (double) codePtr->numCodeBytes;
- statsPtr->currentLitBytes -=
- (double) (codePtr->numLitObjects * sizeof(Tcl_Obj *));
- statsPtr->currentExceptBytes -=
- (double) (codePtr->numExceptRanges * sizeof(ExceptionRange));
- statsPtr->currentAuxBytes -=
- (double) (codePtr->numAuxDataItems * sizeof(AuxData));
+ statsPtr->currentInstBytes -= (double) codePtr->numCodeBytes;
+ statsPtr->currentLitBytes -= (double)
+ codePtr->numLitObjects * sizeof(Tcl_Obj *);
+ statsPtr->currentExceptBytes -= (double)
+ codePtr->numExceptRanges * sizeof(ExceptionRange);
+ statsPtr->currentAuxBytes -= (double)
+ codePtr->numAuxDataItems * sizeof(AuxData);
statsPtr->currentCmdMapBytes -= (double) codePtr->numCmdLocBytes;
- TclpGetTime(&destroyTime);
+ Tcl_GetTime(&destroyTime);
lifetimeSec = destroyTime.sec - codePtr->createTime.sec;
if (lifetimeSec > 2000) { /* avoid overflow */
lifetimeSec = 2000;
}
- lifetimeMicroSec =
- 1000000*lifetimeSec + (destroyTime.usec - codePtr->createTime.usec);
-
+ lifetimeMicroSec = 1000000 * lifetimeSec +
+ (destroyTime.usec - codePtr->createTime.usec);
+
log2 = TclLog2(lifetimeMicroSec);
if (log2 > 31) {
log2 = 31;
@@ -548,44 +1058,342 @@ TclCleanupByteCode(codePtr)
#endif /* TCL_COMPILE_STATS */
/*
- * A single heap object holds the ByteCode structure and its code,
- * object, command location, and auxiliary data arrays. This means we
- * only need to 1) decrement the ref counts of the LiteralEntry's in
- * its literal array, 2) call the free procs for the auxiliary data
- * items, and 3) free the ByteCode structure's heap object.
+ * A single heap object holds the ByteCode structure and its code, object,
+ * command location, and auxiliary data arrays. This means we only need to
+ * 1) decrement the ref counts of the LiteralEntry's in its literal array,
+ * 2) call the free procs for the auxiliary data items, 3) free the
+ * localCache if it is unused, and finally 4) free the ByteCode
+ * structure's heap object.
+ *
+ * The case for TCL_BYTECODE_PRECOMPILED (precompiled ByteCodes, like
+ * those generated from tbcload) is special, as they doesn't make use of
+ * the global literal table. They instead maintain private references to
+ * their literals which must be decremented.
+ *
+ * In order to insure a proper and efficient cleanup of the literal array
+ * when it contains non-shared literals [Bug 983660], we also distinguish
+ * the case of an interpreter being deleted (signaled by interp == NULL).
+ * Also, as the interp deletion will remove the global literal table
+ * anyway, we avoid the extra cost of updating it for each literal being
+ * released.
*/
- if (interp != NULL) {
- /*
- * If the interp has already been freed, then Tcl will have already
- * forcefully released all the literals used by ByteCodes compiled
- * with respect to that interp.
- */
-
+ if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
+
objArrayPtr = codePtr->objArrayPtr;
for (i = 0; i < numLitObjects; i++) {
- /*
- * TclReleaseLiteral sets a ByteCode's object array entry NULL to
- * indicate that it has already freed the literal.
- */
-
- if (*objArrayPtr != NULL) {
- TclReleaseLiteral(interp, *objArrayPtr);
+ objPtr = *objArrayPtr;
+ if (objPtr) {
+ Tcl_DecrRefCount(objPtr);
}
objArrayPtr++;
}
+ codePtr->numLitObjects = 0;
+ } else {
+ objArrayPtr = codePtr->objArrayPtr;
+ while (numLitObjects--) {
+ /* TclReleaseLiteral calls Tcl_DecrRefCount() for us */
+ TclReleaseLiteral(interp, *objArrayPtr++);
+ }
}
-
+
auxDataPtr = codePtr->auxDataArrayPtr;
for (i = 0; i < numAuxDataItems; i++) {
if (auxDataPtr->type->freeProc != NULL) {
- (*auxDataPtr->type->freeProc)(auxDataPtr->clientData);
+ auxDataPtr->type->freeProc(auxDataPtr->clientData);
}
auxDataPtr++;
}
+ /*
+ * TIP #280. Release the location data associated with this byte code
+ * structure, if any. NOTE: The interp we belong to may be gone already,
+ * and the data with it.
+ *
+ * See also tclBasic.c, DeleteInterpProc
+ */
+
+ if (iPtr) {
+ Tcl_HashEntry *hePtr = Tcl_FindHashEntry(iPtr->lineBCPtr,
+ (char *) codePtr);
+
+ if (hePtr) {
+ ReleaseCmdWordData(Tcl_GetHashValue(hePtr));
+ Tcl_DeleteHashEntry(hePtr);
+ }
+ }
+
+ if (codePtr->localCachePtr && (--codePtr->localCachePtr->refCount == 0)) {
+ TclFreeLocalCache(interp, codePtr->localCachePtr);
+ }
+
TclHandleRelease(codePtr->interpHandle);
- ckfree((char *) codePtr);
+ ckfree(codePtr);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * IsCompactibleCompileEnv --
+ *
+ * Checks to see if we may apply some basic compaction optimizations to a
+ * piece of bytecode. Idempotent.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+static int
+IsCompactibleCompileEnv(
+ Tcl_Interp *interp,
+ CompileEnv *envPtr)
+{
+ unsigned char *pc;
+ int size;
+
+ /*
+ * Special: procedures in the '::tcl' namespace (or its children) are
+ * considered to be well-behaved and so can have compaction applied even
+ * if it would otherwise be invalid.
+ */
+
+ if (envPtr->procPtr != NULL && envPtr->procPtr->cmdPtr != NULL
+ && envPtr->procPtr->cmdPtr->nsPtr != NULL) {
+ Namespace *nsPtr = envPtr->procPtr->cmdPtr->nsPtr;
+
+ if (strcmp(nsPtr->fullName, "::tcl") == 0
+ || strncmp(nsPtr->fullName, "::tcl::", 7) == 0) {
+ return 1;
+ }
+ }
+
+ /*
+ * Go through and ensure that no operation involved can cause a desired
+ * change of bytecode sequence during running. This comes down to ensuring
+ * that there are no mapped variables (due to traces) or calls to external
+ * commands (traces, [uplevel] trickery). This is actually a very
+ * conservative check; it turns down a lot of code that is OK in practice.
+ */
+
+ for (pc = envPtr->codeStart ; pc < envPtr->codeNext ; pc += size) {
+ switch (*pc) {
+ /* Invokes */
+ case INST_INVOKE_STK1:
+ case INST_INVOKE_STK4:
+ case INST_INVOKE_EXPANDED:
+ case INST_INVOKE_REPLACE:
+ return 0;
+ /* Runtime evals */
+ case INST_EVAL_STK:
+ case INST_EXPR_STK:
+ case INST_YIELD:
+ return 0;
+ /* Upvars */
+ case INST_UPVAR:
+ case INST_NSUPVAR:
+ case INST_VARIABLE:
+ return 0;
+ default:
+ size = tclInstructionTable[*pc].numBytes;
+ assert (size > 0);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SubstObj --
+ *
+ * This function performs the substitutions specified on the given string
+ * as described in the user documentation for the "subst" Tcl command.
+ *
+ * Results:
+ * A Tcl_Obj* containing the substituted string, or NULL to indicate that
+ * an error occurred.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_SubstObj(
+ Tcl_Interp *interp, /* Interpreter in which substitution occurs */
+ Tcl_Obj *objPtr, /* The value to be substituted. */
+ int flags) /* What substitutions to do. */
+{
+ NRE_callback *rootPtr = TOP_CB(interp);
+
+ if (TclNRRunCallbacks(interp, Tcl_NRSubstObj(interp, objPtr, flags),
+ rootPtr) != TCL_OK) {
+ return NULL;
+ }
+ return Tcl_GetObjResult(interp);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_NRSubstObj --
+ *
+ * Request substitution of a Tcl value by the NR stack.
+ *
+ * Results:
+ * Returns TCL_OK.
+ *
+ * Side effects:
+ * Compiles objPtr into bytecode that performs the substitutions as
+ * governed by flags and places callbacks on the NR stack to execute
+ * the bytecode and store the result in the interp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_NRSubstObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ int flags)
+{
+ ByteCode *codePtr = CompileSubstObj(interp, objPtr, flags);
+
+ /* TODO: Confirm we do not need this. */
+ /* Tcl_ResetResult(interp); */
+ return TclNRExecuteByteCode(interp, codePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompileSubstObj --
+ *
+ * Compile a Tcl value into ByteCode implementing its substitution, as
+ * governed by flags.
+ *
+ * Results:
+ * A (ByteCode *) is returned pointing to the resulting ByteCode.
+ * The caller must manage its refCount and arrange for a call to
+ * TclCleanupByteCode() when the last reference disappears.
+ *
+ * Side effects:
+ * The Tcl_ObjType of objPtr is changed to the "substcode" type, and the
+ * ByteCode and governing flags value are kept in the internal rep for
+ * faster operations the next time CompileSubstObj is called on the same
+ * value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ByteCode *
+CompileSubstObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ int flags)
+{
+ Interp *iPtr = (Interp *) interp;
+ ByteCode *codePtr = NULL;
+
+ if (objPtr->typePtr == &substCodeType) {
+ Namespace *nsPtr = iPtr->varFramePtr->nsPtr;
+
+ codePtr = objPtr->internalRep.ptrAndLongRep.ptr;
+ if ((unsigned long)flags != objPtr->internalRep.ptrAndLongRep.value
+ || ((Interp *) *codePtr->interpHandle != iPtr)
+ || (codePtr->compileEpoch != iPtr->compileEpoch)
+ || (codePtr->nsPtr != nsPtr)
+ || (codePtr->nsEpoch != nsPtr->resolverEpoch)
+ || (codePtr->localCachePtr !=
+ iPtr->varFramePtr->localCachePtr)) {
+ FreeSubstCodeInternalRep(objPtr);
+ }
+ }
+ if (objPtr->typePtr != &substCodeType) {
+ CompileEnv compEnv;
+ int numBytes;
+ const char *bytes = Tcl_GetStringFromObj(objPtr, &numBytes);
+
+ /* TODO: Check for more TIP 280 */
+ TclInitCompileEnv(interp, &compEnv, bytes, numBytes, NULL, 0);
+
+ TclSubstCompile(interp, bytes, numBytes, flags, 1, &compEnv);
+
+ TclEmitOpcode(INST_DONE, &compEnv);
+ TclInitByteCodeObj(objPtr, &compEnv);
+ objPtr->typePtr = &substCodeType;
+ TclFreeCompileEnv(&compEnv);
+
+ codePtr = objPtr->internalRep.twoPtrValue.ptr1;
+ objPtr->internalRep.ptrAndLongRep.ptr = codePtr;
+ objPtr->internalRep.ptrAndLongRep.value = flags;
+ if (iPtr->varFramePtr->localCachePtr) {
+ codePtr->localCachePtr = iPtr->varFramePtr->localCachePtr;
+ codePtr->localCachePtr->refCount++;
+ }
+#ifdef TCL_COMPILE_DEBUG
+ if (tclTraceCompile >= 2) {
+ TclPrintByteCodeObj(interp, objPtr);
+ fflush(stdout);
+ }
+#endif /* TCL_COMPILE_DEBUG */
+ }
+ return codePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeSubstCodeInternalRep --
+ *
+ * Part of the substcode Tcl object type implementation. Frees the
+ * storage associated with a substcode object's internal representation
+ * unless its code is actively being executed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The substcode object's internal rep is marked invalid and its code
+ * gets freed unless the code is actively being executed. In that case
+ * the cleanup is delayed until the last execution of the code completes.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeSubstCodeInternalRep(
+ register Tcl_Obj *objPtr) /* Object whose internal rep to free. */
+{
+ register ByteCode *codePtr = objPtr->internalRep.ptrAndLongRep.ptr;
+
+ objPtr->typePtr = NULL;
+ codePtr->refCount--;
+ if (codePtr->refCount <= 0) {
+ TclCleanupByteCode(codePtr);
+ }
+}
+
+static void
+ReleaseCmdWordData(
+ ExtCmdLoc *eclPtr)
+{
+ int i;
+
+ if (eclPtr->type == TCL_LOCATION_SOURCE) {
+ Tcl_DecrRefCount(eclPtr->path);
+ }
+ for (i=0 ; i<eclPtr->nuloc ; i++) {
+ ckfree((char *) eclPtr->loc[i].line);
+ }
+
+ if (eclPtr->loc != NULL) {
+ ckfree((char *) eclPtr->loc);
+ }
+
+ ckfree((char *) eclPtr);
}
/*
@@ -606,47 +1414,186 @@ TclCleanupByteCode(codePtr)
*/
void
-TclInitCompileEnv(interp, envPtr, string, numBytes)
- Tcl_Interp *interp; /* The interpreter for which a CompileEnv
- * structure is initialized. */
- register CompileEnv *envPtr; /* Points to the CompileEnv structure to
- * initialize. */
- char *string; /* The source string to be compiled. */
- int numBytes; /* Number of bytes in source string. */
+TclInitCompileEnv(
+ Tcl_Interp *interp, /* The interpreter for which a CompileEnv
+ * structure is initialized. */
+ register CompileEnv *envPtr,/* Points to the CompileEnv structure to
+ * initialize. */
+ const char *stringPtr, /* The source string to be compiled. */
+ int numBytes, /* Number of bytes in source string. */
+ const CmdFrame *invoker, /* Location context invoking the bcc */
+ int word) /* Index of the word in that context getting
+ * compiled */
{
Interp *iPtr = (Interp *) interp;
-
+
+ assert(tclInstructionTable[LAST_INST_OPCODE+1].name == NULL);
+
envPtr->iPtr = iPtr;
- envPtr->source = string;
+ envPtr->source = stringPtr;
envPtr->numSrcBytes = numBytes;
envPtr->procPtr = iPtr->compiledProcPtr;
+ iPtr->compiledProcPtr = NULL;
envPtr->numCommands = 0;
envPtr->exceptDepth = 0;
envPtr->maxExceptDepth = 0;
envPtr->maxStackDepth = 0;
- TclInitLiteralTable(&(envPtr->localLitTable));
- envPtr->exprIsJustVarRef = 0;
- envPtr->exprIsComparison = 0;
+ envPtr->currStackDepth = 0;
+ TclInitLiteralTable(&envPtr->localLitTable);
envPtr->codeStart = envPtr->staticCodeSpace;
envPtr->codeNext = envPtr->codeStart;
- envPtr->codeEnd = (envPtr->codeStart + COMPILEENV_INIT_CODE_BYTES);
+ envPtr->codeEnd = envPtr->codeStart + COMPILEENV_INIT_CODE_BYTES;
envPtr->mallocedCodeArray = 0;
envPtr->literalArrayPtr = envPtr->staticLiteralSpace;
envPtr->literalArrayNext = 0;
envPtr->literalArrayEnd = COMPILEENV_INIT_NUM_OBJECTS;
envPtr->mallocedLiteralArray = 0;
-
+
envPtr->exceptArrayPtr = envPtr->staticExceptArraySpace;
+ envPtr->exceptAuxArrayPtr = envPtr->staticExAuxArraySpace;
envPtr->exceptArrayNext = 0;
envPtr->exceptArrayEnd = COMPILEENV_INIT_EXCEPT_RANGES;
envPtr->mallocedExceptArray = 0;
-
+
envPtr->cmdMapPtr = envPtr->staticCmdMapSpace;
envPtr->cmdMapEnd = COMPILEENV_INIT_CMD_MAP_SIZE;
envPtr->mallocedCmdMap = 0;
-
+ envPtr->atCmdStart = 1;
+ envPtr->expandCount = 0;
+
+ /*
+ * TIP #280: Set up the extended command location information, based on
+ * the context invoking the byte code compiler. This structure is used to
+ * keep the per-word line information for all compiled commands.
+ *
+ * See also tclBasic.c, TclEvalObjEx, for the equivalent code in the
+ * non-compiling evaluator
+ */
+
+ envPtr->extCmdMapPtr = ckalloc(sizeof(ExtCmdLoc));
+ envPtr->extCmdMapPtr->loc = NULL;
+ envPtr->extCmdMapPtr->nloc = 0;
+ envPtr->extCmdMapPtr->nuloc = 0;
+ envPtr->extCmdMapPtr->path = NULL;
+
+ if (invoker == NULL) {
+ /*
+ * Initialize the compiler for relative counting in case of a
+ * dynamic context.
+ */
+
+ envPtr->line = 1;
+ if (iPtr->evalFlags & TCL_EVAL_FILE) {
+ iPtr->evalFlags &= ~TCL_EVAL_FILE;
+ envPtr->extCmdMapPtr->type = TCL_LOCATION_SOURCE;
+
+ if (iPtr->scriptFile) {
+ /*
+ * Normalization here, to have the correct pwd. Should have
+ * negligible impact on performance, as the norm should have
+ * been done already by the 'source' invoking us, and it
+ * caches the result.
+ */
+
+ Tcl_Obj *norm =
+ Tcl_FSGetNormalizedPath(interp, iPtr->scriptFile);
+
+ if (norm == NULL) {
+ /*
+ * Error message in the interp result. No place to put it.
+ * And no place to serve the error itself to either. Fake
+ * a path, empty string.
+ */
+
+ TclNewLiteralStringObj(envPtr->extCmdMapPtr->path, "");
+ } else {
+ envPtr->extCmdMapPtr->path = norm;
+ }
+ } else {
+ TclNewLiteralStringObj(envPtr->extCmdMapPtr->path, "");
+ }
+
+ Tcl_IncrRefCount(envPtr->extCmdMapPtr->path);
+ } else {
+ envPtr->extCmdMapPtr->type =
+ (envPtr->procPtr ? TCL_LOCATION_PROC : TCL_LOCATION_BC);
+ }
+ } else {
+ /*
+ * Initialize the compiler using the context, making counting absolute
+ * to that context. Note that the context can be byte code execution.
+ * In that case we have to fill out the missing pieces (line, path,
+ * ...) which may make change the type as well.
+ */
+
+ CmdFrame *ctxPtr = TclStackAlloc(interp, sizeof(CmdFrame));
+ int pc = 0;
+
+ *ctxPtr = *invoker;
+ if (invoker->type == TCL_LOCATION_BC) {
+ /*
+ * Note: Type BC => ctx.data.eval.path is not used.
+ * ctx.data.tebc.codePtr is used instead.
+ */
+
+ TclGetSrcInfoForPc(ctxPtr);
+ pc = 1;
+ }
+
+ if ((ctxPtr->nline <= word) || (ctxPtr->line[word] < 0)) {
+ /*
+ * Word is not a literal, relative counting.
+ */
+
+ envPtr->line = 1;
+ envPtr->extCmdMapPtr->type =
+ (envPtr->procPtr ? TCL_LOCATION_PROC : TCL_LOCATION_BC);
+
+ if (pc && (ctxPtr->type == TCL_LOCATION_SOURCE)) {
+ /*
+ * The reference made by 'TclGetSrcInfoForPc' is dead.
+ */
+
+ Tcl_DecrRefCount(ctxPtr->data.eval.path);
+ }
+ } else {
+ envPtr->line = ctxPtr->line[word];
+ envPtr->extCmdMapPtr->type = ctxPtr->type;
+
+ if (ctxPtr->type == TCL_LOCATION_SOURCE) {
+ envPtr->extCmdMapPtr->path = ctxPtr->data.eval.path;
+
+ if (pc) {
+ /*
+ * The reference 'TclGetSrcInfoForPc' made is transfered.
+ */
+
+ ctxPtr->data.eval.path = NULL;
+ } else {
+ /*
+ * We have a new reference here.
+ */
+
+ Tcl_IncrRefCount(envPtr->extCmdMapPtr->path);
+ }
+ }
+ }
+
+ TclStackFree(interp, ctxPtr);
+ }
+
+ envPtr->extCmdMapPtr->start = envPtr->line;
+
+ /*
+ * Initialize the data about invisible continuation lines as empty, i.e.
+ * not used. The caller (TclSetByteCodeFromAny) will set this up, if such
+ * data is available.
+ */
+
+ envPtr->clNext = NULL;
+
envPtr->auxDataArrayPtr = envPtr->staticAuxDataArraySpace;
envPtr->auxDataArrayNext = 0;
envPtr->auxDataArrayEnd = COMPILEENV_INIT_AUX_DATA_SIZE;
@@ -663,42 +1610,157 @@ TclInitCompileEnv(interp, envPtr, string, numBytes)
*
* Results:
* None.
- *
+ *
* Side effects:
- * Allocated storage in the CompileEnv structure is freed. Note that
- * its local literal table is not deleted and its literal objects are
- * not released. In addition, storage referenced by its auxiliary data
- * items is not freed. This is done so that, when compilation is
- * successful, "ownership" of these objects and aux data items is
- * handed over to the corresponding ByteCode structure.
+ * Allocated storage in the CompileEnv structure is freed. Note that its
+ * local literal table is not deleted and its literal objects are not
+ * released. In addition, storage referenced by its auxiliary data items
+ * is not freed. This is done so that, when compilation is successful,
+ * "ownership" of these objects and aux data items is handed over to the
+ * corresponding ByteCode structure.
*
*----------------------------------------------------------------------
*/
void
-TclFreeCompileEnv(envPtr)
- register CompileEnv *envPtr; /* Points to the CompileEnv structure. */
+TclFreeCompileEnv(
+ register CompileEnv *envPtr)/* Points to the CompileEnv structure. */
{
+ if (envPtr->localLitTable.buckets != envPtr->localLitTable.staticBuckets){
+ ckfree(envPtr->localLitTable.buckets);
+ envPtr->localLitTable.buckets = envPtr->localLitTable.staticBuckets;
+ }
+ if (envPtr->iPtr) {
+ /*
+ * We never converted to Bytecode, so free the things we would
+ * have transferred to it.
+ */
+
+ int i;
+ LiteralEntry *entryPtr = envPtr->literalArrayPtr;
+ AuxData *auxDataPtr = envPtr->auxDataArrayPtr;
+
+ for (i = 0; i < envPtr->literalArrayNext; i++) {
+ TclReleaseLiteral((Tcl_Interp *)envPtr->iPtr, entryPtr->objPtr);
+ entryPtr++;
+ }
+
+#ifdef TCL_COMPILE_DEBUG
+ TclVerifyGlobalLiteralTable(envPtr->iPtr);
+#endif /*TCL_COMPILE_DEBUG*/
+
+ for (i = 0; i < envPtr->auxDataArrayNext; i++) {
+ if (auxDataPtr->type->freeProc != NULL) {
+ auxDataPtr->type->freeProc(auxDataPtr->clientData);
+ }
+ auxDataPtr++;
+ }
+ }
if (envPtr->mallocedCodeArray) {
- ckfree((char *) envPtr->codeStart);
+ ckfree(envPtr->codeStart);
}
if (envPtr->mallocedLiteralArray) {
- ckfree((char *) envPtr->literalArrayPtr);
+ ckfree(envPtr->literalArrayPtr);
}
if (envPtr->mallocedExceptArray) {
- ckfree((char *) envPtr->exceptArrayPtr);
+ ckfree(envPtr->exceptArrayPtr);
+ ckfree(envPtr->exceptAuxArrayPtr);
}
if (envPtr->mallocedCmdMap) {
- ckfree((char *) envPtr->cmdMapPtr);
+ ckfree(envPtr->cmdMapPtr);
}
if (envPtr->mallocedAuxDataArray) {
- ckfree((char *) envPtr->auxDataArrayPtr);
+ ckfree(envPtr->auxDataArrayPtr);
+ }
+ if (envPtr->extCmdMapPtr) {
+ ReleaseCmdWordData(envPtr->extCmdMapPtr);
+ envPtr->extCmdMapPtr = NULL;
}
}
/*
*----------------------------------------------------------------------
*
+ * TclWordKnownAtCompileTime --
+ *
+ * Test whether the value of a token is completely known at compile time.
+ *
+ * Results:
+ * Returns true if the tokenPtr argument points to a word value that is
+ * completely known at compile time. Generally, values that are known at
+ * compile time can be compiled to their values, while values that cannot
+ * be known until substitution at runtime must be compiled to bytecode
+ * instructions that perform that substitution. For several commands,
+ * whether or not arguments are known at compile time determine whether
+ * it is worthwhile to compile at all.
+ *
+ * Side effects:
+ * When returning true, appends the known value of the word to the
+ * unshared Tcl_Obj (*valuePtr), unless valuePtr is NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclWordKnownAtCompileTime(
+ Tcl_Token *tokenPtr, /* Points to Tcl_Token we should check */
+ Tcl_Obj *valuePtr) /* If not NULL, points to an unshared Tcl_Obj
+ * to which we should append the known value
+ * of the word. */
+{
+ int numComponents = tokenPtr->numComponents;
+ Tcl_Obj *tempPtr = NULL;
+
+ if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ if (valuePtr != NULL) {
+ Tcl_AppendToObj(valuePtr, tokenPtr[1].start, tokenPtr[1].size);
+ }
+ return 1;
+ }
+ if (tokenPtr->type != TCL_TOKEN_WORD) {
+ return 0;
+ }
+ tokenPtr++;
+ if (valuePtr != NULL) {
+ tempPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(tempPtr);
+ }
+ while (numComponents--) {
+ switch (tokenPtr->type) {
+ case TCL_TOKEN_TEXT:
+ if (tempPtr != NULL) {
+ Tcl_AppendToObj(tempPtr, tokenPtr->start, tokenPtr->size);
+ }
+ break;
+
+ case TCL_TOKEN_BS:
+ if (tempPtr != NULL) {
+ char utfBuf[TCL_UTF_MAX];
+ int length = TclParseBackslash(tokenPtr->start,
+ tokenPtr->size, NULL, utfBuf);
+
+ Tcl_AppendToObj(tempPtr, utfBuf, length);
+ }
+ break;
+
+ default:
+ if (tempPtr != NULL) {
+ Tcl_DecrRefCount(tempPtr);
+ }
+ return 0;
+ }
+ tokenPtr++;
+ }
+ if (valuePtr != NULL) {
+ Tcl_AppendObjToObj(valuePtr, tempPtr);
+ Tcl_DecrRefCount(tempPtr);
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclCompileScript --
*
* Compile a Tcl script in a string.
@@ -708,316 +1770,473 @@ TclFreeCompileEnv(envPtr)
* on failure. If TCL_ERROR is returned, then the interpreter's result
* contains an error message.
*
- * interp->termOffset is set to the offset of the character in the
- * script just after the last one successfully processed; this will be
- * the offset of the ']' if (flags & TCL_BRACKET_TERM).
- * envPtr->maxStackDepth is set to the maximum number of stack elements
- * needed to execute the script's commands.
- *
* Side effects:
* Adds instructions to envPtr to evaluate the script at runtime.
*
*----------------------------------------------------------------------
*/
-int
-TclCompileScript(interp, script, numBytes, nested, envPtr)
- Tcl_Interp *interp; /* Used for error and status reporting. */
- char *script; /* The source script to compile. */
- int numBytes; /* Number of bytes in script. If < 0, the
- * script consists of all bytes up to the
- * first null character. */
- int nested; /* Non-zero means this is a nested command:
- * close bracket ']' should be considered a
- * command terminator. If zero, close
- * bracket has no special meaning. */
- CompileEnv *envPtr; /* Holds resulting instructions. */
+static int
+ExpandRequested(
+ Tcl_Token *tokenPtr,
+ int numWords)
{
- Interp *iPtr = (Interp *) interp;
- Tcl_Parse parse;
- int maxDepth = 0; /* Maximum number of stack elements needed
- * to execute all cmds. */
- int lastTopLevelCmdIndex = -1;
- /* Index of most recent toplevel command in
- * the command location table. Initialized
- * to avoid compiler warning. */
- int startCodeOffset = -1; /* Offset of first byte of current command's
- * code. Init. to avoid compiler warning. */
- unsigned char *entryCodeNext = envPtr->codeNext;
- char *p, *next;
- Namespace *cmdNsPtr;
- Command *cmdPtr;
- Tcl_Token *tokenPtr;
- int bytesLeft, isFirstCmd, gotParse, wordIdx, currCmdIndex;
- int commandLength, objIndex, code;
- char prev;
- Tcl_DString ds;
+ /* Determine whether any words of the command require expansion */
+ while (numWords--) {
+ if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
+ return 1;
+ }
+ tokenPtr = TokenAfter(tokenPtr);
+ }
+ return 0;
+}
+
+static void
+CompileCmdLiteral(
+ Tcl_Interp *interp,
+ Tcl_Obj *cmdObj,
+ CompileEnv *envPtr)
+{
+ int numBytes;
+ const char *bytes = Tcl_GetStringFromObj(cmdObj, &numBytes);
+ int cmdLitIdx = TclRegisterNewCmdLiteral(envPtr, bytes, numBytes);
+ Command *cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, cmdObj);
+
+ if (cmdPtr) {
+ TclSetCmdNameObj(interp, TclFetchLiteral(envPtr, cmdLitIdx), cmdPtr);
+ }
+ TclEmitPush(cmdLitIdx, envPtr);
+}
+
+void
+TclCompileInvocation(
+ Tcl_Interp *interp,
+ Tcl_Token *tokenPtr,
+ Tcl_Obj *cmdObj,
+ int numWords,
+ CompileEnv *envPtr)
+{
+ int wordIdx = 0, depth = TclGetStackDepth(envPtr);
+ DefineLineInformation;
+
+ if (cmdObj) {
+ CompileCmdLiteral(interp, cmdObj, envPtr);
+ wordIdx = 1;
+ tokenPtr = TokenAfter(tokenPtr);
+ }
+
+ for (; wordIdx < numWords; wordIdx++, tokenPtr = TokenAfter(tokenPtr)) {
+ int objIdx;
+
+ SetLineInformation(wordIdx);
+
+ if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ CompileTokens(envPtr, tokenPtr, interp);
+ continue;
+ }
+
+ objIdx = TclRegisterNewLiteral(envPtr,
+ tokenPtr[1].start, tokenPtr[1].size);
+ if (envPtr->clNext) {
+ TclContinuationsEnterDerived(TclFetchLiteral(envPtr, objIdx),
+ tokenPtr[1].start - envPtr->source, envPtr->clNext);
+ }
+ TclEmitPush(objIdx, envPtr);
+ }
- Tcl_DStringInit(&ds);
+ if (wordIdx <= 255) {
+ TclEmitInvoke(envPtr, INST_INVOKE_STK1, wordIdx);
+ } else {
+ TclEmitInvoke(envPtr, INST_INVOKE_STK4, wordIdx);
+ }
+ TclCheckStackDepth(depth+1, envPtr);
+}
- if (numBytes < 0) {
- numBytes = strlen(script);
+static void
+CompileExpanded(
+ Tcl_Interp *interp,
+ Tcl_Token *tokenPtr,
+ Tcl_Obj *cmdObj,
+ int numWords,
+ CompileEnv *envPtr)
+{
+ int wordIdx = 0;
+ DefineLineInformation;
+ int depth = TclGetStackDepth(envPtr);
+
+ StartExpanding(envPtr);
+ if (cmdObj) {
+ CompileCmdLiteral(interp, cmdObj, envPtr);
+ wordIdx = 1;
+ tokenPtr = TokenAfter(tokenPtr);
+ }
+
+ for (; wordIdx < numWords; wordIdx++, tokenPtr = TokenAfter(tokenPtr)) {
+ int objIdx;
+
+ SetLineInformation(wordIdx);
+
+ if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ CompileTokens(envPtr, tokenPtr, interp);
+ if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
+ TclEmitInstInt4(INST_EXPAND_STKTOP,
+ envPtr->currStackDepth, envPtr);
+ }
+ continue;
+ }
+
+ objIdx = TclRegisterNewLiteral(envPtr,
+ tokenPtr[1].start, tokenPtr[1].size);
+ if (envPtr->clNext) {
+ TclContinuationsEnterDerived(TclFetchLiteral(envPtr, objIdx),
+ tokenPtr[1].start - envPtr->source, envPtr->clNext);
+ }
+ TclEmitPush(objIdx, envPtr);
}
- Tcl_ResetResult(interp);
- isFirstCmd = 1;
/*
- * Each iteration through the following loop compiles the next
- * command from the script.
+ * The stack depth during argument expansion can only be managed at
+ * runtime, as the number of elements in the expanded lists is not known
+ * at compile time. We adjust here the stack depth estimate so that it is
+ * correct after the command with expanded arguments returns.
+ *
+ * The end effect of this command's invocation is that all the words of
+ * the command are popped from the stack, and the result is pushed: the
+ * stack top changes by (1-wordIdx).
+ *
+ * Note that the estimates are not correct while the command is being
+ * prepared and run, INST_EXPAND_STKTOP is not stack-neutral in general.
*/
- p = script;
- bytesLeft = numBytes;
- gotParse = 0;
- while (bytesLeft > 0) {
- if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse) != TCL_OK) {
- code = TCL_ERROR;
- goto error;
+ TclEmitInvoke(envPtr, INST_INVOKE_EXPANDED, wordIdx);
+ TclCheckStackDepth(depth+1, envPtr);
+}
+
+static int
+CompileCmdCompileProc(
+ Tcl_Interp *interp,
+ Tcl_Parse *parsePtr,
+ Command *cmdPtr,
+ CompileEnv *envPtr)
+{
+ int unwind = 0, incrOffset = -1;
+ DefineLineInformation;
+ int depth = TclGetStackDepth(envPtr);
+
+ /*
+ * Emit of the INST_START_CMD instruction is controlled by the value of
+ * envPtr->atCmdStart:
+ *
+ * atCmdStart == 2 : We are not using the INST_START_CMD instruction.
+ * atCmdStart == 1 : INST_START_CMD was the last instruction emitted.
+ * : We do not need to emit another. Instead we
+ * : increment the number of cmds started at it (except
+ * : for the special case at the start of a script.)
+ * atCmdStart == 0 : The last instruction was something else. We need
+ * : to emit INST_START_CMD here.
+ */
+
+ switch (envPtr->atCmdStart) {
+ case 0:
+ unwind = tclInstructionTable[INST_START_CMD].numBytes;
+ TclEmitInstInt4(INST_START_CMD, 0, envPtr);
+ incrOffset = envPtr->codeNext - envPtr->codeStart;
+ TclEmitInt4(0, envPtr);
+ break;
+ case 1:
+ if (envPtr->codeNext > envPtr->codeStart) {
+ incrOffset = envPtr->codeNext - 4 - envPtr->codeStart;
}
- gotParse = 1;
- if (parse.numWords > 0) {
+ break;
+ case 2:
+ /* Nothing to do */
+ ;
+ }
+
+ if (TCL_OK == TclAttemptCompileProc(interp, parsePtr, 1, cmdPtr, envPtr)) {
+ if (incrOffset >= 0) {
/*
- * If not the first command, pop the previous command's result
- * and, if we're compiling a top level command, update the last
- * command's code size to account for the pop instruction.
+ * We successfully compiled a command. Increment the number of
+ * commands that start at the currently active INST_START_CMD.
*/
- if (!isFirstCmd) {
- TclEmitOpcode(INST_POP, envPtr);
- if (!nested) {
- envPtr->cmdMapPtr[lastTopLevelCmdIndex].numCodeBytes =
- (envPtr->codeNext - envPtr->codeStart)
- - startCodeOffset;
- }
+ unsigned char *incrPtr = envPtr->codeStart + incrOffset;
+ unsigned char *startPtr = incrPtr - 5;
+
+ TclIncrUInt4AtPtr(incrPtr, 1);
+ if (unwind) {
+ /* We started the INST_START_CMD. Record the code length. */
+ TclStoreInt4AtPtr(envPtr->codeNext - startPtr, startPtr + 1);
}
+ }
+ TclCheckStackDepth(depth+1, envPtr);
+ return TCL_OK;
+ }
- /*
- * Determine the actual length of the command.
- */
+ envPtr->codeNext -= unwind; /* Unwind INST_START_CMD */
- commandLength = parse.commandSize;
- prev = '\0';
- if (commandLength > 0) {
- prev = parse.commandStart[commandLength-1];
- }
- if (((parse.commandStart+commandLength) != (script+numBytes))
- || ((prev=='\n') || (nested && (prev==']')))) {
- /*
- * The command didn't end at the end of the script (i.e. it
- * ended at a terminator character such as ";". Reduce the
- * length by one so that the trace message doesn't include
- * the terminator character.
- */
-
- commandLength -= 1;
- }
+ /*
+ * Throw out any line information generated by the failed compile attempt.
+ */
- /*
- * If tracing, print a line for each top level command compiled.
- */
-
- if ((tclTraceCompile >= 1)
- && !nested && (envPtr->procPtr == NULL)) {
- fprintf(stdout, " Compiling: ");
- TclPrintSource(stdout, parse.commandStart,
- TclMin(commandLength, 55));
- fprintf(stdout, "\n");
- }
+ while (mapPtr->nuloc - 1 > eclIndex) {
+ mapPtr->nuloc--;
+ ckfree(mapPtr->loc[mapPtr->nuloc].line);
+ mapPtr->loc[mapPtr->nuloc].line = NULL;
+ }
- /*
- * Each iteration of the following loop compiles one word
- * from the command.
- */
-
- envPtr->numCommands++;
- currCmdIndex = (envPtr->numCommands - 1);
- if (!nested) {
- lastTopLevelCmdIndex = currCmdIndex;
- }
- startCodeOffset = (envPtr->codeNext - envPtr->codeStart);
- EnterCmdStartData(envPtr, currCmdIndex,
- (parse.commandStart - envPtr->source), startCodeOffset);
-
- for (wordIdx = 0, tokenPtr = parse.tokenPtr;
- wordIdx < parse.numWords;
- wordIdx++, tokenPtr += (tokenPtr->numComponents + 1)) {
- if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
- /*
- * If this is the first word and the command has a
- * compile procedure, let it compile the command.
- */
+ /*
+ * Reset the index of next command. Toss out any from failed nested
+ * partial compiles.
+ */
- if (wordIdx == 0) {
- if (envPtr->procPtr != NULL) {
- cmdNsPtr = envPtr->procPtr->cmdPtr->nsPtr;
- } else {
- cmdNsPtr = NULL; /* use current NS */
- }
-
- /*
- * We copy the string before trying to find the command
- * by name. We used to modify the string in place, but
- * this is not safe because the name resolution
- * handlers could have side effects that rely on the
- * unmodified string.
- */
-
- Tcl_DStringSetLength(&ds, 0);
- Tcl_DStringAppend(&ds, tokenPtr[1].start,
- tokenPtr[1].size);
-
- cmdPtr = (Command *) Tcl_FindCommand(interp,
- Tcl_DStringValue(&ds),
- (Tcl_Namespace *) cmdNsPtr, /*flags*/ 0);
-
- if ((cmdPtr != NULL)
- && (cmdPtr->compileProc != NULL)
- && !(iPtr->flags & DONT_COMPILE_CMDS_INLINE)) {
- code = (*(cmdPtr->compileProc))(interp, &parse,
- envPtr);
- if (code == TCL_OK) {
- maxDepth = TclMax(envPtr->maxStackDepth,
- maxDepth);
- goto finishCommand;
- } else if (code == TCL_OUT_LINE_COMPILE) {
- /* do nothing */
- } else { /* an error */
- goto error;
- }
- }
-
- /*
- * No compile procedure so push the word. If the
- * command was found, push a CmdName object to
- * reduce runtime lookups.
- */
-
- objIndex = TclRegisterLiteral(envPtr,
- tokenPtr[1].start, tokenPtr[1].size,
- /*onHeap*/ 0);
- if (cmdPtr != NULL) {
- TclSetCmdNameObj(interp,
- envPtr->literalArrayPtr[objIndex].objPtr,
- cmdPtr);
- }
- } else {
- objIndex = TclRegisterLiteral(envPtr,
- tokenPtr[1].start, tokenPtr[1].size,
- /*onHeap*/ 0);
- }
- TclEmitPush(objIndex, envPtr);
- maxDepth = TclMax((wordIdx + 1), maxDepth);
- } else {
- /*
- * The word is not a simple string of characters.
- */
-
- code = TclCompileTokens(interp, tokenPtr+1,
- tokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- goto error;
- }
- maxDepth = TclMax((wordIdx + envPtr->maxStackDepth),
- maxDepth);
- }
- }
+ envPtr->numCommands = mapPtr->nuloc;
+ return TCL_ERROR;
+}
+
+static int
+CompileCommandTokens(
+ Tcl_Interp *interp,
+ Tcl_Parse *parsePtr,
+ CompileEnv *envPtr)
+{
+ Interp *iPtr = (Interp *) interp;
+ Tcl_Token *tokenPtr = parsePtr->tokenPtr;
+ ExtCmdLoc *eclPtr = envPtr->extCmdMapPtr;
+ Tcl_Obj *cmdObj = Tcl_NewObj();
+ Command *cmdPtr = NULL;
+ int code = TCL_ERROR;
+ int cmdKnown, expand = -1;
+ int *wlines, wlineat;
+ int cmdLine = envPtr->line;
+ int *clNext = envPtr->clNext;
+ int cmdIdx = envPtr->numCommands;
+ int startCodeOffset = envPtr->codeNext - envPtr->codeStart;
+ int depth = TclGetStackDepth(envPtr);
+
+ assert (parsePtr->numWords > 0);
+
+ /* Pre-Compile */
+
+ envPtr->numCommands++;
+ EnterCmdStartData(envPtr, cmdIdx,
+ parsePtr->commandStart - envPtr->source, startCodeOffset);
+ /*
+ * TIP #280. Scan the words and compute the extended location information.
+ * The map first contain full per-word line information for use by the
+ * compiler. This is later replaced by a reduced form which signals
+ * non-literal words, stored in 'wlines'.
+ */
+
+ EnterCmdWordData(eclPtr, parsePtr->commandStart - envPtr->source,
+ parsePtr->tokenPtr, parsePtr->commandStart,
+ parsePtr->commandSize, parsePtr->numWords, cmdLine,
+ clNext, &wlines, envPtr);
+ wlineat = eclPtr->nuloc - 1;
+
+ envPtr->line = eclPtr->loc[wlineat].line[0];
+ envPtr->clNext = eclPtr->loc[wlineat].next[0];
+
+ /* Do we know the command word? */
+ Tcl_IncrRefCount(cmdObj);
+ tokenPtr = parsePtr->tokenPtr;
+ cmdKnown = TclWordKnownAtCompileTime(tokenPtr, cmdObj);
+
+ /* Is this a command we should (try to) compile with a compileProc ? */
+ if (cmdKnown && !(iPtr->flags & DONT_COMPILE_CMDS_INLINE)) {
+ cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, cmdObj);
+ if (cmdPtr) {
/*
- * Emit an invoke instruction for the command. We skip this
- * if a compile procedure was found for the command.
+ * Found a command. Test the ways we can be told not to attempt
+ * to compile it.
*/
-
- if (wordIdx > 0) {
- if (wordIdx <= 255) {
- TclEmitInstInt1(INST_INVOKE_STK1, wordIdx, envPtr);
- } else {
- TclEmitInstInt4(INST_INVOKE_STK4, wordIdx, envPtr);
- }
+ if ((cmdPtr->compileProc == NULL)
+ || (cmdPtr->nsPtr->flags & NS_SUPPRESS_COMPILATION)
+ || (cmdPtr->flags & CMD_HAS_EXEC_TRACES)) {
+ cmdPtr = NULL;
}
+ }
+ if (cmdPtr && !(cmdPtr->flags & CMD_COMPILES_EXPANDED)) {
+ expand = ExpandRequested(parsePtr->tokenPtr, parsePtr->numWords);
+ if (expand) {
+ /* We need to expand, but compileProc cannot. */
+ cmdPtr = NULL;
+ }
+ }
+ }
+
+ /* If cmdPtr != NULL, we will try to call cmdPtr->compileProc */
+ if (cmdPtr) {
+ code = CompileCmdCompileProc(interp, parsePtr, cmdPtr, envPtr);
+ }
+
+ if (code == TCL_ERROR) {
+ if (expand < 0) {
+ expand = ExpandRequested(parsePtr->tokenPtr, parsePtr->numWords);
+ }
+
+ if (expand) {
+ CompileExpanded(interp, parsePtr->tokenPtr,
+ cmdKnown ? cmdObj : NULL, parsePtr->numWords, envPtr);
+ } else {
+ TclCompileInvocation(interp, parsePtr->tokenPtr,
+ cmdKnown ? cmdObj : NULL, parsePtr->numWords, envPtr);
+ }
+ }
+
+ Tcl_DecrRefCount(cmdObj);
+ TclEmitOpcode(INST_POP, envPtr);
+ EnterCmdExtentData(envPtr, cmdIdx,
+ parsePtr->term - parsePtr->commandStart,
+ (envPtr->codeNext-envPtr->codeStart) - startCodeOffset);
+
+ /*
+ * TIP #280: Free full form of per-word line data and insert the reduced
+ * form now
+ */
+
+ envPtr->line = cmdLine;
+ envPtr->clNext = clNext;
+ ckfree(eclPtr->loc[wlineat].line);
+ ckfree(eclPtr->loc[wlineat].next);
+ eclPtr->loc[wlineat].line = wlines;
+ eclPtr->loc[wlineat].next = NULL;
+
+ TclCheckStackDepth(depth, envPtr);
+ return cmdIdx;
+}
+
+void
+TclCompileScript(
+ Tcl_Interp *interp, /* Used for error and status reporting. Also
+ * serves as context for finding and compiling
+ * commands. May not be NULL. */
+ const char *script, /* The source script to compile. */
+ int numBytes, /* Number of bytes in script. If < 0, the
+ * script consists of all bytes up to the
+ * first null character. */
+ CompileEnv *envPtr) /* Holds resulting instructions. */
+{
+ int lastCmdIdx = -1; /* Index into envPtr->cmdMapPtr of the last
+ * command this routine compiles into bytecode.
+ * Initial value of -1 indicates this routine
+ * has not yet generated any bytecode. */
+ const char *p = script; /* Where we are in our compile. */
+ int depth = TclGetStackDepth(envPtr);
+
+ if (envPtr->iPtr == NULL) {
+ Tcl_Panic("TclCompileScript() called on uninitialized CompileEnv");
+ }
+
+ /* Each iteration compiles one command from the script. */
+
+ while (numBytes > 0) {
+ Tcl_Parse parse;
+ const char *next;
+
+ if (TCL_OK != Tcl_ParseCommand(interp, p, numBytes, 0, &parse)) {
/*
- * Update the compilation environment structure and record the
- * offsets of the source and code for the command.
+ * Compile bytecodes to report the parse error at runtime.
*/
- finishCommand:
- EnterCmdExtentData(envPtr, currCmdIndex, commandLength,
- (envPtr->codeNext-envPtr->codeStart) - startCodeOffset);
- isFirstCmd = 0;
- } /* end if parse.numWords > 0 */
+ Tcl_LogCommandInfo(interp, script, parse.commandStart,
+ parse.term + 1 - parse.commandStart);
+ TclCompileSyntaxError(interp, envPtr);
+ return;
+ }
+
+#ifdef TCL_COMPILE_DEBUG
+ /*
+ * If tracing, print a line for each top level command compiled.
+ * TODO: Suppress when numWords == 0 ?
+ */
+
+ if ((tclTraceCompile >= 1) && (envPtr->procPtr == NULL)) {
+ int commandLength = parse.term - parse.commandStart;
+ fprintf(stdout, " Compiling: ");
+ TclPrintSource(stdout, parse.commandStart,
+ TclMin(commandLength, 55));
+ fprintf(stdout, "\n");
+ }
+#endif
+
+ /*
+ * TIP #280: Count newlines before the command start.
+ * (See test info-30.33).
+ */
+
+ TclAdvanceLines(&envPtr->line, p, parse.commandStart);
+ TclAdvanceContinuations(&envPtr->line, &envPtr->clNext,
+ parse.commandStart - envPtr->source);
/*
- * Advance to the next command in the script.
+ * Advance parser to the next command in the script.
*/
-
+
next = parse.commandStart + parse.commandSize;
- bytesLeft -= (next - p);
+ numBytes -= next - p;
p = next;
- Tcl_FreeParse(&parse);
- gotParse = 0;
- if (nested && (p[-1] == ']')) {
+
+ if (parse.numWords == 0) {
/*
- * We get here in the special case where TCL_BRACKET_TERM was
- * set in the interpreter and we reached a close bracket in the
- * script. Stop compilation.
+ * The "command" parsed has no words. In this case we can skip
+ * the rest of the loop body. With no words, clearly
+ * CompileCommandTokens() has nothing to do. Since the parser
+ * aggressively sucks up leading comment and white space,
+ * including newlines, parse.commandStart must be pointing at
+ * either the end of script, or a command-terminating semi-colon.
+ * In either case, the TclAdvance*() calls have nothing to do.
+ * Finally, when no words are parsed, no tokens have been
+ * allocated at parse.tokenPtr so there's also nothing for
+ * Tcl_FreeParse() to do.
+ *
+ * The advantage of this shortcut is that CompileCommandTokens()
+ * can be written with an assumption that parse.numWords > 0, with
+ * the implication the CCT() always generates bytecode.
*/
-
- break;
+ continue;
}
- }
- /*
- * If the source script yielded no instructions (e.g., if it was empty),
- * push an empty string as the command's result.
- */
-
- if (envPtr->codeNext == entryCodeNext) {
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0),
- envPtr);
- maxDepth = 1;
- }
-
- if ((nested != 0) && (p > script) && (p[-1] == ']')) {
- iPtr->termOffset = (p - 1) - script;
- } else {
- iPtr->termOffset = (p - script);
- }
- envPtr->maxStackDepth = maxDepth;
- Tcl_DStringFree(&ds);
- return TCL_OK;
-
- error:
- /*
- * Generate various pieces of error information, such as the line
- * number where the error occurred and information to add to the
- * errorInfo variable. Then free resources that had been allocated
- * to the command.
- */
+ lastCmdIdx = CompileCommandTokens(interp, &parse, envPtr);
- commandLength = parse.commandSize;
- prev = '\0';
- if (commandLength > 0) {
- prev = parse.commandStart[commandLength-1];
- }
- if (((parse.commandStart+commandLength) != (script+numBytes))
- || ((prev == '\n') || (nested && (prev == ']')))) {
/*
- * The command where the error occurred didn't end at the end
- * of the script (i.e. it ended at a terminator character such
- * as ";". Reduce the length by one so that the error message
- * doesn't include the terminator character.
+ * TIP #280: Track lines in the just compiled command.
*/
- commandLength -= 1;
- }
- LogCompilationInfo(interp, script, parse.commandStart, commandLength);
- if (gotParse) {
+ TclAdvanceLines(&envPtr->line, parse.commandStart, p);
+ TclAdvanceContinuations(&envPtr->line, &envPtr->clNext,
+ p - envPtr->source);
Tcl_FreeParse(&parse);
}
- iPtr->termOffset = (p - script);
- envPtr->maxStackDepth = maxDepth;
- Tcl_DStringFree(&ds);
- return code;
+
+ if (lastCmdIdx == -1) {
+ /*
+ * Compiling the script yielded no bytecode. The script must be all
+ * whitespace, comments, and empty commands. Such scripts are defined
+ * to successfully produce the empty string result, so we emit the
+ * simple bytecode that makes that happen.
+ */
+
+ PushStringLiteral(envPtr, "");
+ } else {
+ /*
+ * We compiled at least one command to bytecode. The routine
+ * CompileCommandTokens() follows the bytecode of each compiled
+ * command with an INST_POP, so that stack balance is maintained when
+ * several commands are in sequence. (The result of each command is
+ * thrown away before moving on to the next command). For the last
+ * command compiled, we need to undo that INST_POP so that the result
+ * of the last command becomes the result of the script. The code
+ * here removes that trailing INST_POP.
+ */
+
+ envPtr->cmdMapPtr[lastCmdIdx].numCodeBytes--;
+ envPtr->codeNext--;
+ envPtr->currStackDepth++;
+ }
+ TclCheckStackDepth(depth+1, envPtr);
}
/*
@@ -1026,180 +2245,238 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
* TclCompileTokens --
*
* Given an array of tokens parsed from a Tcl command (e.g., the tokens
- * that make up a word) this procedure emits instructions to evaluate
- * the tokens and concatenate their values to form a single result
- * value on the interpreter's runtime evaluation stack.
+ * that make up a word) this procedure emits instructions to evaluate the
+ * tokens and concatenate their values to form a single result value on
+ * the interpreter's runtime evaluation stack.
*
* Results:
* The return value is a standard Tcl result. If an error occurs, an
* error message is left in the interpreter's result.
- *
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to evaluate the tokens.
*
* Side effects:
- * Instructions are added to envPtr to push and evaluate the tokens
- * at runtime.
+ * Instructions are added to envPtr to push and evaluate the tokens at
+ * runtime.
*
*----------------------------------------------------------------------
*/
-int
-TclCompileTokens(interp, tokenPtr, count, envPtr)
- Tcl_Interp *interp; /* Used for error and status reporting. */
- Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens
- * to compile. */
- int count; /* Number of tokens to consider at tokenPtr.
+void
+TclCompileVarSubst(
+ Tcl_Interp *interp,
+ Tcl_Token *tokenPtr,
+ CompileEnv *envPtr)
+{
+ const char *p, *name = tokenPtr[1].start;
+ int nameBytes = tokenPtr[1].size;
+ int i, localVar, localVarName = 1;
+
+ /*
+ * Determine how the variable name should be handled: if it contains any
+ * namespace qualifiers it is not a local variable (localVarName=-1); if
+ * it looks like an array element and the token has a single component, it
+ * should not be created here [Bug 569438] (localVarName=0); otherwise,
+ * the local variable can safely be created (localVarName=1).
+ */
+
+ for (i = 0, p = name; i < nameBytes; i++, p++) {
+ if ((*p == ':') && (i < nameBytes-1) && (*(p+1) == ':')) {
+ localVarName = -1;
+ break;
+ } else if ((*p == '(')
+ && (tokenPtr->numComponents == 1)
+ && (*(name + nameBytes - 1) == ')')) {
+ localVarName = 0;
+ break;
+ }
+ }
+
+ /*
+ * Either push the variable's name, or find its index in the array
+ * of local variables in a procedure frame.
+ */
+
+ localVar = -1;
+ if (localVarName != -1) {
+ localVar = TclFindCompiledLocal(name, nameBytes, localVarName, envPtr);
+ }
+ if (localVar < 0) {
+ PushLiteral(envPtr, name, nameBytes);
+ }
+
+ /*
+ * Emit instructions to load the variable.
+ */
+
+ TclAdvanceLines(&envPtr->line, tokenPtr[1].start,
+ tokenPtr[1].start + tokenPtr[1].size);
+
+ if (tokenPtr->numComponents == 1) {
+ if (localVar < 0) {
+ TclEmitOpcode(INST_LOAD_STK, envPtr);
+ } else if (localVar <= 255) {
+ TclEmitInstInt1(INST_LOAD_SCALAR1, localVar, envPtr);
+ } else {
+ TclEmitInstInt4(INST_LOAD_SCALAR4, localVar, envPtr);
+ }
+ } else {
+ TclCompileTokens(interp, tokenPtr+2, tokenPtr->numComponents-1, envPtr);
+ if (localVar < 0) {
+ TclEmitOpcode(INST_LOAD_ARRAY_STK, envPtr);
+ } else if (localVar <= 255) {
+ TclEmitInstInt1(INST_LOAD_ARRAY1, localVar, envPtr);
+ } else {
+ TclEmitInstInt4(INST_LOAD_ARRAY4, localVar, envPtr);
+ }
+ }
+}
+
+void
+TclCompileTokens(
+ Tcl_Interp *interp, /* Used for error and status reporting. */
+ Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens to
+ * compile. */
+ int count, /* Number of tokens to consider at tokenPtr.
* Must be at least 1. */
- CompileEnv *envPtr; /* Holds the resulting instructions. */
+ CompileEnv *envPtr) /* Holds the resulting instructions. */
{
Tcl_DString textBuffer; /* Holds concatenated chars from adjacent
* TCL_TOKEN_TEXT, TCL_TOKEN_BS tokens. */
char buffer[TCL_UTF_MAX];
- char *name, *p;
- int numObjsToConcat, nameBytes, hasNsQualifiers, localVar;
- int length, maxDepth, depthForVar, i, code;
+ int i, numObjsToConcat, length, adjust;
unsigned char *entryCodeNext = envPtr->codeNext;
+#define NUM_STATIC_POS 20
+ int isLiteral, maxNumCL, numCL;
+ int *clPosition = NULL;
+ int depth = TclGetStackDepth(envPtr);
+ /*
+ * For the handling of continuation lines in literals we first check if
+ * this is actually a literal. For if not we can forego the additional
+ * processing. Otherwise we pre-allocate a small table to store the
+ * locations of all continuation lines we find in this literal, if any.
+ * The table is extended if needed.
+ *
+ * Note: Different to the equivalent code in function 'TclSubstTokens()'
+ * (see file "tclParse.c") we do not seem to need the 'adjust' variable.
+ * We also do not seem to need code which merges continuation line
+ * information of multiple words which concat'd at runtime. Either that or
+ * I have not managed to find a test case for these two possibilities yet.
+ * It might be a difference between compile- versus run-time processing.
+ */
+
+ numCL = 0;
+ maxNumCL = 0;
+ isLiteral = 1;
+ for (i=0 ; i < count; i++) {
+ if ((tokenPtr[i].type != TCL_TOKEN_TEXT)
+ && (tokenPtr[i].type != TCL_TOKEN_BS)) {
+ isLiteral = 0;
+ break;
+ }
+ }
+
+ if (isLiteral) {
+ maxNumCL = NUM_STATIC_POS;
+ clPosition = ckalloc(maxNumCL * sizeof(int));
+ }
+
+ adjust = 0;
Tcl_DStringInit(&textBuffer);
- maxDepth = 0;
numObjsToConcat = 0;
for ( ; count > 0; count--, tokenPtr++) {
switch (tokenPtr->type) {
- case TCL_TOKEN_TEXT:
- Tcl_DStringAppend(&textBuffer, tokenPtr->start,
- tokenPtr->size);
- break;
+ case TCL_TOKEN_TEXT:
+ TclDStringAppendToken(&textBuffer, tokenPtr);
+ TclAdvanceLines(&envPtr->line, tokenPtr->start,
+ tokenPtr->start + tokenPtr->size);
+ break;
- case TCL_TOKEN_BS:
- length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,
- buffer);
- Tcl_DStringAppend(&textBuffer, buffer, length);
- break;
+ case TCL_TOKEN_BS:
+ length = TclParseBackslash(tokenPtr->start, tokenPtr->size,
+ NULL, buffer);
+ Tcl_DStringAppend(&textBuffer, buffer, length);
- case TCL_TOKEN_COMMAND:
- /*
- * Push any accumulated chars appearing before the command.
- */
-
- if (Tcl_DStringLength(&textBuffer) > 0) {
- int literal;
-
- literal = TclRegisterLiteral(envPtr,
- Tcl_DStringValue(&textBuffer),
- Tcl_DStringLength(&textBuffer), /*onHeap*/ 0);
- TclEmitPush(literal, envPtr);
- numObjsToConcat++;
- maxDepth = TclMax(numObjsToConcat, maxDepth);
- Tcl_DStringFree(&textBuffer);
- }
-
- code = TclCompileScript(interp, tokenPtr->start+1,
- tokenPtr->size-2, /*nested*/ 1, envPtr);
- if (code != TCL_OK) {
- goto error;
- }
- maxDepth = TclMax((numObjsToConcat + envPtr->maxStackDepth),
- maxDepth);
- numObjsToConcat++;
- break;
+ /*
+ * If the backslash sequence we found is in a literal, and
+ * represented a continuation line, we compute and store its
+ * location (as char offset to the beginning of the _result_
+ * script). We may have to extend the table of locations.
+ *
+ * Note that the continuation line information is relevant even if
+ * the word we are processing is not a literal, as it can affect
+ * nested commands. See the branch for TCL_TOKEN_COMMAND below,
+ * where the adjustment we are tracking here is taken into
+ * account. The good thing is that we do not need a table of
+ * everything, just the number of lines we have to add as
+ * correction.
+ */
- case TCL_TOKEN_VARIABLE:
- /*
- * Push any accumulated chars appearing before the $<var>.
- */
-
- if (Tcl_DStringLength(&textBuffer) > 0) {
- int literal;
-
- literal = TclRegisterLiteral(envPtr,
- Tcl_DStringValue(&textBuffer),
- Tcl_DStringLength(&textBuffer), /*onHeap*/ 0);
- TclEmitPush(literal, envPtr);
- numObjsToConcat++;
- maxDepth = TclMax(numObjsToConcat, maxDepth);
- Tcl_DStringFree(&textBuffer);
- }
-
- /*
- * Check if the name contains any namespace qualifiers.
- */
-
- name = tokenPtr[1].start;
- nameBytes = tokenPtr[1].size;
- hasNsQualifiers = 0;
- for (i = 0, p = name; i < nameBytes; i++, p++) {
- if ((*p == ':') && (i < (nameBytes-1))
- && (*(p+1) == ':')) {
- hasNsQualifiers = 1;
- break;
+ if ((length == 1) && (buffer[0] == ' ') &&
+ (tokenPtr->start[1] == '\n')) {
+ if (isLiteral) {
+ int clPos = Tcl_DStringLength(&textBuffer);
+
+ if (numCL >= maxNumCL) {
+ maxNumCL *= 2;
+ clPosition = ckrealloc(clPosition,
+ maxNumCL * sizeof(int));
}
+ clPosition[numCL] = clPos;
+ numCL ++;
}
+ adjust++;
+ }
+ break;
- /*
- * Either push the variable's name, or find its index in
- * the array of local variables in a procedure frame.
- */
+ case TCL_TOKEN_COMMAND:
+ /*
+ * Push any accumulated chars appearing before the command.
+ */
- depthForVar = 0;
- if ((envPtr->procPtr == NULL) || hasNsQualifiers) {
- localVar = -1;
- TclEmitPush(TclRegisterLiteral(envPtr, name, nameBytes,
- /*onHeap*/ 0), envPtr);
- depthForVar = 1;
- } else {
- localVar = TclFindCompiledLocal(name, nameBytes,
- /*create*/ 0, /*flags*/ 0, envPtr->procPtr);
- if (localVar < 0) {
- TclEmitPush(TclRegisterLiteral(envPtr, name,
- nameBytes, /*onHeap*/ 0), envPtr);
- depthForVar = 1;
- }
- }
+ if (Tcl_DStringLength(&textBuffer) > 0) {
+ int literal = TclRegisterDStringLiteral(envPtr, &textBuffer);
- /*
- * Emit instructions to load the variable.
- */
-
- if (tokenPtr->numComponents == 1) {
- if (localVar < 0) {
- TclEmitOpcode(INST_LOAD_SCALAR_STK, envPtr);
- } else if (localVar <= 255) {
- TclEmitInstInt1(INST_LOAD_SCALAR1, localVar,
- envPtr);
- } else {
- TclEmitInstInt4(INST_LOAD_SCALAR4, localVar,
- envPtr);
- }
- } else {
- code = TclCompileTokens(interp, tokenPtr+2,
- tokenPtr->numComponents-1, envPtr);
- if (code != TCL_OK) {
- sprintf(buffer,
- "\n (parsing index for array \"%.*s\")",
- ((nameBytes > 100)? 100 : nameBytes), name);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- goto error;
- }
- depthForVar += envPtr->maxStackDepth;
- if (localVar < 0) {
- TclEmitOpcode(INST_LOAD_ARRAY_STK, envPtr);
- } else if (localVar <= 255) {
- TclEmitInstInt1(INST_LOAD_ARRAY1, localVar,
- envPtr);
- } else {
- TclEmitInstInt4(INST_LOAD_ARRAY4, localVar,
- envPtr);
- }
+ TclEmitPush(literal, envPtr);
+ numObjsToConcat++;
+ Tcl_DStringFree(&textBuffer);
+
+ if (numCL) {
+ TclContinuationsEnter(TclFetchLiteral(envPtr, literal),
+ numCL, clPosition);
}
- maxDepth = TclMax(numObjsToConcat + depthForVar, maxDepth);
+ numCL = 0;
+ }
+
+ envPtr->line += adjust;
+ TclCompileScript(interp, tokenPtr->start+1,
+ tokenPtr->size-2, envPtr);
+ envPtr->line -= adjust;
+ numObjsToConcat++;
+ break;
+
+ case TCL_TOKEN_VARIABLE:
+ /*
+ * Push any accumulated chars appearing before the $<var>.
+ */
+
+ if (Tcl_DStringLength(&textBuffer) > 0) {
+ int literal;
+
+ literal = TclRegisterDStringLiteral(envPtr, &textBuffer);
+ TclEmitPush(literal, envPtr);
numObjsToConcat++;
- count -= tokenPtr->numComponents;
- tokenPtr += tokenPtr->numComponents;
- break;
+ Tcl_DStringFree(&textBuffer);
+ }
- default:
- panic("Unexpected token type in TclCompileTokens");
+ TclCompileVarSubst(interp, tokenPtr, envPtr);
+ numObjsToConcat++;
+ count -= tokenPtr->numComponents;
+ tokenPtr += tokenPtr->numComponents;
+ break;
+
+ default:
+ Tcl_Panic("Unexpected token type in TclCompileTokens: %d; %.*s",
+ tokenPtr->type, tokenPtr->size, tokenPtr->start);
}
}
@@ -1208,13 +2485,15 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
*/
if (Tcl_DStringLength(&textBuffer) > 0) {
- int literal;
+ int literal = TclRegisterDStringLiteral(envPtr, &textBuffer);
- literal = TclRegisterLiteral(envPtr, Tcl_DStringValue(&textBuffer),
- Tcl_DStringLength(&textBuffer), /*onHeap*/ 0);
TclEmitPush(literal, envPtr);
numObjsToConcat++;
- maxDepth = TclMax(numObjsToConcat, maxDepth);
+ if (numCL) {
+ TclContinuationsEnter(TclFetchLiteral(envPtr, literal),
+ numCL, clPosition);
+ }
+ numCL = 0;
}
/*
@@ -1222,30 +2501,31 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
*/
while (numObjsToConcat > 255) {
- TclEmitInstInt1(INST_CONCAT1, 255, envPtr);
+ TclEmitInstInt1(INST_STR_CONCAT1, 255, envPtr);
numObjsToConcat -= 254; /* concat pushes 1 obj, the result */
}
if (numObjsToConcat > 1) {
- TclEmitInstInt1(INST_CONCAT1, numObjsToConcat, envPtr);
+ TclEmitInstInt1(INST_STR_CONCAT1, numObjsToConcat, envPtr);
}
/*
* If the tokens yielded no instructions, push an empty string.
*/
-
+
if (envPtr->codeNext == entryCodeNext) {
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0),
- envPtr);
- maxDepth = 1;
+ PushStringLiteral(envPtr, "");
}
Tcl_DStringFree(&textBuffer);
- envPtr->maxStackDepth = maxDepth;
- return TCL_OK;
- error:
- Tcl_DStringFree(&textBuffer);
- envPtr->maxStackDepth = maxDepth;
- return code;
+ /*
+ * Release the temp table we used to collect the locations of continuation
+ * lines, if any.
+ */
+
+ if (maxNumCL) {
+ ckfree(clPosition);
+ }
+ TclCheckStackDepth(depth+1, envPtr);
}
/*
@@ -1256,15 +2536,12 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
* Given an array of parse tokens for a word containing one or more Tcl
* commands, emit inline instructions to execute them. This procedure
* differs from TclCompileTokens in that a simple word such as a loop
- * body enclosed in braces is not just pushed as a string, but is
- * itself parsed into tokens and compiled.
+ * body enclosed in braces is not just pushed as a string, but is itself
+ * parsed into tokens and compiled.
*
* Results:
* The return value is a standard Tcl result. If an error occurs, an
* error message is left in the interpreter's result.
- *
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the tokens.
*
* Side effects:
* Instructions are added to envPtr to execute the tokens at runtime.
@@ -1272,41 +2549,32 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
*----------------------------------------------------------------------
*/
-int
-TclCompileCmdWord(interp, tokenPtr, count, envPtr)
- Tcl_Interp *interp; /* Used for error and status reporting. */
- Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens
- * for a command word to compile inline. */
- int count; /* Number of tokens to consider at tokenPtr.
+void
+TclCompileCmdWord(
+ Tcl_Interp *interp, /* Used for error and status reporting. */
+ Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens for
+ * a command word to compile inline. */
+ int count, /* Number of tokens to consider at tokenPtr.
* Must be at least 1. */
- CompileEnv *envPtr; /* Holds the resulting instructions. */
+ CompileEnv *envPtr) /* Holds the resulting instructions. */
{
- int code;
-
- /*
- * Handle the common case: if there is a single text token, compile it
- * into an inline sequence of instructions.
- */
-
- envPtr->maxStackDepth = 0;
if ((count == 1) && (tokenPtr->type == TCL_TOKEN_TEXT)) {
- code = TclCompileScript(interp, tokenPtr->start, tokenPtr->size,
- /*nested*/ 0, envPtr);
- return code;
- }
+ /*
+ * Handle the common case: if there is a single text token, compile it
+ * into an inline sequence of instructions.
+ */
- /*
- * Multiple tokens or the single token involves substitutions. Emit
- * instructions to invoke the eval command procedure at runtime on the
- * result of evaluating the tokens.
- */
+ TclCompileScript(interp, tokenPtr->start, tokenPtr->size, envPtr);
+ } else {
+ /*
+ * Multiple tokens or the single token involves substitutions. Emit
+ * instructions to invoke the eval command procedure at runtime on the
+ * result of evaluating the tokens.
+ */
- code = TclCompileTokens(interp, tokenPtr, count, envPtr);
- if (code != TCL_OK) {
- return code;
+ TclCompileTokens(interp, tokenPtr, count, envPtr);
+ TclEmitInvoke(envPtr, INST_EVAL_STK);
}
- TclEmitOpcode(INST_EVAL_STK, envPtr);
- return TCL_OK;
}
/*
@@ -1323,9 +2591,6 @@ TclCompileCmdWord(interp, tokenPtr, count, envPtr)
* Results:
* The return value is a standard Tcl result. If an error occurs, an
* error message is left in the interpreter's result.
- *
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the expression.
*
* Side effects:
* Instructions are added to envPtr to execute the expression.
@@ -1333,209 +2598,95 @@ TclCompileCmdWord(interp, tokenPtr, count, envPtr)
*----------------------------------------------------------------------
*/
-int
-TclCompileExprWords(interp, tokenPtr, numWords, envPtr)
- Tcl_Interp *interp; /* Used for error and status reporting. */
- Tcl_Token *tokenPtr; /* Points to first in an array of word
- * tokens tokens for the expression to
- * compile inline. */
- int numWords; /* Number of word tokens starting at
- * tokenPtr. Must be at least 1. Each word
- * token contains one or more subtokens. */
- CompileEnv *envPtr; /* Holds the resulting instructions. */
-{
- Tcl_Token *wordPtr, *partPtr;
- JumpFixup jumpFixup;
- int maxDepth, doExprInline, range, numBytes, i, j, code;
- char *script;
- char saveChar;
- int saveExprIsJustVarRef = envPtr->exprIsJustVarRef;
- int saveExprIsComparison = envPtr->exprIsComparison;
-
- envPtr->maxStackDepth = 0;
- maxDepth = 0;
- range = -1;
- code = TCL_OK;
+void
+TclCompileExprWords(
+ Tcl_Interp *interp, /* Used for error and status reporting. */
+ Tcl_Token *tokenPtr, /* Points to first in an array of word tokens
+ * tokens for the expression to compile
+ * inline. */
+ int numWords, /* Number of word tokens starting at tokenPtr.
+ * Must be at least 1. Each word token
+ * contains one or more subtokens. */
+ CompileEnv *envPtr) /* Holds the resulting instructions. */
+{
+ Tcl_Token *wordPtr;
+ int i, concatItems;
/*
- * If the expression is a single word that doesn't require
- * substitutions, just compile it's string into inline instructions.
+ * If the expression is a single word that doesn't require substitutions,
+ * just compile its string into inline instructions.
*/
if ((numWords == 1) && (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD)) {
- /*
- * Temporarily overwrite the character just after the end of the
- * string with a 0 byte.
- */
-
- script = tokenPtr[1].start;
- numBytes = tokenPtr[1].size;
- saveChar = script[numBytes];
- script[numBytes] = 0;
- code = TclCompileExpr(interp, script, numBytes, envPtr);
- script[numBytes] = saveChar;
- return code;
- }
-
- /*
- * Multiple words or the single word requires substitutions. We may
- * need to call expr's command proc at runtime. This often recompiles
- * the expression each time and is slow. However, there are some
- * circumstances where we can still compile inline code "optimistically"
- * and check for type errors during execution that signal when double
- * substitutions must be done.
- */
-
- doExprInline = 1;
- wordPtr = tokenPtr;
- for (i = 0; ((i < numWords) && doExprInline); i++) {
- if (wordPtr->type == TCL_TOKEN_WORD) {
- for (j = 0, partPtr = wordPtr+1; j < wordPtr->numComponents;
- j++, partPtr++) {
- if ((partPtr->type == TCL_TOKEN_BS)
- || (partPtr->type == TCL_TOKEN_COMMAND)) {
- doExprInline = 0;
- break;
- }
- }
- }
- wordPtr += (wordPtr->numComponents + 1);
+ TclCompileExpr(interp, tokenPtr[1].start,tokenPtr[1].size, envPtr, 1);
+ return;
}
/*
- * If only variable substitutions appear (no backslash or command
- * substitutions), inline compile the expr inside a "catch" so that if
- * there is any error, we call expr's command proc at runtime.
- */
-
- if (doExprInline) {
- Tcl_DString exprBuffer;
- int startCodeOffset = (envPtr->codeNext - envPtr->codeStart);
- int startExceptNext = envPtr->exceptArrayNext;
-
- envPtr->exceptDepth++;
- envPtr->maxExceptDepth =
- TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);
- range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
- TclEmitInstInt4(INST_BEGIN_CATCH4, range, envPtr);
-
- Tcl_DStringInit(&exprBuffer);
- wordPtr = tokenPtr;
- for (i = 0; i < numWords; i++) {
- if (i > 0) {
- Tcl_DStringAppend(&exprBuffer, " ", 1);
- }
- for (j = 0, partPtr = wordPtr+1; j < wordPtr->numComponents;
- j++, partPtr++) {
- switch (partPtr->type) {
- case TCL_TOKEN_TEXT:
- Tcl_DStringAppend(&exprBuffer, partPtr->start,
- partPtr->size);
- break;
-
- case TCL_TOKEN_VARIABLE:
- Tcl_DStringAppend(&exprBuffer, partPtr->start,
- partPtr->size);
- j += partPtr->numComponents;
- partPtr += partPtr->numComponents;
- break;
-
- default:
- panic("unexpected token type in TclCompileExprWords");
- }
- }
- wordPtr += (wordPtr->numComponents + 1);
- }
- envPtr->exceptArrayPtr[range].codeOffset =
- (envPtr->codeNext - envPtr->codeStart);
- code = TclCompileExpr(interp, Tcl_DStringValue(&exprBuffer),
- Tcl_DStringLength(&exprBuffer), envPtr);
- envPtr->exceptArrayPtr[range].numCodeBytes =
- (envPtr->codeNext - envPtr->codeStart)
- - envPtr->exceptArrayPtr[range].codeOffset;
- maxDepth = envPtr->maxStackDepth;
- Tcl_DStringFree(&exprBuffer);
-
- if ((code != TCL_OK) || (envPtr->exprIsJustVarRef)
- || (envPtr->exprIsComparison)) {
- /*
- * Delete the inline code and call the expr command proc at
- * runtime. There was a compilation error or the inline code
- * might not have the right 2 level substitution semantics:
- * e.g., if the expr consisted of a single variable ref or the
- * top-level operator is a comparison (which might operate on
- * strings). The code might appear to execute successfully but
- * produce the wrong result. We depend on execution failing if a
- * second level of substitutions is required.
- */
-
- envPtr->codeNext = (envPtr->codeStart + startCodeOffset);
- envPtr->exceptArrayNext = startExceptNext;
- doExprInline = 0;
- } else {
- TclEmitOpcode(INST_END_CATCH, envPtr); /* for ok case */
- TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);
- envPtr->exceptArrayPtr[range].catchOffset =
- (envPtr->codeNext - envPtr->codeStart);
- TclEmitOpcode(INST_END_CATCH, envPtr); /* for error case */
- }
- }
-
- /*
* Emit code to call the expr command proc at runtime. Concatenate the
* (already substituted once) expr tokens with a space between each.
*/
wordPtr = tokenPtr;
for (i = 0; i < numWords; i++) {
- code = TclCompileTokens(interp, wordPtr+1, wordPtr->numComponents,
- envPtr);
- if (code != TCL_OK) {
- break;
- }
+ CompileTokens(envPtr, wordPtr, interp);
if (i < (numWords - 1)) {
- TclEmitPush(TclRegisterLiteral(envPtr, " ", 1, /*onHeap*/ 0),
- envPtr);
- maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
- } else {
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
+ PushStringLiteral(envPtr, " ");
}
- wordPtr += (wordPtr->numComponents + 1);
+ wordPtr += wordPtr->numComponents + 1;
}
- if (code == TCL_OK) {
- int concatItems = 2*numWords - 1;
- while (concatItems > 255) {
- TclEmitInstInt1(INST_CONCAT1, 255, envPtr);
- concatItems -= 254;
- }
- if (concatItems > 1) {
- TclEmitInstInt1(INST_CONCAT1, concatItems, envPtr);
- }
- TclEmitOpcode(INST_EXPR_STK, envPtr);
+ concatItems = 2*numWords - 1;
+ while (concatItems > 255) {
+ TclEmitInstInt1(INST_STR_CONCAT1, 255, envPtr);
+ concatItems -= 254;
}
-
- /*
- * If generating inline code, update the target of the jump at the end.
- */
-
- if (doExprInline) {
- int jumpDist = (envPtr->codeNext - envPtr->codeStart)
- - jumpFixup.codeOffset;
- if (TclFixupForwardJump(envPtr, &jumpFixup, jumpDist, 127)) {
- /*
- * Update the inline expression code's catch ExceptionRange
- * target since it, being after the jump, also moved down.
- */
-
- envPtr->exceptArrayPtr[range].catchOffset += 3;
+ if (concatItems > 1) {
+ TclEmitInstInt1(INST_STR_CONCAT1, concatItems, envPtr);
+ }
+ TclEmitOpcode(INST_EXPR_STK, envPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileNoOp --
+ *
+ * Function called to compile no-op's
+ *
+ * Results:
+ * The return value is TCL_OK, indicating successful compilation.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute a no-op at runtime. No
+ * result is pushed onto the stack: the compiler has to take care of this
+ * itself if the last compiled command is a NoOp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileNoOp(
+ Tcl_Interp *interp, /* Used for error reporting. */
+ Tcl_Parse *parsePtr, /* Points to a parse structure for the command
+ * created by Tcl_ParseCommand. */
+ Command *cmdPtr, /* Points to defintion of command being
+ * compiled. */
+ CompileEnv *envPtr) /* Holds resulting instructions. */
+{
+ Tcl_Token *tokenPtr;
+ int i;
+
+ tokenPtr = parsePtr->tokenPtr;
+ for (i = 1; i < parsePtr->numWords; i++) {
+ tokenPtr = tokenPtr + tokenPtr->numComponents + 1;
+
+ if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ CompileTokens(envPtr, tokenPtr, interp);
+ TclEmitOpcode(INST_POP, envPtr);
}
- envPtr->exceptDepth--;
}
-
- envPtr->exprIsJustVarRef = saveExprIsJustVarRef;
- envPtr->exprIsComparison = saveExprIsComparison;
- envPtr->maxStackDepth = maxDepth;
- return code;
+ PushStringLiteral(envPtr, "");
+ return TCL_OK;
}
/*
@@ -1544,10 +2695,10 @@ TclCompileExprWords(interp, tokenPtr, numWords, envPtr)
* TclInitByteCodeObj --
*
* Create a ByteCode structure and initialize it from a CompileEnv
- * compilation environment structure. The ByteCode structure is
- * smaller and contains just that information needed to execute
- * the bytecode instructions resulting from compiling a Tcl script.
- * The resulting structure is placed in the specified object.
+ * compilation environment structure. The ByteCode structure is smaller
+ * and contains just that information needed to execute the bytecode
+ * instructions resulting from compiling a Tcl script. The resulting
+ * structure is placed in the specified object.
*
* Results:
* A newly constructed ByteCode object is stored in the internal
@@ -1555,65 +2706,75 @@ TclCompileExprWords(interp, tokenPtr, numWords, envPtr)
*
* Side effects:
* A single heap object is allocated to hold the new ByteCode structure
- * and its code, object, command location, and aux data arrays. Note
- * that "ownership" (i.e., the pointers to) the Tcl objects and aux
- * data items will be handed over to the new ByteCode structure from
- * the CompileEnv structure.
+ * and its code, object, command location, and aux data arrays. Note that
+ * "ownership" (i.e., the pointers to) the Tcl objects and aux data items
+ * will be handed over to the new ByteCode structure from the CompileEnv
+ * structure.
*
*----------------------------------------------------------------------
*/
void
-TclInitByteCodeObj(objPtr, envPtr)
- Tcl_Obj *objPtr; /* Points object that should be
- * initialized, and whose string rep
- * contains the source code. */
- register CompileEnv *envPtr; /* Points to the CompileEnv structure from
- * which to create a ByteCode structure. */
+TclInitByteCodeObj(
+ Tcl_Obj *objPtr, /* Points object that should be initialized,
+ * and whose string rep contains the source
+ * code. */
+ register CompileEnv *envPtr)/* Points to the CompileEnv structure from
+ * which to create a ByteCode structure. */
{
register ByteCode *codePtr;
size_t codeBytes, objArrayBytes, exceptArrayBytes, cmdLocBytes;
size_t auxDataArrayBytes, structureSize;
register unsigned char *p;
+#ifdef TCL_COMPILE_DEBUG
unsigned char *nextPtr;
+#endif
int numLitObjects = envPtr->literalArrayNext;
Namespace *namespacePtr;
- int i;
+ int i, isNew;
Interp *iPtr;
+ if (envPtr->iPtr == NULL) {
+ Tcl_Panic("TclInitByteCodeObj() called on uninitialized CompileEnv");
+ }
+
iPtr = envPtr->iPtr;
- codeBytes = (envPtr->codeNext - envPtr->codeStart);
- objArrayBytes = (envPtr->literalArrayNext * sizeof(Tcl_Obj *));
- exceptArrayBytes = (envPtr->exceptArrayNext * sizeof(ExceptionRange));
- auxDataArrayBytes = (envPtr->auxDataArrayNext * sizeof(AuxData));
+ codeBytes = envPtr->codeNext - envPtr->codeStart;
+ objArrayBytes = envPtr->literalArrayNext * sizeof(Tcl_Obj *);
+ exceptArrayBytes = envPtr->exceptArrayNext * sizeof(ExceptionRange);
+ auxDataArrayBytes = envPtr->auxDataArrayNext * sizeof(AuxData);
cmdLocBytes = GetCmdLocEncodingSize(envPtr);
-
+
/*
* Compute the total number of bytes needed for this bytecode.
*/
structureSize = sizeof(ByteCode);
- structureSize += TCL_ALIGN(codeBytes); /* align object array */
- structureSize += TCL_ALIGN(objArrayBytes); /* align exc range arr */
+ structureSize += TCL_ALIGN(codeBytes); /* align object array */
+ structureSize += TCL_ALIGN(objArrayBytes); /* align exc range arr */
structureSize += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */
structureSize += auxDataArrayBytes;
structureSize += cmdLocBytes;
if (envPtr->iPtr->varFramePtr != NULL) {
- namespacePtr = envPtr->iPtr->varFramePtr->nsPtr;
+ namespacePtr = envPtr->iPtr->varFramePtr->nsPtr;
} else {
- namespacePtr = envPtr->iPtr->globalNsPtr;
+ namespacePtr = envPtr->iPtr->globalNsPtr;
}
-
- p = (unsigned char *) ckalloc((size_t) structureSize);
+
+ p = ckalloc(structureSize);
codePtr = (ByteCode *) p;
codePtr->interpHandle = TclHandlePreserve(iPtr->handle);
codePtr->compileEpoch = iPtr->compileEpoch;
codePtr->nsPtr = namespacePtr;
codePtr->nsEpoch = namespacePtr->resolverEpoch;
codePtr->refCount = 1;
- codePtr->flags = 0;
+ if (namespacePtr->compiledVarResProc || iPtr->resolverPtr) {
+ codePtr->flags = TCL_BYTECODE_RESOLVE_VARS;
+ } else {
+ codePtr->flags = 0;
+ }
codePtr->source = envPtr->source;
codePtr->procPtr = envPtr->procPtr;
@@ -1626,43 +2787,65 @@ TclInitByteCodeObj(objPtr, envPtr)
codePtr->numCmdLocBytes = cmdLocBytes;
codePtr->maxExceptDepth = envPtr->maxExceptDepth;
codePtr->maxStackDepth = envPtr->maxStackDepth;
-
+
p += sizeof(ByteCode);
codePtr->codeStart = p;
- memcpy((VOID *) p, (VOID *) envPtr->codeStart, (size_t) codeBytes);
-
- p += TCL_ALIGN(codeBytes); /* align object array */
+ memcpy(p, envPtr->codeStart, (size_t) codeBytes);
+
+ p += TCL_ALIGN(codeBytes); /* align object array */
codePtr->objArrayPtr = (Tcl_Obj **) p;
for (i = 0; i < numLitObjects; i++) {
- codePtr->objArrayPtr[i] = envPtr->literalArrayPtr[i].objPtr;
+ Tcl_Obj *fetched = TclFetchLiteral(envPtr, i);
+
+ if (objPtr == fetched) {
+ /*
+ * Prevent circular reference where the bytecode intrep of
+ * a value contains a literal which is that same value.
+ * If this is allowed to happen, refcount decrements may not
+ * reach zero, and memory may leak. Bugs 467523, 3357771
+ *
+ * NOTE: [Bugs 3392070, 3389764] We make a copy based completely
+ * on the string value, and do not call Tcl_DuplicateObj() so we
+ * can be sure we do not have any lingering cycles hiding in
+ * the intrep.
+ */
+ int numBytes;
+ const char *bytes = Tcl_GetStringFromObj(objPtr, &numBytes);
+
+ codePtr->objArrayPtr[i] = Tcl_NewStringObj(bytes, numBytes);
+ Tcl_IncrRefCount(codePtr->objArrayPtr[i]);
+ TclReleaseLiteral((Tcl_Interp *)iPtr, objPtr);
+ } else {
+ codePtr->objArrayPtr[i] = fetched;
+ }
}
- p += TCL_ALIGN(objArrayBytes); /* align exception range array */
+ p += TCL_ALIGN(objArrayBytes); /* align exception range array */
if (exceptArrayBytes > 0) {
codePtr->exceptArrayPtr = (ExceptionRange *) p;
- memcpy((VOID *) p, (VOID *) envPtr->exceptArrayPtr,
- (size_t) exceptArrayBytes);
+ memcpy(p, envPtr->exceptArrayPtr, (size_t) exceptArrayBytes);
} else {
codePtr->exceptArrayPtr = NULL;
}
-
- p += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */
+
+ p += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */
if (auxDataArrayBytes > 0) {
codePtr->auxDataArrayPtr = (AuxData *) p;
- memcpy((VOID *) p, (VOID *) envPtr->auxDataArrayPtr,
- (size_t) auxDataArrayBytes);
+ memcpy(p, envPtr->auxDataArrayPtr, (size_t) auxDataArrayBytes);
} else {
codePtr->auxDataArrayPtr = NULL;
}
p += auxDataArrayBytes;
+#ifndef TCL_COMPILE_DEBUG
+ EncodeCmdLocMap(envPtr, codePtr, (unsigned char *) p);
+#else
nextPtr = EncodeCmdLocMap(envPtr, codePtr, (unsigned char *) p);
-#ifdef TCL_COMPILE_DEBUG
- if (((size_t)(nextPtr - p)) != cmdLocBytes) {
- panic("TclInitByteCodeObj: encoded cmd location bytes %d != expected size %d\n", (nextPtr - p), cmdLocBytes);
+ if (((size_t)(nextPtr - p)) != cmdLocBytes) {
+ Tcl_Panic("TclInitByteCodeObj: encoded cmd location bytes %lu != expected size %lu", (unsigned long)(nextPtr - p), (unsigned long)cmdLocBytes);
}
#endif
-
+
/*
* Record various compilation-related statistics about the new ByteCode
* structure. Don't include overhead for statistics-related fields.
@@ -1671,98 +2854,33 @@ TclInitByteCodeObj(objPtr, envPtr)
#ifdef TCL_COMPILE_STATS
codePtr->structureSize = structureSize
- (sizeof(size_t) + sizeof(Tcl_Time));
- TclpGetTime(&(codePtr->createTime));
-
+ Tcl_GetTime(&codePtr->createTime);
+
RecordByteCodeStats(codePtr);
#endif /* TCL_COMPILE_STATS */
-
+
/*
- * Free the old internal rep then convert the object to a
- * bytecode object by making its internal rep point to the just
- * compiled ByteCode.
+ * Free the old internal rep then convert the object to a bytecode object
+ * by making its internal rep point to the just compiled ByteCode.
*/
-
- if ((objPtr->typePtr != NULL) &&
- (objPtr->typePtr->freeIntRepProc != NULL)) {
- (*objPtr->typePtr->freeIntRepProc)(objPtr);
- }
- objPtr->internalRep.otherValuePtr = (VOID *) codePtr;
- objPtr->typePtr = &tclByteCodeType;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * LogCompilationInfo --
- *
- * This procedure is invoked after an error occurs during compilation.
- * It adds information to the "errorInfo" variable to describe the
- * command that was being compiled when the error occurred.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information about the command is added to errorInfo and the
- * line number stored internally in the interpreter is set. If this
- * is the first call to this procedure or Tcl_AddObjErrorInfo since
- * an error occurred, then old information in errorInfo is
- * deleted.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-LogCompilationInfo(interp, script, command, length)
- Tcl_Interp *interp; /* Interpreter in which to log the
- * information. */
- char *script; /* First character in script containing
- * command (must be <= command). */
- 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). */
-{
- char buffer[200];
- register char *p;
- char *ellipsis = "";
- Interp *iPtr = (Interp *) interp;
- if (iPtr->flags & ERR_ALREADY_LOGGED) {
- /*
- * Someone else has already logged error information for this
- * command; we shouldn't add anything more.
- */
-
- return;
- }
+ TclFreeIntRep(objPtr);
+ objPtr->internalRep.twoPtrValue.ptr1 = codePtr;
+ objPtr->typePtr = &tclByteCodeType;
/*
- * Compute the line number where the error occurred.
+ * TIP #280. Associate the extended per-word line information with the
+ * byte code object (internal rep), for use with the bc compiler.
*/
- iPtr->errorLine = 1;
- for (p = script; p != command; p++) {
- if (*p == '\n') {
- iPtr->errorLine++;
- }
- }
+ Tcl_SetHashValue(Tcl_CreateHashEntry(iPtr->lineBCPtr, codePtr,
+ &isNew), envPtr->extCmdMapPtr);
+ envPtr->extCmdMapPtr = NULL;
- /*
- * Create an error message to add to errorInfo, including up to a
- * maximum number of characters of the command.
- */
+ /* We've used up the CompileEnv. Mark as uninitialized. */
+ envPtr->iPtr = NULL;
- if (length < 0) {
- length = strlen(command);
- }
- if (length > 150) {
- length = 150;
- ellipsis = "...";
- }
- sprintf(buffer, "\n while compiling\n\"%.*s%s\"",
- length, command, ellipsis);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
+ codePtr->localCachePtr = NULL;
}
/*
@@ -1779,50 +2897,76 @@ LogCompilationInfo(interp, script, command, length)
* Results:
* If create is 0 and the name is non-NULL, then if the variable is
* found, the index of its entry in the procedure's array of local
- * variables is returned; otherwise -1 is returned. If name is NULL,
- * the index of a new temporary variable is returned. Finally, if
- * create is 1 and name is non-NULL, the index of a new entry is
- * returned.
+ * variables is returned; otherwise -1 is returned. If name is NULL, the
+ * index of a new temporary variable is returned. Finally, if create is 1
+ * and name is non-NULL, the index of a new entry is returned.
*
* Side effects:
- * Creates and registers a new local variable if create is 1 and
- * the variable is unknown, or if the name is NULL.
+ * Creates and registers a new local variable if create is 1 and the
+ * variable is unknown, or if the name is NULL.
*
*----------------------------------------------------------------------
*/
int
-TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
- register char *name; /* Points to first character of the name of
- * a scalar or array variable. If NULL, a
+TclFindCompiledLocal(
+ register const char *name, /* Points to first character of the name of a
+ * scalar or array variable. If NULL, a
* temporary var should be created. */
- int nameBytes; /* Number of bytes in the name. */
- int create; /* If 1, allocate a local frame entry for
- * the variable if it is new. */
- int flags; /* Flag bits for the compiled local if
- * created. Only VAR_SCALAR, VAR_ARRAY, and
- * VAR_LINK make sense. */
- register Proc *procPtr; /* Points to structure describing procedure
- * containing the variable reference. */
+ int nameBytes, /* Number of bytes in the name. */
+ int create, /* If 1, allocate a local frame entry for the
+ * variable if it is new. */
+ CompileEnv *envPtr) /* Points to the current compile environment*/
{
register CompiledLocal *localPtr;
int localVar = -1;
register int i;
+ Proc *procPtr;
/*
* If not creating a temporary, does a local variable of the specified
* name already exist?
*/
- if (name != NULL) {
+ procPtr = envPtr->procPtr;
+
+ if (procPtr == NULL) {
+ /*
+ * Compiling a non-body script: give it read access to the LVT in the
+ * current localCache
+ */
+
+ LocalCache *cachePtr = envPtr->iPtr->varFramePtr->localCachePtr;
+ const char *localName;
+ Tcl_Obj **varNamePtr;
+ int len;
+
+ if (!cachePtr || !name) {
+ return -1;
+ }
+
+ varNamePtr = &cachePtr->varName0;
+ for (i=0; i < cachePtr->numVars; varNamePtr++, i++) {
+ if (*varNamePtr) {
+ localName = Tcl_GetStringFromObj(*varNamePtr, &len);
+ if ((len == nameBytes) && !strncmp(name, localName, len)) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ if (name != NULL) {
int localCt = procPtr->numCompiledLocals;
+
localPtr = procPtr->firstLocalPtr;
for (i = 0; i < localCt; i++) {
if (!TclIsVarTemporary(localPtr)) {
char *localName = localPtr->name;
- if ((name[0] == localName[0])
- && (nameBytes == localPtr->nameLength)
- && (strncmp(name, localName, (unsigned) nameBytes) == 0)) {
+
+ if ((nameBytes == localPtr->nameLength) &&
+ (strncmp(name,localName,(unsigned)nameBytes) == 0)) {
return i;
}
}
@@ -1833,12 +2977,10 @@ TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
/*
* Create a new variable if appropriate.
*/
-
+
if (create || (name == NULL)) {
localVar = procPtr->numCompiledLocals;
- localPtr = (CompiledLocal *) ckalloc((unsigned)
- (sizeof(CompiledLocal) - sizeof(localPtr->name)
- + nameBytes+1));
+ localPtr = ckalloc(TclOffset(CompiledLocal, name) + nameBytes + 1);
if (procPtr->firstLocalPtr == NULL) {
procPtr->firstLocalPtr = procPtr->lastLocalPtr = localPtr;
} else {
@@ -1848,7 +2990,7 @@ TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
localPtr->nextPtr = NULL;
localPtr->nameLength = nameBytes;
localPtr->frameIndex = localVar;
- localPtr->flags = flags;
+ localPtr->flags = 0;
if (name == NULL) {
localPtr->flags |= VAR_TEMPORARY;
}
@@ -1856,8 +2998,7 @@ TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
localPtr->resolveInfo = NULL;
if (name != NULL) {
- memcpy((VOID *) localPtr->name, (VOID *) name,
- (size_t) nameBytes);
+ memcpy(localPtr->name, name, (size_t) nameBytes);
}
localPtr->name[nameBytes] = '\0';
procPtr->numCompiledLocals++;
@@ -1868,164 +3009,57 @@ TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
/*
*----------------------------------------------------------------------
*
- * TclInitCompiledLocals --
+ * TclExpandCodeArray --
*
- * This routine is invoked in order to initialize the compiled
- * locals table for a new call frame.
+ * Procedure that uses malloc to allocate more storage for a CompileEnv's
+ * code array.
*
* Results:
* None.
*
* Side effects:
- * May invoke various name resolvers in order to determine which
- * variables are being referenced at runtime.
+ * The byte code array in *envPtr is reallocated to a new array of double
+ * the size, and if envPtr->mallocedCodeArray is non-zero the old array
+ * is freed. Byte codes are copied from the old array to the new one.
*
*----------------------------------------------------------------------
*/
void
-TclInitCompiledLocals(interp, framePtr, nsPtr)
- Tcl_Interp *interp; /* Current interpreter. */
- CallFrame *framePtr; /* Call frame to initialize. */
- Namespace *nsPtr; /* Pointer to current namespace. */
+TclExpandCodeArray(
+ void *envArgPtr) /* Points to the CompileEnv whose code array
+ * must be enlarged. */
{
- register CompiledLocal *localPtr;
- Interp *iPtr = (Interp*) interp;
- Tcl_ResolvedVarInfo *vinfo, *resVarInfo;
- Var *varPtr = framePtr->compiledLocals;
- Var *resolvedVarPtr;
- ResolverScheme *resPtr;
- int result;
+ CompileEnv *envPtr = envArgPtr;
+ /* The CompileEnv containing the code array to
+ * be doubled in size. */
/*
- * Initialize the array of local variables stored in the call frame.
- * Some variables may have special resolution rules. In that case,
- * we call their "resolver" procs to get our hands on the variable,
- * and we make the compiled local a link to the real variable.
+ * envPtr->codeNext is equal to envPtr->codeEnd. The currently defined
+ * code bytes are stored between envPtr->codeStart and envPtr->codeNext-1
+ * [inclusive].
*/
- for (localPtr = framePtr->procPtr->firstLocalPtr;
- localPtr != NULL;
- localPtr = localPtr->nextPtr) {
-
- /*
- * Check to see if this local is affected by namespace or
- * interp resolvers. The resolver to use is cached for the
- * next invocation of the procedure.
- */
-
- if (!(localPtr->flags & (VAR_ARGUMENT|VAR_TEMPORARY|VAR_RESOLVED))
- && (nsPtr->compiledVarResProc || iPtr->resolverPtr)) {
- resPtr = iPtr->resolverPtr;
-
- if (nsPtr->compiledVarResProc) {
- result = (*nsPtr->compiledVarResProc)(nsPtr->interp,
- localPtr->name, localPtr->nameLength,
- (Tcl_Namespace *) nsPtr, &vinfo);
- } else {
- result = TCL_CONTINUE;
- }
-
- while ((result == TCL_CONTINUE) && resPtr) {
- if (resPtr->compiledVarResProc) {
- result = (*resPtr->compiledVarResProc)(nsPtr->interp,
- localPtr->name, localPtr->nameLength,
- (Tcl_Namespace *) nsPtr, &vinfo);
- }
- resPtr = resPtr->nextPtr;
- }
- if (result == TCL_OK) {
- localPtr->resolveInfo = vinfo;
- localPtr->flags |= VAR_RESOLVED;
- }
- }
+ size_t currBytes = envPtr->codeNext - envPtr->codeStart;
+ size_t newBytes = 2 * (envPtr->codeEnd - envPtr->codeStart);
+ if (envPtr->mallocedCodeArray) {
+ envPtr->codeStart = ckrealloc(envPtr->codeStart, newBytes);
+ } else {
/*
- * Now invoke the resolvers to determine the exact variables that
- * should be used.
+ * envPtr->codeStart isn't a ckalloc'd pointer, so we must code a
+ * ckrealloc equivalent for ourselves.
*/
- resVarInfo = localPtr->resolveInfo;
- resolvedVarPtr = NULL;
+ unsigned char *newPtr = ckalloc(newBytes);
- if (resVarInfo && resVarInfo->fetchProc) {
- resolvedVarPtr = (Var*) (*resVarInfo->fetchProc)(interp,
- resVarInfo);
- }
-
- if (resolvedVarPtr) {
- varPtr->name = localPtr->name; /* will be just '\0' if temp var */
- varPtr->nsPtr = NULL;
- varPtr->hPtr = NULL;
- varPtr->refCount = 0;
- varPtr->tracePtr = NULL;
- varPtr->searchPtr = NULL;
- varPtr->flags = 0;
- TclSetVarLink(varPtr);
- varPtr->value.linkPtr = resolvedVarPtr;
- resolvedVarPtr->refCount++;
- } else {
- varPtr->value.objPtr = NULL;
- varPtr->name = localPtr->name; /* will be just '\0' if temp var */
- varPtr->nsPtr = NULL;
- varPtr->hPtr = NULL;
- varPtr->refCount = 0;
- varPtr->tracePtr = NULL;
- varPtr->searchPtr = NULL;
- varPtr->flags = (localPtr->flags | VAR_UNDEFINED);
- }
- varPtr++;
+ memcpy(newPtr, envPtr->codeStart, currBytes);
+ envPtr->codeStart = newPtr;
+ envPtr->mallocedCodeArray = 1;
}
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclExpandCodeArray --
- *
- * Procedure that uses malloc to allocate more storage for a
- * CompileEnv's code array.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The byte code array in *envPtr is reallocated to a new array of
- * double the size, and if envPtr->mallocedCodeArray is non-zero the
- * old array is freed. Byte codes are copied from the old array to the
- * new one.
- *
- *----------------------------------------------------------------------
- */
-void
-TclExpandCodeArray(envPtr)
- CompileEnv *envPtr; /* Points to the CompileEnv whose code array
- * must be enlarged. */
-{
- /*
- * envPtr->codeNext is equal to envPtr->codeEnd. The currently defined
- * code bytes are stored between envPtr->codeStart and
- * (envPtr->codeNext - 1) [inclusive].
- */
-
- size_t currBytes = (envPtr->codeNext - envPtr->codeStart);
- size_t newBytes = 2*(envPtr->codeEnd - envPtr->codeStart);
- unsigned char *newPtr = (unsigned char *) ckalloc((unsigned) newBytes);
-
- /*
- * Copy from old code array to new, free old code array if needed, and
- * mark new code array as malloced.
- */
-
- memcpy((VOID *) newPtr, (VOID *) envPtr->codeStart, currBytes);
- if (envPtr->mallocedCodeArray) {
- ckfree((char *) envPtr->codeStart);
- }
- envPtr->codeStart = newPtr;
- envPtr->codeNext = (newPtr + currBytes);
- envPtr->codeEnd = (newPtr + newBytes);
- envPtr->mallocedCodeArray = 1;
+ envPtr->codeNext = envPtr->codeStart + currBytes;
+ envPtr->codeEnd = envPtr->codeStart + newBytes;
}
/*
@@ -2033,37 +3067,37 @@ TclExpandCodeArray(envPtr)
*
* EnterCmdStartData --
*
- * Registers the starting source and bytecode location of a
- * command. This information is used at runtime to map between
- * instruction pc and source locations.
+ * Registers the starting source and bytecode location of a command. This
+ * information is used at runtime to map between instruction pc and
+ * source locations.
*
* Results:
* None.
*
* Side effects:
* Inserts source and code location information into the compilation
- * environment envPtr for the command at index cmdIndex. The
- * compilation environment's CmdLocation array is grown if necessary.
+ * environment envPtr for the command at index cmdIndex. The compilation
+ * environment's CmdLocation array is grown if necessary.
*
*----------------------------------------------------------------------
*/
static void
-EnterCmdStartData(envPtr, cmdIndex, srcOffset, codeOffset)
- CompileEnv *envPtr; /* Points to the compilation environment
+EnterCmdStartData(
+ CompileEnv *envPtr, /* Points to the compilation environment
* structure in which to enter command
* location information. */
- int cmdIndex; /* Index of the command whose start data
- * is being set. */
- int srcOffset; /* Offset of first char of the command. */
- int codeOffset; /* Offset of first byte of command code. */
+ int cmdIndex, /* Index of the command whose start data is
+ * being set. */
+ int srcOffset, /* Offset of first char of the command. */
+ int codeOffset) /* Offset of first byte of command code. */
{
CmdLocation *cmdLocPtr;
-
+
if ((cmdIndex < 0) || (cmdIndex >= envPtr->numCommands)) {
- panic("EnterCmdStartData: bad command index %d\n", cmdIndex);
+ Tcl_Panic("EnterCmdStartData: bad command index %d", cmdIndex);
}
-
+
if (cmdIndex >= envPtr->cmdMapEnd) {
/*
* Expand the command location array by allocating more storage from
@@ -2072,32 +3106,34 @@ EnterCmdStartData(envPtr, cmdIndex, srcOffset, codeOffset)
*/
size_t currElems = envPtr->cmdMapEnd;
- size_t newElems = 2*currElems;
+ size_t newElems = 2 * currElems;
size_t currBytes = currElems * sizeof(CmdLocation);
- size_t newBytes = newElems * sizeof(CmdLocation);
- CmdLocation *newPtr = (CmdLocation *) ckalloc((unsigned) newBytes);
-
- /*
- * Copy from old command location array to new, free old command
- * location array if needed, and mark new array as malloced.
- */
-
- memcpy((VOID *) newPtr, (VOID *) envPtr->cmdMapPtr, currBytes);
+ size_t newBytes = newElems * sizeof(CmdLocation);
+
if (envPtr->mallocedCmdMap) {
- ckfree((char *) envPtr->cmdMapPtr);
+ envPtr->cmdMapPtr = ckrealloc(envPtr->cmdMapPtr, newBytes);
+ } else {
+ /*
+ * envPtr->cmdMapPtr isn't a ckalloc'd pointer, so we must code a
+ * ckrealloc equivalent for ourselves.
+ */
+
+ CmdLocation *newPtr = ckalloc(newBytes);
+
+ memcpy(newPtr, envPtr->cmdMapPtr, currBytes);
+ envPtr->cmdMapPtr = newPtr;
+ envPtr->mallocedCmdMap = 1;
}
- envPtr->cmdMapPtr = (CmdLocation *) newPtr;
envPtr->cmdMapEnd = newElems;
- envPtr->mallocedCmdMap = 1;
}
if (cmdIndex > 0) {
if (codeOffset < envPtr->cmdMapPtr[cmdIndex-1].codeOffset) {
- panic("EnterCmdStartData: cmd map not sorted by code offset");
+ Tcl_Panic("EnterCmdStartData: cmd map not sorted by code offset");
}
}
- cmdLocPtr = &(envPtr->cmdMapPtr[cmdIndex]);
+ cmdLocPtr = &envPtr->cmdMapPtr[cmdIndex];
cmdLocPtr->codeOffset = codeOffset;
cmdLocPtr->srcOffset = srcOffset;
cmdLocPtr->numSrcBytes = -1;
@@ -2118,41 +3154,121 @@ EnterCmdStartData(envPtr, cmdIndex, srcOffset, codeOffset)
*
* Side effects:
* Inserts source and code length information into the compilation
- * environment envPtr for the command at index cmdIndex. Starting
- * source and bytecode information for the command must already
- * have been registered.
+ * environment envPtr for the command at index cmdIndex. Starting source
+ * and bytecode information for the command must already have been
+ * registered.
*
*----------------------------------------------------------------------
*/
static void
-EnterCmdExtentData(envPtr, cmdIndex, numSrcBytes, numCodeBytes)
- CompileEnv *envPtr; /* Points to the compilation environment
+EnterCmdExtentData(
+ CompileEnv *envPtr, /* Points to the compilation environment
* structure in which to enter command
* location information. */
- int cmdIndex; /* Index of the command whose source and
- * code length data is being set. */
- int numSrcBytes; /* Number of command source chars. */
- int numCodeBytes; /* Offset of last byte of command code. */
+ int cmdIndex, /* Index of the command whose source and code
+ * length data is being set. */
+ int numSrcBytes, /* Number of command source chars. */
+ int numCodeBytes) /* Offset of last byte of command code. */
{
CmdLocation *cmdLocPtr;
if ((cmdIndex < 0) || (cmdIndex >= envPtr->numCommands)) {
- panic("EnterCmdExtentData: bad command index %d\n", cmdIndex);
+ Tcl_Panic("EnterCmdExtentData: bad command index %d", cmdIndex);
}
-
+
if (cmdIndex > envPtr->cmdMapEnd) {
- panic("EnterCmdExtentData: missing start data for command %d\n",
- cmdIndex);
+ Tcl_Panic("EnterCmdExtentData: missing start data for command %d",
+ cmdIndex);
}
- cmdLocPtr = &(envPtr->cmdMapPtr[cmdIndex]);
+ cmdLocPtr = &envPtr->cmdMapPtr[cmdIndex];
cmdLocPtr->numSrcBytes = numSrcBytes;
cmdLocPtr->numCodeBytes = numCodeBytes;
}
/*
*----------------------------------------------------------------------
+ * TIP #280
+ *
+ * EnterCmdWordData --
+ *
+ * Registers the lines for the words of a command. This information is
+ * used at runtime by 'info frame'.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Inserts word location information into the compilation environment
+ * envPtr for the command at index cmdIndex. The compilation
+ * environment's ExtCmdLoc.ECL array is grown if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EnterCmdWordData(
+ ExtCmdLoc *eclPtr, /* Points to the map environment structure in
+ * which to enter command location
+ * information. */
+ int srcOffset, /* Offset of first char of the command. */
+ Tcl_Token *tokenPtr,
+ const char *cmd,
+ int len,
+ int numWords,
+ int line,
+ int *clNext,
+ int **wlines,
+ CompileEnv *envPtr)
+{
+ ECL *ePtr;
+ const char *last;
+ int wordIdx, wordLine, *wwlines, *wordNext;
+
+ if (eclPtr->nuloc >= eclPtr->nloc) {
+ /*
+ * Expand the ECL array by allocating more storage from the heap. The
+ * currently allocated ECL entries are stored from eclPtr->loc[0] up
+ * to eclPtr->loc[eclPtr->nuloc-1] (inclusive).
+ */
+
+ size_t currElems = eclPtr->nloc;
+ size_t newElems = (currElems ? 2*currElems : 1);
+ size_t newBytes = newElems * sizeof(ECL);
+
+ eclPtr->loc = ckrealloc(eclPtr->loc, newBytes);
+ eclPtr->nloc = newElems;
+ }
+
+ ePtr = &eclPtr->loc[eclPtr->nuloc];
+ ePtr->srcOffset = srcOffset;
+ ePtr->line = ckalloc(numWords * sizeof(int));
+ ePtr->next = ckalloc(numWords * sizeof(int *));
+ ePtr->nline = numWords;
+ wwlines = ckalloc(numWords * sizeof(int));
+
+ last = cmd;
+ wordLine = line;
+ wordNext = clNext;
+ for (wordIdx=0 ; wordIdx<numWords;
+ wordIdx++, tokenPtr += tokenPtr->numComponents + 1) {
+ TclAdvanceLines(&wordLine, last, tokenPtr->start);
+ TclAdvanceContinuations(&wordLine, &wordNext,
+ tokenPtr->start - envPtr->source);
+ wwlines[wordIdx] =
+ (TclWordKnownAtCompileTime(tokenPtr, NULL) ? wordLine : -1);
+ ePtr->line[wordIdx] = wordLine;
+ ePtr->next[wordIdx] = wordNext;
+ last = tokenPtr->start;
+ }
+
+ *wlines = wwlines;
+ eclPtr->nuloc ++;
+}
+
+/*
+ *----------------------------------------------------------------------
*
* TclCreateExceptRange --
*
@@ -2163,56 +3279,63 @@ EnterCmdExtentData(envPtr, cmdIndex, numSrcBytes, numCodeBytes)
* Returns the index for the newly created ExceptionRange.
*
* Side effects:
- * If there is not enough room in the CompileEnv's ExceptionRange
- * array, the array in expanded: a new array of double the size is
- * allocated, if envPtr->mallocedExceptArray is non-zero the old
- * array is freed, and ExceptionRange entries are copied from the old
- * array to the new one.
+ * If there is not enough room in the CompileEnv's ExceptionRange array,
+ * the array in expanded: a new array of double the size is allocated, if
+ * envPtr->mallocedExceptArray is non-zero the old array is freed, and
+ * ExceptionRange entries are copied from the old array to the new one.
*
*----------------------------------------------------------------------
*/
int
-TclCreateExceptRange(type, envPtr)
- ExceptionRangeType type; /* The kind of ExceptionRange desired. */
- register CompileEnv *envPtr;/* Points to CompileEnv for which to
- * create a new ExceptionRange structure. */
+TclCreateExceptRange(
+ ExceptionRangeType type, /* The kind of ExceptionRange desired. */
+ register CompileEnv *envPtr)/* Points to CompileEnv for which to create a
+ * new ExceptionRange structure. */
{
register ExceptionRange *rangePtr;
+ register ExceptionAux *auxPtr;
int index = envPtr->exceptArrayNext;
-
+
if (index >= envPtr->exceptArrayEnd) {
- /*
+ /*
* Expand the ExceptionRange array. The currently allocated entries
* are stored between elements 0 and (envPtr->exceptArrayNext - 1)
* [inclusive].
*/
-
+
size_t currBytes =
- envPtr->exceptArrayNext * sizeof(ExceptionRange);
+ envPtr->exceptArrayNext * sizeof(ExceptionRange);
+ size_t currBytes2 = envPtr->exceptArrayNext * sizeof(ExceptionAux);
int newElems = 2*envPtr->exceptArrayEnd;
size_t newBytes = newElems * sizeof(ExceptionRange);
- ExceptionRange *newPtr = (ExceptionRange *)
- ckalloc((unsigned) newBytes);
-
- /*
- * Copy from old ExceptionRange array to new, free old
- * ExceptionRange array if needed, and mark the new ExceptionRange
- * array as malloced.
- */
-
- memcpy((VOID *) newPtr, (VOID *) envPtr->exceptArrayPtr,
- currBytes);
+ size_t newBytes2 = newElems * sizeof(ExceptionAux);
+
if (envPtr->mallocedExceptArray) {
- ckfree((char *) envPtr->exceptArrayPtr);
+ envPtr->exceptArrayPtr =
+ ckrealloc(envPtr->exceptArrayPtr, newBytes);
+ envPtr->exceptAuxArrayPtr =
+ ckrealloc(envPtr->exceptAuxArrayPtr, newBytes2);
+ } else {
+ /*
+ * envPtr->exceptArrayPtr isn't a ckalloc'd pointer, so we must
+ * code a ckrealloc equivalent for ourselves.
+ */
+
+ ExceptionRange *newPtr = ckalloc(newBytes);
+ ExceptionAux *newPtr2 = ckalloc(newBytes2);
+
+ memcpy(newPtr, envPtr->exceptArrayPtr, currBytes);
+ memcpy(newPtr2, envPtr->exceptAuxArrayPtr, currBytes2);
+ envPtr->exceptArrayPtr = newPtr;
+ envPtr->exceptAuxArrayPtr = newPtr2;
+ envPtr->mallocedExceptArray = 1;
}
- envPtr->exceptArrayPtr = (ExceptionRange *) newPtr;
envPtr->exceptArrayEnd = newElems;
- envPtr->mallocedExceptArray = 1;
}
envPtr->exceptArrayNext++;
-
- rangePtr = &(envPtr->exceptArrayPtr[index]);
+
+ rangePtr = &envPtr->exceptArrayPtr[index];
rangePtr->type = type;
rangePtr->nestingLevel = envPtr->exceptDepth;
rangePtr->codeOffset = -1;
@@ -2220,16 +3343,300 @@ TclCreateExceptRange(type, envPtr)
rangePtr->breakOffset = -1;
rangePtr->continueOffset = -1;
rangePtr->catchOffset = -1;
+ auxPtr = &envPtr->exceptAuxArrayPtr[index];
+ auxPtr->supportsContinue = 1;
+ auxPtr->stackDepth = envPtr->currStackDepth;
+ auxPtr->expandTarget = envPtr->expandCount;
+ auxPtr->expandTargetDepth = -1;
+ auxPtr->numBreakTargets = 0;
+ auxPtr->breakTargets = NULL;
+ auxPtr->allocBreakTargets = 0;
+ auxPtr->numContinueTargets = 0;
+ auxPtr->continueTargets = NULL;
+ auxPtr->allocContinueTargets = 0;
return index;
}
/*
+ * ---------------------------------------------------------------------
+ *
+ * TclGetInnermostExceptionRange --
+ *
+ * Returns the innermost exception range that covers the current code
+ * creation point, and (optionally) the stack depth that is expected at
+ * that point. Relies on the fact that the range has a numCodeBytes = -1
+ * when it is being populated and that inner ranges come after outer
+ * ranges.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+ExceptionRange *
+TclGetInnermostExceptionRange(
+ CompileEnv *envPtr,
+ int returnCode,
+ ExceptionAux **auxPtrPtr)
+{
+ int exnIdx = -1, i;
+
+ for (i=0 ; i<envPtr->exceptArrayNext ; i++) {
+ ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[i];
+
+ if (CurrentOffset(envPtr) >= rangePtr->codeOffset &&
+ (rangePtr->numCodeBytes == -1 || CurrentOffset(envPtr) <
+ rangePtr->codeOffset+rangePtr->numCodeBytes) &&
+ (returnCode != TCL_CONTINUE ||
+ envPtr->exceptAuxArrayPtr[i].supportsContinue)) {
+ exnIdx = i;
+ }
+ }
+ if (exnIdx == -1) {
+ return NULL;
+ }
+ if (auxPtrPtr) {
+ *auxPtrPtr = &envPtr->exceptAuxArrayPtr[exnIdx];
+ }
+ return &envPtr->exceptArrayPtr[exnIdx];
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * TclAddLoopBreakFixup, TclAddLoopContinueFixup --
+ *
+ * Adds a place that wants to break/continue to the loop exception range
+ * tracking that will be fixed up once the loop can be finalized. These
+ * functions will generate an INST_JUMP4 that will be fixed up during the
+ * loop finalization.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+void
+TclAddLoopBreakFixup(
+ CompileEnv *envPtr,
+ ExceptionAux *auxPtr)
+{
+ int range = auxPtr - envPtr->exceptAuxArrayPtr;
+
+ if (envPtr->exceptArrayPtr[range].type != LOOP_EXCEPTION_RANGE) {
+ Tcl_Panic("trying to add 'break' fixup to full exception range");
+ }
+
+ if (++auxPtr->numBreakTargets > auxPtr->allocBreakTargets) {
+ auxPtr->allocBreakTargets *= 2;
+ auxPtr->allocBreakTargets += 2;
+ if (auxPtr->breakTargets) {
+ auxPtr->breakTargets = ckrealloc(auxPtr->breakTargets,
+ sizeof(int) * auxPtr->allocBreakTargets);
+ } else {
+ auxPtr->breakTargets =
+ ckalloc(sizeof(int) * auxPtr->allocBreakTargets);
+ }
+ }
+ auxPtr->breakTargets[auxPtr->numBreakTargets - 1] = CurrentOffset(envPtr);
+ TclEmitInstInt4(INST_JUMP4, 0, envPtr);
+}
+
+void
+TclAddLoopContinueFixup(
+ CompileEnv *envPtr,
+ ExceptionAux *auxPtr)
+{
+ int range = auxPtr - envPtr->exceptAuxArrayPtr;
+
+ if (envPtr->exceptArrayPtr[range].type != LOOP_EXCEPTION_RANGE) {
+ Tcl_Panic("trying to add 'continue' fixup to full exception range");
+ }
+
+ if (++auxPtr->numContinueTargets > auxPtr->allocContinueTargets) {
+ auxPtr->allocContinueTargets *= 2;
+ auxPtr->allocContinueTargets += 2;
+ if (auxPtr->continueTargets) {
+ auxPtr->continueTargets = ckrealloc(auxPtr->continueTargets,
+ sizeof(int) * auxPtr->allocContinueTargets);
+ } else {
+ auxPtr->continueTargets =
+ ckalloc(sizeof(int) * auxPtr->allocContinueTargets);
+ }
+ }
+ auxPtr->continueTargets[auxPtr->numContinueTargets - 1] =
+ CurrentOffset(envPtr);
+ TclEmitInstInt4(INST_JUMP4, 0, envPtr);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * TclCleanupStackForBreakContinue --
+ *
+ * Ditch the extra elements from the auxiliary stack and the main stack.
+ * How to do this exactly depends on whether there are any elements on
+ * the auxiliary stack to pop.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+void
+TclCleanupStackForBreakContinue(
+ CompileEnv *envPtr,
+ ExceptionAux *auxPtr)
+{
+ int savedStackDepth = envPtr->currStackDepth;
+ int toPop = envPtr->expandCount - auxPtr->expandTarget;
+
+ if (toPop > 0) {
+ while (toPop --> 0) {
+ TclEmitOpcode(INST_EXPAND_DROP, envPtr);
+ }
+ TclAdjustStackDepth(auxPtr->expandTargetDepth - envPtr->currStackDepth,
+ envPtr);
+ envPtr->currStackDepth = auxPtr->expandTargetDepth;
+ }
+ toPop = envPtr->currStackDepth - auxPtr->stackDepth;
+ while (toPop --> 0) {
+ TclEmitOpcode(INST_POP, envPtr);
+ }
+ envPtr->currStackDepth = savedStackDepth;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * StartExpanding --
+ *
+ * Pushes an INST_EXPAND_START and does some additional housekeeping so
+ * that the [break] and [continue] compilers can use an exception-free
+ * issue to discard it.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+static void
+StartExpanding(
+ CompileEnv *envPtr)
+{
+ int i;
+
+ TclEmitOpcode(INST_EXPAND_START, envPtr);
+
+ /*
+ * Update inner exception ranges with information about the environment
+ * where this expansion started.
+ */
+
+ for (i=0 ; i<envPtr->exceptArrayNext ; i++) {
+ ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[i];
+ ExceptionAux *auxPtr = &envPtr->exceptAuxArrayPtr[i];
+
+ /*
+ * Ignore loops unless they're still being built.
+ */
+
+ if (rangePtr->codeOffset > CurrentOffset(envPtr)) {
+ continue;
+ }
+ if (rangePtr->numCodeBytes != -1) {
+ continue;
+ }
+
+ /*
+ * Adequate condition: further out loops and further in exceptions
+ * don't actually need this information.
+ */
+
+ if (auxPtr->expandTarget == envPtr->expandCount) {
+ auxPtr->expandTargetDepth = envPtr->currStackDepth;
+ }
+ }
+
+ /*
+ * There's now one more expansion being processed on the auxiliary stack.
+ */
+
+ envPtr->expandCount++;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * TclFinalizeLoopExceptionRange --
+ *
+ * Finalizes a loop exception range, binding the registered [break] and
+ * [continue] implementations so that they jump to the correct place.
+ * Note that this must only be called after *all* the exception range
+ * target offsets have been set.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+void
+TclFinalizeLoopExceptionRange(
+ CompileEnv *envPtr,
+ int range)
+{
+ ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[range];
+ ExceptionAux *auxPtr = &envPtr->exceptAuxArrayPtr[range];
+ int i, offset;
+ unsigned char *site;
+
+ if (rangePtr->type != LOOP_EXCEPTION_RANGE) {
+ Tcl_Panic("trying to finalize a loop exception range");
+ }
+
+ /*
+ * Do the jump fixups. Note that these are always issued as INST_JUMP4 so
+ * there is no need to fuss around with updating code offsets.
+ */
+
+ for (i=0 ; i<auxPtr->numBreakTargets ; i++) {
+ site = envPtr->codeStart + auxPtr->breakTargets[i];
+ offset = rangePtr->breakOffset - auxPtr->breakTargets[i];
+ TclUpdateInstInt4AtPc(INST_JUMP4, offset, site);
+ }
+ for (i=0 ; i<auxPtr->numContinueTargets ; i++) {
+ site = envPtr->codeStart + auxPtr->continueTargets[i];
+ if (rangePtr->continueOffset == -1) {
+ int j;
+
+ /*
+ * WTF? Can't bind, so revert to an INST_CONTINUE. Not enough
+ * space to do anything else.
+ */
+
+ *site = INST_CONTINUE;
+ for (j=0 ; j<4 ; j++) {
+ *++site = INST_NOP;
+ }
+ } else {
+ offset = rangePtr->continueOffset - auxPtr->continueTargets[i];
+ TclUpdateInstInt4AtPc(INST_JUMP4, offset, site);
+ }
+ }
+
+ /*
+ * Drop the arrays we were holding the only reference to.
+ */
+
+ if (auxPtr->breakTargets) {
+ ckfree(auxPtr->breakTargets);
+ auxPtr->breakTargets = NULL;
+ auxPtr->numBreakTargets = 0;
+ }
+ if (auxPtr->continueTargets) {
+ ckfree(auxPtr->continueTargets);
+ auxPtr->continueTargets = NULL;
+ auxPtr->numContinueTargets = 0;
+ }
+}
+
+/*
*----------------------------------------------------------------------
*
* TclCreateAuxData --
*
- * Procedure that allocates and initializes a new AuxData structure in
- * a CompileEnv's array of compilation auxiliary data records. These
+ * Procedure that allocates and initializes a new AuxData structure in a
+ * CompileEnv's array of compilation auxiliary data records. These
* AuxData records hold information created during compilation by
* CompileProcs and used by instructions during execution.
*
@@ -2237,57 +3644,60 @@ TclCreateExceptRange(type, envPtr)
* Returns the index for the newly created AuxData structure.
*
* Side effects:
- * If there is not enough room in the CompileEnv's AuxData array,
- * the AuxData array in expanded: a new array of double the size
- * is allocated, if envPtr->mallocedAuxDataArray is non-zero
- * the old array is freed, and AuxData entries are copied from
- * the old array to the new one.
+ * If there is not enough room in the CompileEnv's AuxData array, the
+ * AuxData array in expanded: a new array of double the size is
+ * allocated, if envPtr->mallocedAuxDataArray is non-zero the old array
+ * is freed, and AuxData entries are copied from the old array to the new
+ * one.
*
*----------------------------------------------------------------------
*/
int
-TclCreateAuxData(clientData, typePtr, envPtr)
- ClientData clientData; /* The compilation auxiliary data to store
- * in the new aux data record. */
- AuxDataType *typePtr; /* Pointer to the type to attach to this AuxData */
- register CompileEnv *envPtr;/* Points to the CompileEnv for which a new
+TclCreateAuxData(
+ ClientData clientData, /* The compilation auxiliary data to store in
+ * the new aux data record. */
+ const AuxDataType *typePtr, /* Pointer to the type to attach to this
+ * AuxData */
+ register CompileEnv *envPtr)/* Points to the CompileEnv for which a new
* aux data structure is to be allocated. */
{
int index; /* Index for the new AuxData structure. */
register AuxData *auxDataPtr;
- /* Points to the new AuxData structure */
-
+ /* Points to the new AuxData structure */
+
index = envPtr->auxDataArrayNext;
if (index >= envPtr->auxDataArrayEnd) {
- /*
+ /*
* Expand the AuxData array. The currently allocated entries are
* stored between elements 0 and (envPtr->auxDataArrayNext - 1)
* [inclusive].
*/
-
+
size_t currBytes = envPtr->auxDataArrayNext * sizeof(AuxData);
int newElems = 2*envPtr->auxDataArrayEnd;
size_t newBytes = newElems * sizeof(AuxData);
- AuxData *newPtr = (AuxData *) ckalloc((unsigned) newBytes);
-
- /*
- * Copy from old AuxData array to new, free old AuxData array if
- * needed, and mark the new AuxData array as malloced.
- */
-
- memcpy((VOID *) newPtr, (VOID *) envPtr->auxDataArrayPtr,
- currBytes);
+
if (envPtr->mallocedAuxDataArray) {
- ckfree((char *) envPtr->auxDataArrayPtr);
+ envPtr->auxDataArrayPtr =
+ ckrealloc(envPtr->auxDataArrayPtr, newBytes);
+ } else {
+ /*
+ * envPtr->auxDataArrayPtr isn't a ckalloc'd pointer, so we must
+ * code a ckrealloc equivalent for ourselves.
+ */
+
+ AuxData *newPtr = ckalloc(newBytes);
+
+ memcpy(newPtr, envPtr->auxDataArrayPtr, currBytes);
+ envPtr->auxDataArrayPtr = newPtr;
+ envPtr->mallocedAuxDataArray = 1;
}
- envPtr->auxDataArrayPtr = newPtr;
envPtr->auxDataArrayEnd = newElems;
- envPtr->mallocedAuxDataArray = 1;
}
envPtr->auxDataArrayNext++;
-
- auxDataPtr = &(envPtr->auxDataArrayPtr[index]);
+
+ auxDataPtr = &envPtr->auxDataArrayPtr[index];
auxDataPtr->clientData = clientData;
auxDataPtr->type = typePtr;
return index;
@@ -2298,8 +3708,8 @@ TclCreateAuxData(clientData, typePtr, envPtr)
*
* TclInitJumpFixupArray --
*
- * Initializes a JumpFixupArray structure to hold some number of
- * jump fixup entries.
+ * Initializes a JumpFixupArray structure to hold some number of jump
+ * fixup entries.
*
* Results:
* None.
@@ -2311,14 +3721,14 @@ TclCreateAuxData(clientData, typePtr, envPtr)
*/
void
-TclInitJumpFixupArray(fixupArrayPtr)
- register JumpFixupArray *fixupArrayPtr;
- /* Points to the JumpFixupArray structure
- * to initialize. */
+TclInitJumpFixupArray(
+ register JumpFixupArray *fixupArrayPtr)
+ /* Points to the JumpFixupArray structure to
+ * initialize. */
{
fixupArrayPtr->fixup = fixupArrayPtr->staticFixupSpace;
fixupArrayPtr->next = 0;
- fixupArrayPtr->end = (JUMPFIXUP_INIT_ENTRIES - 1);
+ fixupArrayPtr->end = JUMPFIXUP_INIT_ENTRIES - 1;
fixupArrayPtr->mallocedArray = 0;
}
@@ -2327,8 +3737,8 @@ TclInitJumpFixupArray(fixupArrayPtr)
*
* TclExpandJumpFixupArray --
*
- * Procedure that uses malloc to allocate more storage for a
- * jump fixup array.
+ * Procedure that uses malloc to allocate more storage for a jump fixup
+ * array.
*
* Results:
* None.
@@ -2336,41 +3746,43 @@ TclInitJumpFixupArray(fixupArrayPtr)
* Side effects:
* The jump fixup array in *fixupArrayPtr is reallocated to a new array
* of double the size, and if fixupArrayPtr->mallocedArray is non-zero
- * the old array is freed. Jump fixup structures are copied from the
- * old array to the new one.
+ * the old array is freed. Jump fixup structures are copied from the old
+ * array to the new one.
*
*----------------------------------------------------------------------
*/
void
-TclExpandJumpFixupArray(fixupArrayPtr)
- register JumpFixupArray *fixupArrayPtr;
- /* Points to the JumpFixupArray structure
- * to enlarge. */
+TclExpandJumpFixupArray(
+ register JumpFixupArray *fixupArrayPtr)
+ /* Points to the JumpFixupArray structure to
+ * enlarge. */
{
/*
- * The currently allocated jump fixup entries are stored from fixup[0]
- * up to fixup[fixupArrayPtr->fixupNext] (*not* inclusive). We assume
+ * The currently allocated jump fixup entries are stored from fixup[0] up
+ * to fixup[fixupArrayPtr->fixupNext] (*not* inclusive). We assume
* fixupArrayPtr->fixupNext is equal to fixupArrayPtr->fixupEnd.
*/
size_t currBytes = fixupArrayPtr->next * sizeof(JumpFixup);
int newElems = 2*(fixupArrayPtr->end + 1);
size_t newBytes = newElems * sizeof(JumpFixup);
- JumpFixup *newPtr = (JumpFixup *) ckalloc((unsigned) newBytes);
- /*
- * Copy from the old array to new, free the old array if needed,
- * and mark the new array as malloced.
- */
-
- memcpy((VOID *) newPtr, (VOID *) fixupArrayPtr->fixup, currBytes);
if (fixupArrayPtr->mallocedArray) {
- ckfree((char *) fixupArrayPtr->fixup);
+ fixupArrayPtr->fixup = ckrealloc(fixupArrayPtr->fixup, newBytes);
+ } else {
+ /*
+ * fixupArrayPtr->fixup isn't a ckalloc'd pointer, so we must code a
+ * ckrealloc equivalent for ourselves.
+ */
+
+ JumpFixup *newPtr = ckalloc(newBytes);
+
+ memcpy(newPtr, fixupArrayPtr->fixup, currBytes);
+ fixupArrayPtr->fixup = newPtr;
+ fixupArrayPtr->mallocedArray = 1;
}
- fixupArrayPtr->fixup = (JumpFixup *) newPtr;
fixupArrayPtr->end = newElems;
- fixupArrayPtr->mallocedArray = 1;
}
/*
@@ -2390,13 +3802,13 @@ TclExpandJumpFixupArray(fixupArrayPtr)
*/
void
-TclFreeJumpFixupArray(fixupArrayPtr)
- register JumpFixupArray *fixupArrayPtr;
- /* Points to the JumpFixupArray structure
- * to free. */
+TclFreeJumpFixupArray(
+ register JumpFixupArray *fixupArrayPtr)
+ /* Points to the JumpFixupArray structure to
+ * free. */
{
if (fixupArrayPtr->mallocedArray) {
- ckfree((char *) fixupArrayPtr->fixup);
+ ckfree(fixupArrayPtr->fixup);
}
}
@@ -2408,27 +3820,27 @@ TclFreeJumpFixupArray(fixupArrayPtr)
* Procedure to emit a two-byte forward jump of kind "jumpType". Since
* the jump may later have to be grown to five bytes if the jump target
* is more than, say, 127 bytes away, this procedure also initializes a
- * JumpFixup record with information about the jump.
+ * JumpFixup record with information about the jump.
*
* Results:
* None.
*
* Side effects:
- * The JumpFixup record pointed to by "jumpFixupPtr" is initialized
- * with information needed later if the jump is to be grown. Also,
- * a two byte jump of the designated type is emitted at the current
- * point in the bytecode stream.
+ * The JumpFixup record pointed to by "jumpFixupPtr" is initialized with
+ * information needed later if the jump is to be grown. Also, a two byte
+ * jump of the designated type is emitted at the current point in the
+ * bytecode stream.
*
*----------------------------------------------------------------------
*/
void
-TclEmitForwardJump(envPtr, jumpType, jumpFixupPtr)
- CompileEnv *envPtr; /* Points to the CompileEnv structure that
+TclEmitForwardJump(
+ CompileEnv *envPtr, /* Points to the CompileEnv structure that
* holds the resulting instruction. */
- TclJumpType jumpType; /* Indicates the kind of jump: if true or
+ TclJumpType jumpType, /* Indicates the kind of jump: if true or
* false or unconditional. */
- JumpFixup *jumpFixupPtr; /* Points to the JumpFixup structure to
+ JumpFixup *jumpFixupPtr) /* Points to the JumpFixup structure to
* initialize with information about this
* forward jump. */
{
@@ -2436,15 +3848,15 @@ TclEmitForwardJump(envPtr, jumpType, jumpFixupPtr)
* Initialize the JumpFixup structure:
* - codeOffset is offset of first byte of jump below
* - cmdIndex is index of the command after the current one
- * - exceptIndex is the index of the first ExceptionRange after
- * the current one.
+ * - exceptIndex is the index of the first ExceptionRange after the
+ * current one.
*/
-
+
jumpFixupPtr->jumpType = jumpType;
- jumpFixupPtr->codeOffset = (envPtr->codeNext - envPtr->codeStart);
+ jumpFixupPtr->codeOffset = envPtr->codeNext - envPtr->codeStart;
jumpFixupPtr->cmdIndex = envPtr->numCommands;
jumpFixupPtr->exceptIndex = envPtr->exceptArrayNext;
-
+
switch (jumpType) {
case TCL_UNCONDITIONAL_JUMP:
TclEmitInstInt1(INST_JUMP1, 0, envPtr);
@@ -2463,45 +3875,43 @@ TclEmitForwardJump(envPtr, jumpType, jumpFixupPtr)
*
* TclFixupForwardJump --
*
- * Procedure that updates a previously-emitted forward jump to jump
- * a specified number of bytes, "jumpDist". If necessary, the jump is
- * grown from two to five bytes; this is done if the jump distance is
- * greater than "distThreshold" (normally 127 bytes). The jump is
- * described by a JumpFixup record previously initialized by
- * TclEmitForwardJump.
+ * Procedure that updates a previously-emitted forward jump to jump a
+ * specified number of bytes, "jumpDist". If necessary, the jump is grown
+ * from two to five bytes; this is done if the jump distance is greater
+ * than "distThreshold" (normally 127 bytes). The jump is described by a
+ * JumpFixup record previously initialized by TclEmitForwardJump.
*
* Results:
* 1 if the jump was grown and subsequent instructions had to be moved;
- * otherwise 0. This result is returned to allow callers to update
- * any additional code offsets they may hold.
+ * otherwise 0. This result is returned to allow callers to update any
+ * additional code offsets they may hold.
*
* Side effects:
* The jump may be grown and subsequent instructions moved. If this
* happens, the code offsets for any commands and any ExceptionRange
- * records between the jump and the current code address will be
- * updated to reflect the moved code. Also, the bytecode instruction
- * array in the CompileEnv structure may be grown and reallocated.
+ * records between the jump and the current code address will be updated
+ * to reflect the moved code. Also, the bytecode instruction array in the
+ * CompileEnv structure may be grown and reallocated.
*
*----------------------------------------------------------------------
*/
int
-TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
- CompileEnv *envPtr; /* Points to the CompileEnv structure that
+TclFixupForwardJump(
+ CompileEnv *envPtr, /* Points to the CompileEnv structure that
* holds the resulting instruction. */
- JumpFixup *jumpFixupPtr; /* Points to the JumpFixup structure that
+ JumpFixup *jumpFixupPtr, /* Points to the JumpFixup structure that
* describes the forward jump. */
- int jumpDist; /* Jump distance to set in jump
- * instruction. */
- int distThreshold; /* Maximum distance before the two byte
- * jump is grown to five bytes. */
+ int jumpDist, /* Jump distance to set in jump instr. */
+ int distThreshold) /* Maximum distance before the two byte jump
+ * is grown to five bytes. */
{
unsigned char *jumpPc, *p;
int firstCmd, lastCmd, firstRange, lastRange, k;
- unsigned int numBytes;
-
+ unsigned numBytes;
+
if (jumpDist <= distThreshold) {
- jumpPc = (envPtr->codeStart + jumpFixupPtr->codeOffset);
+ jumpPc = envPtr->codeStart + jumpFixupPtr->codeOffset;
switch (jumpFixupPtr->jumpType) {
case TCL_UNCONDITIONAL_JUMP:
TclUpdateInstInt1AtPc(INST_JUMP1, jumpDist, jumpPc);
@@ -2517,20 +3927,20 @@ TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
}
/*
- * We must grow the jump then move subsequent instructions down.
- * Note that if we expand the space for generated instructions,
- * code addresses might change; be careful about updating any of
- * these addresses held in variables.
+ * We must grow the jump then move subsequent instructions down. Note that
+ * if we expand the space for generated instructions, code addresses might
+ * change; be careful about updating any of these addresses held in
+ * variables.
*/
-
+
if ((envPtr->codeNext + 3) > envPtr->codeEnd) {
- TclExpandCodeArray(envPtr);
- }
- jumpPc = (envPtr->codeStart + jumpFixupPtr->codeOffset);
- for (numBytes = envPtr->codeNext-jumpPc-2, p = jumpPc+2+numBytes-1;
- numBytes > 0; numBytes--, p--) {
- p[3] = p[0];
+ TclExpandCodeArray(envPtr);
}
+ jumpPc = envPtr->codeStart + jumpFixupPtr->codeOffset;
+ numBytes = envPtr->codeNext-jumpPc-2;
+ p = jumpPc+2;
+ memmove(p+3, p, numBytes);
+
envPtr->codeNext += 3;
jumpDist += 3;
switch (jumpFixupPtr->jumpType) {
@@ -2544,26 +3954,26 @@ TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
TclUpdateInstInt4AtPc(INST_JUMP_FALSE4, jumpDist, jumpPc);
break;
}
-
+
/*
- * Adjust the code offsets for any commands and any ExceptionRange
- * records between the jump and the current code address.
+ * Adjust the code offsets for any commands and any ExceptionRange records
+ * between the jump and the current code address.
*/
-
+
firstCmd = jumpFixupPtr->cmdIndex;
- lastCmd = (envPtr->numCommands - 1);
+ lastCmd = envPtr->numCommands - 1;
if (firstCmd < lastCmd) {
for (k = firstCmd; k <= lastCmd; k++) {
- (envPtr->cmdMapPtr[k]).codeOffset += 3;
+ envPtr->cmdMapPtr[k].codeOffset += 3;
}
}
-
+
firstRange = jumpFixupPtr->exceptIndex;
- lastRange = (envPtr->exceptArrayNext - 1);
+ lastRange = envPtr->exceptArrayNext - 1;
for (k = firstRange; k <= lastRange; k++) {
- ExceptionRange *rangePtr = &(envPtr->exceptArrayPtr[k]);
+ ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[k];
+
rangePtr->codeOffset += 3;
-
switch (rangePtr->type) {
case LOOP_EXCEPTION_RANGE:
rangePtr->breakOffset += 3;
@@ -2575,25 +3985,234 @@ TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
rangePtr->catchOffset += 3;
break;
default:
- panic("TclFixupForwardJump: bad ExceptionRange type %d\n",
- rangePtr->type);
+ Tcl_Panic("TclFixupForwardJump: bad ExceptionRange type %d",
+ rangePtr->type);
}
}
+
+ for (k = 0 ; k < envPtr->exceptArrayNext ; k++) {
+ ExceptionAux *auxPtr = &envPtr->exceptAuxArrayPtr[k];
+ int i;
+
+ for (i=0 ; i<auxPtr->numBreakTargets ; i++) {
+ if (jumpFixupPtr->codeOffset < auxPtr->breakTargets[i]) {
+ auxPtr->breakTargets[i] += 3;
+ }
+ }
+ for (i=0 ; i<auxPtr->numContinueTargets ; i++) {
+ if (jumpFixupPtr->codeOffset < auxPtr->continueTargets[i]) {
+ auxPtr->continueTargets[i] += 3;
+ }
+ }
+ }
+
return 1; /* the jump was grown */
}
/*
*----------------------------------------------------------------------
*
+ * TclEmitInvoke --
+ *
+ * Emit one of the invoke-related instructions, wrapping it if necessary
+ * in code that ensures that any break or continue operation passing
+ * through it gets the stack unwinding correct, converting it into an
+ * internal jump if in an appropriate context.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Issues the jump with all correct stack management. May create another
+ * loop exception range; pointers to ExceptionRange and ExceptionAux
+ * structures should not be held across this call.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclEmitInvoke(
+ CompileEnv *envPtr,
+ int opcode,
+ ...)
+{
+ va_list argList;
+ ExceptionRange *rangePtr;
+ ExceptionAux *auxBreakPtr, *auxContinuePtr;
+ int arg1, arg2, wordCount = 0, expandCount = 0;
+ int loopRange = 0, breakRange = 0, continueRange = 0;
+ int cleanup, depth = TclGetStackDepth(envPtr);
+
+ /*
+ * Parse the arguments.
+ */
+
+ va_start(argList, opcode);
+ switch (opcode) {
+ case INST_INVOKE_STK1:
+ wordCount = arg1 = cleanup = va_arg(argList, int);
+ arg2 = 0;
+ break;
+ case INST_INVOKE_STK4:
+ wordCount = arg1 = cleanup = va_arg(argList, int);
+ arg2 = 0;
+ break;
+ case INST_INVOKE_REPLACE:
+ arg1 = va_arg(argList, int);
+ arg2 = va_arg(argList, int);
+ wordCount = arg1 + arg2 - 1;
+ cleanup = arg1 + 1;
+ break;
+ default:
+ Tcl_Panic("unexpected opcode");
+ case INST_EVAL_STK:
+ wordCount = cleanup = 1;
+ arg1 = arg2 = 0;
+ break;
+ case INST_RETURN_STK:
+ wordCount = cleanup = 2;
+ arg1 = arg2 = 0;
+ break;
+ case INST_INVOKE_EXPANDED:
+ wordCount = arg1 = cleanup = va_arg(argList, int);
+ arg2 = 0;
+ expandCount = 1;
+ break;
+ }
+ va_end(argList);
+
+ /*
+ * Determine if we need to handle break and continue exceptions with a
+ * special handling exception range (so that we can correctly unwind the
+ * stack).
+ *
+ * These must be done separately; they can be different (especially for
+ * calls from inside a [for] increment clause).
+ */
+
+ rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_BREAK, &auxBreakPtr);
+ if (rangePtr == NULL || rangePtr->type != LOOP_EXCEPTION_RANGE) {
+ auxBreakPtr = NULL;
+ } else if (auxBreakPtr->stackDepth == envPtr->currStackDepth-wordCount
+ && auxBreakPtr->expandTarget == envPtr->expandCount-expandCount) {
+ auxBreakPtr = NULL;
+ } else {
+ breakRange = auxBreakPtr - envPtr->exceptAuxArrayPtr;
+ }
+
+ rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_CONTINUE,
+ &auxContinuePtr);
+ if (rangePtr == NULL || rangePtr->type != LOOP_EXCEPTION_RANGE) {
+ auxContinuePtr = NULL;
+ } else if (auxContinuePtr->stackDepth == envPtr->currStackDepth-wordCount
+ && auxContinuePtr->expandTarget == envPtr->expandCount-expandCount) {
+ auxContinuePtr = NULL;
+ } else {
+ continueRange = auxBreakPtr - envPtr->exceptAuxArrayPtr;
+ }
+
+ if (auxBreakPtr != NULL || auxContinuePtr != NULL) {
+ loopRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
+ ExceptionRangeStarts(envPtr, loopRange);
+ }
+
+ /*
+ * Issue the invoke itself.
+ */
+
+ switch (opcode) {
+ case INST_INVOKE_STK1:
+ TclEmitInstInt1(INST_INVOKE_STK1, arg1, envPtr);
+ break;
+ case INST_INVOKE_STK4:
+ TclEmitInstInt4(INST_INVOKE_STK4, arg1, envPtr);
+ break;
+ case INST_INVOKE_EXPANDED:
+ TclEmitOpcode(INST_INVOKE_EXPANDED, envPtr);
+ envPtr->expandCount--;
+ TclAdjustStackDepth(1 - arg1, envPtr);
+ break;
+ case INST_EVAL_STK:
+ TclEmitOpcode(INST_EVAL_STK, envPtr);
+ break;
+ case INST_RETURN_STK:
+ TclEmitOpcode(INST_RETURN_STK, envPtr);
+ break;
+ case INST_INVOKE_REPLACE:
+ TclEmitInstInt4(INST_INVOKE_REPLACE, arg1, envPtr);
+ TclEmitInt1(arg2, envPtr);
+ TclAdjustStackDepth(-1, envPtr); /* Correction to stack depth calcs */
+ break;
+ }
+
+ /*
+ * If we're generating a special wrapper exception range, we need to
+ * finish that up now.
+ */
+
+ if (auxBreakPtr != NULL || auxContinuePtr != NULL) {
+ int savedStackDepth = envPtr->currStackDepth;
+ int savedExpandCount = envPtr->expandCount;
+ JumpFixup nonTrapFixup;
+
+ if (auxBreakPtr != NULL) {
+ auxBreakPtr = envPtr->exceptAuxArrayPtr + breakRange;
+ }
+ if (auxContinuePtr != NULL) {
+ auxContinuePtr = envPtr->exceptAuxArrayPtr + continueRange;
+ }
+
+ ExceptionRangeEnds(envPtr, loopRange);
+ TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &nonTrapFixup);
+
+ /*
+ * Careful! When generating these stack unwinding sequences, the depth
+ * of stack in the cases where they are taken is not the same as if
+ * the exception is not taken.
+ */
+
+ if (auxBreakPtr != NULL) {
+ TclAdjustStackDepth(-1, envPtr);
+
+ ExceptionRangeTarget(envPtr, loopRange, breakOffset);
+ TclCleanupStackForBreakContinue(envPtr, auxBreakPtr);
+ TclAddLoopBreakFixup(envPtr, auxBreakPtr);
+ TclAdjustStackDepth(1, envPtr);
+
+ envPtr->currStackDepth = savedStackDepth;
+ envPtr->expandCount = savedExpandCount;
+ }
+
+ if (auxContinuePtr != NULL) {
+ TclAdjustStackDepth(-1, envPtr);
+
+ ExceptionRangeTarget(envPtr, loopRange, continueOffset);
+ TclCleanupStackForBreakContinue(envPtr, auxContinuePtr);
+ TclAddLoopContinueFixup(envPtr, auxContinuePtr);
+ TclAdjustStackDepth(1, envPtr);
+
+ envPtr->currStackDepth = savedStackDepth;
+ envPtr->expandCount = savedExpandCount;
+ }
+
+ TclFinalizeLoopExceptionRange(envPtr, loopRange);
+ TclFixupForwardJumpToHere(envPtr, &nonTrapFixup, 127);
+ }
+ TclCheckStackDepth(depth+1-cleanup, envPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclGetInstructionTable --
*
- * Returns a pointer to the table describing Tcl bytecode instructions.
- * This procedure is defined so that clients can access the pointer from
- * outside the TCL DLLs.
+ * Returns a pointer to the table describing Tcl bytecode instructions.
+ * This procedure is defined so that clients can access the pointer from
+ * outside the TCL DLLs.
*
* Results:
* Returns a pointer to the global instruction table, same as the
- * expression (&instructionTable[0]).
+ * expression (&tclInstructionTable[0]).
*
* Side effects:
* None.
@@ -2601,43 +4220,43 @@ TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
*----------------------------------------------------------------------
*/
-InstructionDesc *
-TclGetInstructionTable()
+const void * /* == InstructionDesc* == */
+TclGetInstructionTable(void)
{
- return &instructionTable[0];
+ return &tclInstructionTable[0];
}
/*
*--------------------------------------------------------------
*
- * TclRegisterAuxDataType --
+ * RegisterAuxDataType --
*
- * This procedure is called to register a new AuxData type
- * in the table of all AuxData types supported by Tcl.
+ * This procedure is called to register a new AuxData type in the table
+ * of all AuxData types supported by Tcl.
*
* Results:
* None.
*
* Side effects:
* The type is registered in the AuxData type table. If there was already
- * a type with the same name as in typePtr, it is replaced with the
- * new type.
+ * a type with the same name as in typePtr, it is replaced with the new
+ * type.
*
*--------------------------------------------------------------
*/
-void
-TclRegisterAuxDataType(typePtr)
- AuxDataType *typePtr; /* Information about object type;
- * storage must be statically
- * allocated (must live forever). */
+static void
+RegisterAuxDataType(
+ const AuxDataType *typePtr) /* Information about object type; storage must
+ * be statically allocated (must live forever;
+ * will not be deallocated). */
{
register Tcl_HashEntry *hPtr;
- int new;
+ int isNew;
Tcl_MutexLock(&tableMutex);
if (!auxDataTypeTableInitialized) {
- TclInitAuxDataTypeTable();
+ TclInitAuxDataTypeTable();
}
/*
@@ -2645,17 +4264,17 @@ TclRegisterAuxDataType(typePtr)
*/
hPtr = Tcl_FindHashEntry(&auxDataTypeTable, typePtr->name);
- if (hPtr != (Tcl_HashEntry *) NULL) {
- Tcl_DeleteHashEntry(hPtr);
+ if (hPtr != NULL) {
+ Tcl_DeleteHashEntry(hPtr);
}
/*
* Now insert the new object type.
*/
- hPtr = Tcl_CreateHashEntry(&auxDataTypeTable, typePtr->name, &new);
- if (new) {
- Tcl_SetHashValue(hPtr, typePtr);
+ hPtr = Tcl_CreateHashEntry(&auxDataTypeTable, typePtr->name, &isNew);
+ if (isNew) {
+ Tcl_SetHashValue(hPtr, typePtr);
}
Tcl_MutexUnlock(&tableMutex);
}
@@ -2677,21 +4296,21 @@ TclRegisterAuxDataType(typePtr)
*----------------------------------------------------------------------
*/
-AuxDataType *
-TclGetAuxDataType(typeName)
- char *typeName; /* Name of AuxData type to look up. */
+const AuxDataType *
+TclGetAuxDataType(
+ const char *typeName) /* Name of AuxData type to look up. */
{
register Tcl_HashEntry *hPtr;
- AuxDataType *typePtr = NULL;
+ const AuxDataType *typePtr = NULL;
Tcl_MutexLock(&tableMutex);
if (!auxDataTypeTableInitialized) {
- TclInitAuxDataTypeTable();
+ TclInitAuxDataTypeTable();
}
hPtr = Tcl_FindHashEntry(&auxDataTypeTable, typeName);
- if (hPtr != (Tcl_HashEntry *) NULL) {
- typePtr = (AuxDataType *) Tcl_GetHashValue(hPtr);
+ if (hPtr != NULL) {
+ typePtr = Tcl_GetHashValue(hPtr);
}
Tcl_MutexUnlock(&tableMutex);
@@ -2703,8 +4322,8 @@ TclGetAuxDataType(typeName)
*
* TclInitAuxDataTypeTable --
*
- * This procedure is invoked to perform once-only initialization of
- * the AuxData type table. It also registers the AuxData types defined in
+ * This procedure is invoked to perform once-only initialization of the
+ * AuxData type table. It also registers the AuxData types defined in
* this file.
*
* Results:
@@ -2718,7 +4337,7 @@ TclGetAuxDataType(typeName)
*/
void
-TclInitAuxDataTypeTable()
+TclInitAuxDataTypeTable(void)
{
/*
* The table mutex must already be held before this routine is invoked.
@@ -2728,10 +4347,12 @@ TclInitAuxDataTypeTable()
Tcl_InitHashTable(&auxDataTypeTable, TCL_STRING_KEYS);
/*
- * There is only one AuxData type at this time, so register it here.
+ * There are only three AuxData types at this time, so register them here.
*/
- TclRegisterAuxDataType(&tclForeachInfoType);
+ RegisterAuxDataType(&tclForeachInfoType);
+ RegisterAuxDataType(&tclJumptableInfoType);
+ RegisterAuxDataType(&tclDictUpdateInfoType);
}
/*
@@ -2739,10 +4360,10 @@ TclInitAuxDataTypeTable()
*
* TclFinalizeAuxDataTypeTable --
*
- * This procedure is called by Tcl_Finalize after all exit handlers
- * have been run to free up storage associated with the table of AuxData
- * types. This procedure is called by TclFinalizeExecution() which
- * is called by Tcl_Finalize().
+ * This procedure is called by Tcl_Finalize after all exit handlers have
+ * been run to free up storage associated with the table of AuxData
+ * types. This procedure is called by TclFinalizeExecution() which is
+ * called by Tcl_Finalize().
*
* Results:
* None.
@@ -2754,12 +4375,12 @@ TclInitAuxDataTypeTable()
*/
void
-TclFinalizeAuxDataTypeTable()
+TclFinalizeAuxDataTypeTable(void)
{
Tcl_MutexLock(&tableMutex);
if (auxDataTypeTableInitialized) {
- Tcl_DeleteHashTable(&auxDataTypeTable);
- auxDataTypeTableInitialized = 0;
+ Tcl_DeleteHashTable(&auxDataTypeTable);
+ auxDataTypeTableInitialized = 0;
}
Tcl_MutexUnlock(&tableMutex);
}
@@ -2782,57 +4403,57 @@ TclFinalizeAuxDataTypeTable()
*/
static int
-GetCmdLocEncodingSize(envPtr)
- CompileEnv *envPtr; /* Points to compilation environment
- * structure containing the CmdLocation
- * structure to encode. */
+GetCmdLocEncodingSize(
+ CompileEnv *envPtr) /* Points to compilation environment structure
+ * containing the CmdLocation structure to
+ * encode. */
{
register CmdLocation *mapPtr = envPtr->cmdMapPtr;
int numCmds = envPtr->numCommands;
int codeDelta, codeLen, srcDelta, srcLen;
int codeDeltaNext, codeLengthNext, srcDeltaNext, srcLengthNext;
/* The offsets in their respective byte
- * sequences where the next encoded offset
- * or length should go. */
+ * sequences where the next encoded offset or
+ * length should go. */
int prevCodeOffset, prevSrcOffset, i;
codeDeltaNext = codeLengthNext = srcDeltaNext = srcLengthNext = 0;
prevCodeOffset = prevSrcOffset = 0;
for (i = 0; i < numCmds; i++) {
- codeDelta = (mapPtr[i].codeOffset - prevCodeOffset);
+ codeDelta = mapPtr[i].codeOffset - prevCodeOffset;
if (codeDelta < 0) {
- panic("GetCmdLocEncodingSize: bad code offset");
+ Tcl_Panic("GetCmdLocEncodingSize: bad code offset");
} else if (codeDelta <= 127) {
codeDeltaNext++;
} else {
- codeDeltaNext += 5; /* 1 byte for 0xFF, 4 for positive delta */
+ codeDeltaNext += 5; /* 1 byte for 0xFF, 4 for positive delta */
}
prevCodeOffset = mapPtr[i].codeOffset;
codeLen = mapPtr[i].numCodeBytes;
if (codeLen < 0) {
- panic("GetCmdLocEncodingSize: bad code length");
+ Tcl_Panic("GetCmdLocEncodingSize: bad code length");
} else if (codeLen <= 127) {
codeLengthNext++;
} else {
- codeLengthNext += 5; /* 1 byte for 0xFF, 4 for length */
+ codeLengthNext += 5;/* 1 byte for 0xFF, 4 for length */
}
- srcDelta = (mapPtr[i].srcOffset - prevSrcOffset);
- if ((-127 <= srcDelta) && (srcDelta <= 127)) {
+ srcDelta = mapPtr[i].srcOffset - prevSrcOffset;
+ if ((-127 <= srcDelta) && (srcDelta <= 127) && (srcDelta != -1)) {
srcDeltaNext++;
} else {
- srcDeltaNext += 5; /* 1 byte for 0xFF, 4 for delta */
+ srcDeltaNext += 5; /* 1 byte for 0xFF, 4 for delta */
}
prevSrcOffset = mapPtr[i].srcOffset;
srcLen = mapPtr[i].numSrcBytes;
if (srcLen < 0) {
- panic("GetCmdLocEncodingSize: bad source length");
+ Tcl_Panic("GetCmdLocEncodingSize: bad source length");
} else if (srcLen <= 127) {
srcLengthNext++;
} else {
- srcLengthNext += 5; /* 1 byte for 0xFF, 4 for length */
+ srcLengthNext += 5; /* 1 byte for 0xFF, 4 for length */
}
}
@@ -2844,8 +4465,8 @@ GetCmdLocEncodingSize(envPtr)
*
* EncodeCmdLocMap --
*
- * Encode the command location information for some compiled code into
- * a ByteCode structure. The encoded command location map is stored as
+ * Encode the command location information for some compiled code into a
+ * ByteCode structure. The encoded command location map is stored as
* three adjacent byte sequences.
*
* Results:
@@ -2853,30 +4474,30 @@ GetCmdLocEncodingSize(envPtr)
* information.
*
* Side effects:
- * The encoded information is stored into the block of memory headed
- * by codePtr. Also records pointers to the start of the four byte
- * sequences in fields in codePtr's ByteCode header structure.
+ * The encoded information is stored into the block of memory headed by
+ * codePtr. Also records pointers to the start of the four byte sequences
+ * in fields in codePtr's ByteCode header structure.
*
*----------------------------------------------------------------------
*/
static unsigned char *
-EncodeCmdLocMap(envPtr, codePtr, startPtr)
- CompileEnv *envPtr; /* Points to compilation environment
- * structure containing the CmdLocation
- * structure to encode. */
- ByteCode *codePtr; /* ByteCode in which to encode envPtr's
+EncodeCmdLocMap(
+ CompileEnv *envPtr, /* Points to compilation environment structure
+ * containing the CmdLocation structure to
+ * encode. */
+ ByteCode *codePtr, /* ByteCode in which to encode envPtr's
* command location information. */
- unsigned char *startPtr; /* Points to the first byte in codePtr's
- * memory block where the location
- * information is to be stored. */
+ unsigned char *startPtr) /* Points to the first byte in codePtr's
+ * memory block where the location information
+ * is to be stored. */
{
register CmdLocation *mapPtr = envPtr->cmdMapPtr;
int numCmds = envPtr->numCommands;
register unsigned char *p = startPtr;
int codeDelta, codeLen, srcDelta, srcLen, prevOffset;
register int i;
-
+
/*
* Encode the code offset for each command as a sequence of deltas.
*/
@@ -2884,9 +4505,9 @@ EncodeCmdLocMap(envPtr, codePtr, startPtr)
codePtr->codeDeltaStart = p;
prevOffset = 0;
for (i = 0; i < numCmds; i++) {
- codeDelta = (mapPtr[i].codeOffset - prevOffset);
+ codeDelta = mapPtr[i].codeOffset - prevOffset;
if (codeDelta < 0) {
- panic("EncodeCmdLocMap: bad code offset");
+ Tcl_Panic("EncodeCmdLocMap: bad code offset");
} else if (codeDelta <= 127) {
TclStoreInt1AtPtr(codeDelta, p);
p++;
@@ -2907,7 +4528,7 @@ EncodeCmdLocMap(envPtr, codePtr, startPtr)
for (i = 0; i < numCmds; i++) {
codeLen = mapPtr[i].numCodeBytes;
if (codeLen < 0) {
- panic("EncodeCmdLocMap: bad code length");
+ Tcl_Panic("EncodeCmdLocMap: bad code length");
} else if (codeLen <= 127) {
TclStoreInt1AtPtr(codeLen, p);
p++;
@@ -2926,8 +4547,8 @@ EncodeCmdLocMap(envPtr, codePtr, startPtr)
codePtr->srcDeltaStart = p;
prevOffset = 0;
for (i = 0; i < numCmds; i++) {
- srcDelta = (mapPtr[i].srcOffset - prevOffset);
- if ((-127 <= srcDelta) && (srcDelta <= 127)) {
+ srcDelta = mapPtr[i].srcOffset - prevOffset;
+ if ((-127 <= srcDelta) && (srcDelta <= 127) && (srcDelta != -1)) {
TclStoreInt1AtPtr(srcDelta, p);
p++;
} else {
@@ -2947,7 +4568,7 @@ EncodeCmdLocMap(envPtr, codePtr, startPtr)
for (i = 0; i < numCmds; i++) {
srcLen = mapPtr[i].numSrcBytes;
if (srcLen < 0) {
- panic("EncodeCmdLocMap: bad source length");
+ Tcl_Panic("EncodeCmdLocMap: bad source length");
} else if (srcLen <= 127) {
TclStoreInt1AtPtr(srcLen, p);
p++;
@@ -2958,7 +4579,7 @@ EncodeCmdLocMap(envPtr, codePtr, startPtr)
p += 4;
}
}
-
+
return p;
}
@@ -2968,98 +4589,227 @@ EncodeCmdLocMap(envPtr, codePtr, startPtr)
*
* TclPrintByteCodeObj --
*
- * This procedure prints ("disassembles") the instructions of a
- * bytecode object to stdout.
+ * This procedure prints ("disassembles") the instructions of a bytecode
+ * object to stdout.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclPrintByteCodeObj(
+ Tcl_Interp *interp, /* Used only for Tcl_GetStringFromObj. */
+ Tcl_Obj *objPtr) /* The bytecode object to disassemble. */
+{
+ Tcl_Obj *bufPtr = TclDisassembleByteCodeObj(objPtr);
+
+ fprintf(stdout, "\n%s", TclGetString(bufPtr));
+ Tcl_DecrRefCount(bufPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclPrintInstruction --
+ *
+ * This procedure prints ("disassembles") one instruction from a bytecode
+ * object to stdout.
+ *
+ * Results:
+ * Returns the length in bytes of the current instruiction.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclPrintInstruction(
+ ByteCode *codePtr, /* Bytecode containing the instruction. */
+ const unsigned char *pc) /* Points to first byte of instruction. */
+{
+ Tcl_Obj *bufferObj;
+ int numBytes;
+
+ TclNewObj(bufferObj);
+ numBytes = FormatInstruction(codePtr, pc, bufferObj);
+ fprintf(stdout, "%s", TclGetString(bufferObj));
+ Tcl_DecrRefCount(bufferObj);
+ return numBytes;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclPrintObject --
+ *
+ * This procedure prints up to a specified number of characters from the
+ * argument Tcl object's string representation to a specified file.
*
* Results:
* None.
*
* Side effects:
+ * Outputs characters to the specified file.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclPrintObject(
+ FILE *outFile, /* The file to print the source to. */
+ Tcl_Obj *objPtr, /* Points to the Tcl object whose string
+ * representation should be printed. */
+ int maxChars) /* Maximum number of chars to print. */
+{
+ char *bytes;
+ int length;
+
+ bytes = Tcl_GetStringFromObj(objPtr, &length);
+ TclPrintSource(outFile, bytes, TclMin(length, maxChars));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclPrintSource --
+ *
+ * This procedure prints up to a specified number of characters from the
+ * argument string to a specified file. It tries to produce legible
+ * output by adding backslashes as necessary.
+ *
+ * Results:
* None.
*
+ * Side effects:
+ * Outputs characters to the specified file.
+ *
*----------------------------------------------------------------------
*/
void
-TclPrintByteCodeObj(interp, objPtr)
- Tcl_Interp *interp; /* Used only for Tcl_GetStringFromObj. */
- Tcl_Obj *objPtr; /* The bytecode object to disassemble. */
+TclPrintSource(
+ FILE *outFile, /* The file to print the source to. */
+ const char *stringPtr, /* The string to print. */
+ int maxChars) /* Maximum number of chars to print. */
{
- ByteCode* codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
+ Tcl_Obj *bufferObj;
+
+ TclNewObj(bufferObj);
+ PrintSourceToObj(bufferObj, stringPtr, maxChars);
+ fprintf(outFile, "%s", TclGetString(bufferObj));
+ Tcl_DecrRefCount(bufferObj);
+}
+#endif /* TCL_COMPILE_DEBUG */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclDisassembleByteCodeObj --
+ *
+ * Given an object which is of bytecode type, return a disassembled
+ * version of the bytecode (in a new refcount 0 object). No guarantees
+ * are made about the details of the contents of the result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclDisassembleByteCodeObj(
+ Tcl_Obj *objPtr) /* The bytecode object to disassemble. */
+{
+ ByteCode *codePtr = objPtr->internalRep.twoPtrValue.ptr1;
unsigned char *codeStart, *codeLimit, *pc;
unsigned char *codeDeltaNext, *codeLengthNext;
unsigned char *srcDeltaNext, *srcLengthNext;
int codeOffset, codeLen, srcOffset, srcLen, numCmds, delta, i;
Interp *iPtr = (Interp *) *codePtr->interpHandle;
+ Tcl_Obj *bufferObj;
+ char ptrBuf1[20], ptrBuf2[20];
+ TclNewObj(bufferObj);
if (codePtr->refCount <= 0) {
- return; /* already freed */
+ return bufferObj; /* Already freed. */
}
codeStart = codePtr->codeStart;
- codeLimit = (codeStart + codePtr->numCodeBytes);
+ codeLimit = codeStart + codePtr->numCodeBytes;
numCmds = codePtr->numCommands;
/*
* Print header lines describing the ByteCode.
*/
- fprintf(stdout, "\nByteCode 0x%x, refCt %u, epoch %u, interp 0x%x (epoch %u)\n",
- (unsigned int) codePtr, codePtr->refCount,
- codePtr->compileEpoch, (unsigned int) iPtr,
+ sprintf(ptrBuf1, "%p", codePtr);
+ sprintf(ptrBuf2, "%p", iPtr);
+ Tcl_AppendPrintfToObj(bufferObj,
+ "ByteCode 0x%s, refCt %u, epoch %u, interp 0x%s (epoch %u)\n",
+ ptrBuf1, codePtr->refCount, codePtr->compileEpoch, ptrBuf2,
iPtr->compileEpoch);
- fprintf(stdout, " Source ");
- TclPrintSource(stdout, codePtr->source,
+ Tcl_AppendToObj(bufferObj, " Source ", -1);
+ PrintSourceToObj(bufferObj, codePtr->source,
TclMin(codePtr->numSrcBytes, 55));
- fprintf(stdout, "\n Cmds %d, src %d, inst %d, litObjs %u, aux %d, stkDepth %u, code/src %.2f\n",
+ Tcl_AppendPrintfToObj(bufferObj,
+ "\n Cmds %d, src %d, inst %d, litObjs %u, aux %d, stkDepth %u, code/src %.2f\n",
numCmds, codePtr->numSrcBytes, codePtr->numCodeBytes,
codePtr->numLitObjects, codePtr->numAuxDataItems,
codePtr->maxStackDepth,
#ifdef TCL_COMPILE_STATS
- (codePtr->numSrcBytes?
- ((float)codePtr->structureSize)/((float)codePtr->numSrcBytes) : 0.0));
-#else
- 0.0);
+ codePtr->numSrcBytes?
+ codePtr->structureSize/(float)codePtr->numSrcBytes :
#endif
+ 0.0);
+
#ifdef TCL_COMPILE_STATS
- fprintf(stdout,
- " Code %d = header %d+inst %d+litObj %d+exc %d+aux %d+cmdMap %d\n",
- codePtr->structureSize,
- (sizeof(ByteCode) - (sizeof(size_t) + sizeof(Tcl_Time))),
+ Tcl_AppendPrintfToObj(bufferObj,
+ " Code %lu = header %lu+inst %d+litObj %lu+exc %lu+aux %lu+cmdMap %d\n",
+ (unsigned long) codePtr->structureSize,
+ (unsigned long) (sizeof(ByteCode) - sizeof(size_t) - sizeof(Tcl_Time)),
codePtr->numCodeBytes,
- (codePtr->numLitObjects * sizeof(Tcl_Obj *)),
- (codePtr->numExceptRanges * sizeof(ExceptionRange)),
- (codePtr->numAuxDataItems * sizeof(AuxData)),
+ (unsigned long) (codePtr->numLitObjects * sizeof(Tcl_Obj *)),
+ (unsigned long) (codePtr->numExceptRanges*sizeof(ExceptionRange)),
+ (unsigned long) (codePtr->numAuxDataItems * sizeof(AuxData)),
codePtr->numCmdLocBytes);
#endif /* TCL_COMPILE_STATS */
-
+
/*
* If the ByteCode is the compiled body of a Tcl procedure, print
* information about that procedure. Note that we don't know the
* procedure's name since ByteCode's can be shared among procedures.
*/
-
+
if (codePtr->procPtr != NULL) {
Proc *procPtr = codePtr->procPtr;
int numCompiledLocals = procPtr->numCompiledLocals;
- fprintf(stdout,
- " Proc 0x%x, refCt %d, args %d, compiled locals %d\n",
- (unsigned int) procPtr, procPtr->refCount, procPtr->numArgs,
+
+ sprintf(ptrBuf1, "%p", procPtr);
+ Tcl_AppendPrintfToObj(bufferObj,
+ " Proc 0x%s, refCt %d, args %d, compiled locals %d\n",
+ ptrBuf1, procPtr->refCount, procPtr->numArgs,
numCompiledLocals);
if (numCompiledLocals > 0) {
CompiledLocal *localPtr = procPtr->firstLocalPtr;
+
for (i = 0; i < numCompiledLocals; i++) {
- fprintf(stdout, " slot %d%s%s%s%s%s%s", i,
- ((localPtr->flags & VAR_SCALAR)? ", scalar" : ""),
- ((localPtr->flags & VAR_ARRAY)? ", array" : ""),
- ((localPtr->flags & VAR_LINK)? ", link" : ""),
- ((localPtr->flags & VAR_ARGUMENT)? ", arg" : ""),
- ((localPtr->flags & VAR_TEMPORARY)? ", temp" : ""),
- ((localPtr->flags & VAR_RESOLVED)? ", resolved" : ""));
+ Tcl_AppendPrintfToObj(bufferObj,
+ " slot %d%s%s%s%s%s%s", i,
+ (localPtr->flags & (VAR_ARRAY|VAR_LINK)) ? "" : ", scalar",
+ (localPtr->flags & VAR_ARRAY) ? ", array" : "",
+ (localPtr->flags & VAR_LINK) ? ", link" : "",
+ (localPtr->flags & VAR_ARGUMENT) ? ", arg" : "",
+ (localPtr->flags & VAR_TEMPORARY) ? ", temp" : "",
+ (localPtr->flags & VAR_RESOLVED) ? ", resolved" : "");
if (TclIsVarTemporary(localPtr)) {
- fprintf(stdout, "\n");
+ Tcl_AppendToObj(bufferObj, "\n", -1);
} else {
- fprintf(stdout, ", \"%s\"\n", localPtr->name);
+ Tcl_AppendPrintfToObj(bufferObj, ", \"%s\"\n",
+ localPtr->name);
}
localPtr = localPtr->nextPtr;
}
@@ -3071,58 +4821,60 @@ TclPrintByteCodeObj(interp, objPtr)
*/
if (codePtr->numExceptRanges > 0) {
- fprintf(stdout, " Exception ranges %d, depth %d:\n",
- codePtr->numExceptRanges, codePtr->maxExceptDepth);
+ Tcl_AppendPrintfToObj(bufferObj, " Exception ranges %d, depth %d:\n",
+ codePtr->numExceptRanges, codePtr->maxExceptDepth);
for (i = 0; i < codePtr->numExceptRanges; i++) {
- ExceptionRange *rangePtr = &(codePtr->exceptArrayPtr[i]);
- fprintf(stdout, " %d: level %d, %s, pc %d-%d, ",
+ ExceptionRange *rangePtr = &codePtr->exceptArrayPtr[i];
+
+ Tcl_AppendPrintfToObj(bufferObj,
+ " %d: level %d, %s, pc %d-%d, ",
i, rangePtr->nestingLevel,
- ((rangePtr->type == LOOP_EXCEPTION_RANGE)
- ? "loop" : "catch"),
+ (rangePtr->type==LOOP_EXCEPTION_RANGE ? "loop" : "catch"),
rangePtr->codeOffset,
(rangePtr->codeOffset + rangePtr->numCodeBytes - 1));
switch (rangePtr->type) {
case LOOP_EXCEPTION_RANGE:
- fprintf(stdout, "continue %d, break %d\n",
- rangePtr->continueOffset, rangePtr->breakOffset);
+ Tcl_AppendPrintfToObj(bufferObj, "continue %d, break %d\n",
+ rangePtr->continueOffset, rangePtr->breakOffset);
break;
case CATCH_EXCEPTION_RANGE:
- fprintf(stdout, "catch %d\n", rangePtr->catchOffset);
+ Tcl_AppendPrintfToObj(bufferObj, "catch %d\n",
+ rangePtr->catchOffset);
break;
default:
- panic("TclPrintByteCodeObj: bad ExceptionRange type %d\n",
- rangePtr->type);
+ Tcl_Panic("TclDisassembleByteCodeObj: bad ExceptionRange type %d",
+ rangePtr->type);
}
}
}
-
+
/*
- * If there were no commands (e.g., an expression or an empty string
- * was compiled), just print all instructions and return.
+ * If there were no commands (e.g., an expression or an empty string was
+ * compiled), just print all instructions and return.
*/
if (numCmds == 0) {
pc = codeStart;
while (pc < codeLimit) {
- fprintf(stdout, " ");
- pc += TclPrintInstruction(codePtr, pc);
+ Tcl_AppendToObj(bufferObj, " ", -1);
+ pc += FormatInstruction(codePtr, pc, bufferObj);
}
- return;
+ return bufferObj;
}
-
+
/*
- * Print table showing the code offset, source offset, and source
- * length for each command. These are encoded as a sequence of bytes.
+ * Print table showing the code offset, source offset, and source length
+ * for each command. These are encoded as a sequence of bytes.
*/
- fprintf(stdout, " Commands %d:", numCmds);
+ Tcl_AppendPrintfToObj(bufferObj, " Commands %d:", numCmds);
codeDeltaNext = codePtr->codeDeltaStart;
codeLengthNext = codePtr->codeLengthStart;
- srcDeltaNext = codePtr->srcDeltaStart;
+ srcDeltaNext = codePtr->srcDeltaStart;
srcLengthNext = codePtr->srcLengthStart;
codeOffset = srcOffset = 0;
for (i = 0; i < numCmds; i++) {
- if ((unsigned int) (*codeDeltaNext) == (unsigned int) 0xFF) {
+ if ((unsigned) *codeDeltaNext == (unsigned) 0xFF) {
codeDeltaNext++;
delta = TclGetInt4AtPtr(codeDeltaNext);
codeDeltaNext += 4;
@@ -3132,7 +4884,7 @@ TclPrintByteCodeObj(interp, objPtr)
}
codeOffset += delta;
- if ((unsigned int) (*codeLengthNext) == (unsigned int) 0xFF) {
+ if ((unsigned) *codeLengthNext == (unsigned) 0xFF) {
codeLengthNext++;
codeLen = TclGetInt4AtPtr(codeLengthNext);
codeLengthNext += 4;
@@ -3140,8 +4892,8 @@ TclPrintByteCodeObj(interp, objPtr)
codeLen = TclGetInt1AtPtr(codeLengthNext);
codeLengthNext++;
}
-
- if ((unsigned int) (*srcDeltaNext) == (unsigned int) 0xFF) {
+
+ if ((unsigned) *srcDeltaNext == (unsigned) 0xFF) {
srcDeltaNext++;
delta = TclGetInt4AtPtr(srcDeltaNext);
srcDeltaNext += 4;
@@ -3151,7 +4903,7 @@ TclPrintByteCodeObj(interp, objPtr)
}
srcOffset += delta;
- if ((unsigned int) (*srcLengthNext) == (unsigned int) 0xFF) {
+ if ((unsigned) *srcLengthNext == (unsigned) 0xFF) {
srcLengthNext++;
srcLen = TclGetInt4AtPtr(srcLengthNext);
srcLengthNext += 4;
@@ -3159,29 +4911,29 @@ TclPrintByteCodeObj(interp, objPtr)
srcLen = TclGetInt1AtPtr(srcLengthNext);
srcLengthNext++;
}
-
- fprintf(stdout, "%s%4d: pc %d-%d, src %d-%d",
- ((i % 2)? " " : "\n "),
+
+ Tcl_AppendPrintfToObj(bufferObj, "%s%4d: pc %d-%d, src %d-%d",
+ ((i % 2)? " " : "\n "),
(i+1), codeOffset, (codeOffset + codeLen - 1),
srcOffset, (srcOffset + srcLen - 1));
}
if (numCmds > 0) {
- fprintf(stdout, "\n");
+ Tcl_AppendToObj(bufferObj, "\n", -1);
}
-
+
/*
- * Print each instruction. If the instruction corresponds to the start
- * of a command, print the command's source. Note that we don't need
- * the code length here.
+ * Print each instruction. If the instruction corresponds to the start of
+ * a command, print the command's source. Note that we don't need the code
+ * length here.
*/
codeDeltaNext = codePtr->codeDeltaStart;
- srcDeltaNext = codePtr->srcDeltaStart;
+ srcDeltaNext = codePtr->srcDeltaStart;
srcLengthNext = codePtr->srcLengthStart;
codeOffset = srcOffset = 0;
pc = codeStart;
for (i = 0; i < numCmds; i++) {
- if ((unsigned int) (*codeDeltaNext) == (unsigned int) 0xFF) {
+ if ((unsigned) *codeDeltaNext == (unsigned) 0xFF) {
codeDeltaNext++;
delta = TclGetInt4AtPtr(codeDeltaNext);
codeDeltaNext += 4;
@@ -3191,7 +4943,7 @@ TclPrintByteCodeObj(interp, objPtr)
}
codeOffset += delta;
- if ((unsigned int) (*srcDeltaNext) == (unsigned int) 0xFF) {
+ if ((unsigned) *srcDeltaNext == (unsigned) 0xFF) {
srcDeltaNext++;
delta = TclGetInt4AtPtr(srcDeltaNext);
srcDeltaNext += 4;
@@ -3201,7 +4953,7 @@ TclPrintByteCodeObj(interp, objPtr)
}
srcOffset += delta;
- if ((unsigned int) (*srcLengthNext) == (unsigned int) 0xFF) {
+ if ((unsigned) *srcLengthNext == (unsigned) 0xFF) {
srcLengthNext++;
srcLen = TclGetInt4AtPtr(srcLengthNext);
srcLengthNext += 4;
@@ -3213,16 +4965,16 @@ TclPrintByteCodeObj(interp, objPtr)
/*
* Print instructions before command i.
*/
-
+
while ((pc-codeStart) < codeOffset) {
- fprintf(stdout, " ");
- pc += TclPrintInstruction(codePtr, pc);
+ Tcl_AppendToObj(bufferObj, " ", -1);
+ pc += FormatInstruction(codePtr, pc, bufferObj);
}
- fprintf(stdout, " Command %d: ", (i+1));
- TclPrintSource(stdout, (codePtr->source + srcOffset),
- TclMin(srcLen, 55));
- fprintf(stdout, "\n");
+ Tcl_AppendPrintfToObj(bufferObj, " Command %d: ", i+1);
+ PrintSourceToObj(bufferObj, (codePtr->source + srcOffset),
+ TclMin(srcLen, 55));
+ Tcl_AppendToObj(bufferObj, "\n", -1);
}
if (pc < codeLimit) {
/*
@@ -3230,225 +4982,395 @@ TclPrintByteCodeObj(interp, objPtr)
*/
while (pc < codeLimit) {
- fprintf(stdout, " ");
- pc += TclPrintInstruction(codePtr, pc);
+ Tcl_AppendToObj(bufferObj, " ", -1);
+ pc += FormatInstruction(codePtr, pc, bufferObj);
}
}
+ return bufferObj;
}
-#endif /* TCL_COMPILE_DEBUG */
/*
*----------------------------------------------------------------------
*
- * TclPrintInstruction --
- *
- * This procedure prints ("disassembles") one instruction from a
- * bytecode object to stdout.
+ * FormatInstruction --
*
- * Results:
- * Returns the length in bytes of the current instruiction.
- *
- * Side effects:
- * None.
+ * Appends a representation of a bytecode instruction to a Tcl_Obj.
*
*----------------------------------------------------------------------
*/
-int
-TclPrintInstruction(codePtr, pc)
- ByteCode* codePtr; /* Bytecode containing the instruction. */
- unsigned char *pc; /* Points to first byte of instruction. */
+static int
+FormatInstruction(
+ ByteCode *codePtr, /* Bytecode containing the instruction. */
+ const unsigned char *pc, /* Points to first byte of instruction. */
+ Tcl_Obj *bufferObj) /* Object to append instruction info to. */
{
Proc *procPtr = codePtr->procPtr;
unsigned char opCode = *pc;
- register InstructionDesc *instDesc = &instructionTable[opCode];
+ register const InstructionDesc *instDesc = &tclInstructionTable[opCode];
unsigned char *codeStart = codePtr->codeStart;
- unsigned int pcOffset = (pc - codeStart);
- int opnd, i, j;
-
- fprintf(stdout, "(%u) %s ", pcOffset, instDesc->name);
+ unsigned pcOffset = pc - codeStart;
+ int opnd = 0, i, j, numBytes = 1;
+ int localCt = procPtr ? procPtr->numCompiledLocals : 0;
+ CompiledLocal *localPtr = procPtr ? procPtr->firstLocalPtr : NULL;
+ char suffixBuffer[128]; /* Additional info to print after main opcode
+ * and immediates. */
+ char *suffixSrc = NULL;
+ Tcl_Obj *suffixObj = NULL;
+ AuxData *auxPtr = NULL;
+
+ suffixBuffer[0] = '\0';
+ Tcl_AppendPrintfToObj(bufferObj, "(%u) %s ", pcOffset, instDesc->name);
for (i = 0; i < instDesc->numOperands; i++) {
switch (instDesc->opTypes[i]) {
case OPERAND_INT1:
- opnd = TclGetInt1AtPtr(pc+1+i);
- if ((i == 0) && ((opCode == INST_JUMP1)
- || (opCode == INST_JUMP_TRUE1)
- || (opCode == INST_JUMP_FALSE1))) {
- fprintf(stdout, "%d # pc %u", opnd, (pcOffset + opnd));
- } else {
- fprintf(stdout, "%d", opnd);
+ opnd = TclGetInt1AtPtr(pc+numBytes); numBytes++;
+ if (opCode == INST_JUMP1 || opCode == INST_JUMP_TRUE1
+ || opCode == INST_JUMP_FALSE1) {
+ sprintf(suffixBuffer, "pc %u", pcOffset+opnd);
}
+ Tcl_AppendPrintfToObj(bufferObj, "%+d ", opnd);
break;
case OPERAND_INT4:
- opnd = TclGetInt4AtPtr(pc+1+i);
- if ((i == 0) && ((opCode == INST_JUMP4)
- || (opCode == INST_JUMP_TRUE4)
- || (opCode == INST_JUMP_FALSE4))) {
- fprintf(stdout, "%d # pc %u", opnd, (pcOffset + opnd));
- } else {
- fprintf(stdout, "%d", opnd);
+ opnd = TclGetInt4AtPtr(pc+numBytes); numBytes += 4;
+ if (opCode == INST_JUMP4 || opCode == INST_JUMP_TRUE4
+ || opCode == INST_JUMP_FALSE4) {
+ sprintf(suffixBuffer, "pc %u", pcOffset+opnd);
+ } else if (opCode == INST_START_CMD) {
+ sprintf(suffixBuffer, "next cmd at pc %u", pcOffset+opnd);
}
+ Tcl_AppendPrintfToObj(bufferObj, "%+d ", opnd);
break;
case OPERAND_UINT1:
- opnd = TclGetUInt1AtPtr(pc+1+i);
- if ((i == 0) && (opCode == INST_PUSH1)) {
- fprintf(stdout, "%u # ", (unsigned int) opnd);
- TclPrintObject(stdout, codePtr->objArrayPtr[opnd], 40);
- } else if ((i == 0) && ((opCode == INST_LOAD_SCALAR1)
- || (opCode == INST_LOAD_ARRAY1)
- || (opCode == INST_STORE_SCALAR1)
- || (opCode == INST_STORE_ARRAY1))) {
- int localCt = procPtr->numCompiledLocals;
- CompiledLocal *localPtr = procPtr->firstLocalPtr;
- if (opnd >= localCt) {
- panic("TclPrintInstruction: bad local var index %u (%u locals)\n",
- (unsigned int) opnd, localCt);
- return instDesc->numBytes;
- }
- for (j = 0; j < opnd; j++) {
- localPtr = localPtr->nextPtr;
- }
- if (TclIsVarTemporary(localPtr)) {
- fprintf(stdout, "%u # temp var %u",
- (unsigned int) opnd, (unsigned int) opnd);
- } else {
- fprintf(stdout, "%u # var ", (unsigned int) opnd);
- TclPrintSource(stdout, localPtr->name, 40);
- }
- } else {
- fprintf(stdout, "%u ", (unsigned int) opnd);
+ opnd = TclGetUInt1AtPtr(pc+numBytes); numBytes++;
+ if (opCode == INST_PUSH1) {
+ suffixObj = codePtr->objArrayPtr[opnd];
}
+ Tcl_AppendPrintfToObj(bufferObj, "%u ", (unsigned) opnd);
break;
+ case OPERAND_AUX4:
case OPERAND_UINT4:
- opnd = TclGetUInt4AtPtr(pc+1+i);
+ opnd = TclGetUInt4AtPtr(pc+numBytes); numBytes += 4;
if (opCode == INST_PUSH4) {
- fprintf(stdout, "%u # ", opnd);
- TclPrintObject(stdout, codePtr->objArrayPtr[opnd], 40);
- } else if ((i == 0) && ((opCode == INST_LOAD_SCALAR4)
- || (opCode == INST_LOAD_ARRAY4)
- || (opCode == INST_STORE_SCALAR4)
- || (opCode == INST_STORE_ARRAY4))) {
- int localCt = procPtr->numCompiledLocals;
- CompiledLocal *localPtr = procPtr->firstLocalPtr;
+ suffixObj = codePtr->objArrayPtr[opnd];
+ } else if (opCode == INST_START_CMD && opnd != 1) {
+ sprintf(suffixBuffer+strlen(suffixBuffer),
+ ", %u cmds start here", opnd);
+ }
+ Tcl_AppendPrintfToObj(bufferObj, "%u ", (unsigned) opnd);
+ if (instDesc->opTypes[i] == OPERAND_AUX4) {
+ auxPtr = &codePtr->auxDataArrayPtr[opnd];
+ }
+ break;
+ case OPERAND_IDX4:
+ opnd = TclGetInt4AtPtr(pc+numBytes); numBytes += 4;
+ if (opnd >= -1) {
+ Tcl_AppendPrintfToObj(bufferObj, "%d ", opnd);
+ } else if (opnd == -2) {
+ Tcl_AppendPrintfToObj(bufferObj, "end ");
+ } else {
+ Tcl_AppendPrintfToObj(bufferObj, "end-%d ", -2-opnd);
+ }
+ break;
+ case OPERAND_LVT1:
+ opnd = TclGetUInt1AtPtr(pc+numBytes);
+ numBytes++;
+ goto printLVTindex;
+ case OPERAND_LVT4:
+ opnd = TclGetUInt4AtPtr(pc+numBytes);
+ numBytes += 4;
+ printLVTindex:
+ if (localPtr != NULL) {
if (opnd >= localCt) {
- panic("TclPrintInstruction: bad local var index %u (%u locals)\n",
- (unsigned int) opnd, localCt);
- return instDesc->numBytes;
+ Tcl_Panic("FormatInstruction: bad local var index %u (%u locals)",
+ (unsigned) opnd, localCt);
}
for (j = 0; j < opnd; j++) {
localPtr = localPtr->nextPtr;
}
if (TclIsVarTemporary(localPtr)) {
- fprintf(stdout, "%u # temp var %u",
- (unsigned int) opnd, (unsigned int) opnd);
+ sprintf(suffixBuffer, "temp var %u", (unsigned) opnd);
} else {
- fprintf(stdout, "%u # var ", (unsigned int) opnd);
- TclPrintSource(stdout, localPtr->name, 40);
+ sprintf(suffixBuffer, "var ");
+ suffixSrc = localPtr->name;
}
- } else {
- fprintf(stdout, "%u ", (unsigned int) opnd);
}
+ Tcl_AppendPrintfToObj(bufferObj, "%%v%u ", (unsigned) opnd);
+ break;
+ case OPERAND_SCLS1:
+ opnd = TclGetUInt1AtPtr(pc+numBytes); numBytes++;
+ Tcl_AppendPrintfToObj(bufferObj, "%s ",
+ tclStringClassTable[opnd].name);
break;
case OPERAND_NONE:
default:
break;
}
}
- fprintf(stdout, "\n");
- return instDesc->numBytes;
+ if (suffixObj) {
+ const char *bytes;
+ int length;
+
+ Tcl_AppendToObj(bufferObj, "\t# ", -1);
+ bytes = Tcl_GetStringFromObj(codePtr->objArrayPtr[opnd], &length);
+ PrintSourceToObj(bufferObj, bytes, TclMin(length, 40));
+ } else if (suffixBuffer[0]) {
+ Tcl_AppendPrintfToObj(bufferObj, "\t# %s", suffixBuffer);
+ if (suffixSrc) {
+ PrintSourceToObj(bufferObj, suffixSrc, 40);
+ }
+ }
+ Tcl_AppendToObj(bufferObj, "\n", -1);
+ if (auxPtr && auxPtr->type->printProc) {
+ Tcl_AppendToObj(bufferObj, "\t\t[", -1);
+ auxPtr->type->printProc(auxPtr->clientData, bufferObj, codePtr,
+ pcOffset);
+ Tcl_AppendToObj(bufferObj, "]\n", -1);
+ }
+ return numBytes;
}
/*
*----------------------------------------------------------------------
*
- * TclPrintObject --
+ * TclGetInnerContext --
*
- * This procedure prints up to a specified number of characters from
- * the argument Tcl object's string representation to a specified file.
+ * If possible, returns a list capturing the inner context. Otherwise
+ * return NULL.
*
- * Results:
- * None.
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclGetInnerContext(
+ Tcl_Interp *interp,
+ const unsigned char *pc,
+ Tcl_Obj **tosPtr)
+{
+ int objc = 0, off = 0;
+ Tcl_Obj *result;
+ Interp *iPtr = (Interp *) interp;
+
+ 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 = iPtr->innerContext;
+ if (Tcl_IsShared(result)) {
+ Tcl_DecrRefCount(result);
+ iPtr->innerContext = result = Tcl_NewListObj(objc + 1, NULL);
+ Tcl_IncrRefCount(result);
+ } else {
+ int len;
+
+ /*
+ * Reset while keeping the list intrep as much as possible.
+ */
+
+ Tcl_ListObjLength(interp, result, &len);
+ Tcl_ListObjReplace(interp, result, 0, len, 0, NULL);
+ }
+ Tcl_ListObjAppendElement(NULL, result, TclNewInstNameObj(*pc));
+
+ for (; objc>0 ; objc--) {
+ Tcl_Obj *objPtr;
+
+ objPtr = tosPtr[1 - objc + off];
+ if (!objPtr) {
+ Tcl_Panic("InnerContext: bad tos -- appending null object");
+ }
+ if ((objPtr->refCount<=0)
+#ifdef TCL_MEM_DEBUG
+ || (objPtr->refCount==0x61616161)
+#endif
+ ) {
+ Tcl_Panic("InnerContext: bad tos -- appending freed object %p",
+ objPtr);
+ }
+ Tcl_ListObjAppendElement(NULL, result, objPtr);
+ }
+
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
*
- * Side effects:
- * Outputs characters to the specified file.
+ * TclNewInstNameObj --
+ *
+ * Creates a new InstName Tcl_Obj based on the given instruction
*
*----------------------------------------------------------------------
*/
-void
-TclPrintObject(outFile, objPtr, maxChars)
- FILE *outFile; /* The file to print the source to. */
- Tcl_Obj *objPtr; /* Points to the Tcl object whose string
- * representation should be printed. */
- int maxChars; /* Maximum number of chars to print. */
+Tcl_Obj *
+TclNewInstNameObj(
+ unsigned char inst)
{
- char *bytes;
- int length;
-
- bytes = Tcl_GetStringFromObj(objPtr, &length);
- TclPrintSource(outFile, bytes, TclMin(length, maxChars));
+ Tcl_Obj *objPtr = Tcl_NewObj();
+
+ objPtr->typePtr = &tclInstNameType;
+ objPtr->internalRep.longValue = (long) inst;
+ objPtr->bytes = NULL;
+
+ return objPtr;
}
/*
*----------------------------------------------------------------------
*
- * TclPrintSource --
+ * UpdateStringOfInstName --
*
- * This procedure prints up to a specified number of characters from
- * the argument string to a specified file. It tries to produce legible
- * output by adding backslashes as necessary.
+ * Update the string representation for an instruction name object.
*
- * Results:
- * None.
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateStringOfInstName(
+ Tcl_Obj *objPtr)
+{
+ int inst = objPtr->internalRep.longValue;
+ char *s, buf[20];
+ int len;
+
+ if ((inst < 0) || (inst > LAST_INST_OPCODE)) {
+ sprintf(buf, "inst_%d", inst);
+ s = buf;
+ } else {
+ s = (char *) tclInstructionTable[objPtr->internalRep.longValue].name;
+ }
+ len = strlen(s);
+ objPtr->bytes = ckalloc(len + 1);
+ memcpy(objPtr->bytes, s, len + 1);
+ objPtr->length = len;
+}
+
+/*
+ *----------------------------------------------------------------------
*
- * Side effects:
- * Outputs characters to the specified file.
+ * PrintSourceToObj --
+ *
+ * Appends a quoted representation of a string to a Tcl_Obj.
*
*----------------------------------------------------------------------
*/
-void
-TclPrintSource(outFile, string, maxChars)
- FILE *outFile; /* The file to print the source to. */
- char *string; /* The string to print. */
- int maxChars; /* Maximum number of chars to print. */
+static void
+PrintSourceToObj(
+ Tcl_Obj *appendObj, /* The object to print the source to. */
+ const char *stringPtr, /* The string to print. */
+ int maxChars) /* Maximum number of chars to print. */
{
- register char *p;
- register int i = 0;
+ register const char *p;
+ register int i = 0, len;
- if (string == NULL) {
- fprintf(outFile, "\"\"");
+ if (stringPtr == NULL) {
+ Tcl_AppendToObj(appendObj, "\"\"", -1);
return;
}
- fprintf(outFile, "\"");
- p = string;
- for (; (*p != '\0') && (i < maxChars); p++, i++) {
- switch (*p) {
- case '"':
- fprintf(outFile, "\\\"");
- continue;
- case '\f':
- fprintf(outFile, "\\f");
- continue;
- case '\n':
- fprintf(outFile, "\\n");
- continue;
- case '\r':
- fprintf(outFile, "\\r");
- continue;
- case '\t':
- fprintf(outFile, "\\t");
- continue;
- case '\v':
- fprintf(outFile, "\\v");
- continue;
- default:
- fprintf(outFile, "%c", *p);
- continue;
+ Tcl_AppendToObj(appendObj, "\"", -1);
+ p = stringPtr;
+ for (; (*p != '\0') && (i < maxChars); p+=len) {
+ Tcl_UniChar ch;
+
+ len = TclUtfToUniChar(p, &ch);
+ switch (ch) {
+ case '"':
+ Tcl_AppendToObj(appendObj, "\\\"", -1);
+ i += 2;
+ continue;
+ case '\f':
+ Tcl_AppendToObj(appendObj, "\\f", -1);
+ i += 2;
+ continue;
+ case '\n':
+ Tcl_AppendToObj(appendObj, "\\n", -1);
+ i += 2;
+ continue;
+ case '\r':
+ Tcl_AppendToObj(appendObj, "\\r", -1);
+ i += 2;
+ continue;
+ case '\t':
+ Tcl_AppendToObj(appendObj, "\\t", -1);
+ i += 2;
+ continue;
+ case '\v':
+ Tcl_AppendToObj(appendObj, "\\v", -1);
+ i += 2;
+ continue;
+ default:
+ if (ch < 0x20 || ch >= 0x7f) {
+ Tcl_AppendPrintfToObj(appendObj, "\\u%04x", ch);
+ i += 6;
+ } else {
+ Tcl_AppendPrintfToObj(appendObj, "%c", ch);
+ i++;
+ }
+ continue;
}
}
- fprintf(outFile, "\"");
+ Tcl_AppendToObj(appendObj, "\"", -1);
+ if (*p != '\0') {
+ Tcl_AppendToObj(appendObj, "...", -1);
+ }
}
#ifdef TCL_COMPILE_STATS
@@ -3466,36 +5388,51 @@ TclPrintSource(outFile, string, maxChars)
*
* Side effects:
* Accumulates aggregate code-related statistics in the interpreter's
- * ByteCodeStats structure. Records statistics specific to a ByteCode
- * in its ByteCode structure.
+ * ByteCodeStats structure. Records statistics specific to a ByteCode in
+ * its ByteCode structure.
*
*----------------------------------------------------------------------
*/
void
-RecordByteCodeStats(codePtr)
- ByteCode *codePtr; /* Points to ByteCode structure with info
+RecordByteCodeStats(
+ ByteCode *codePtr) /* Points to ByteCode structure with info
* to add to accumulated statistics. */
{
Interp *iPtr = (Interp *) *codePtr->interpHandle;
- register ByteCodeStats *statsPtr = &(iPtr->stats);
+ register ByteCodeStats *statsPtr;
+
+ if (iPtr == NULL) {
+ /* Avoid segfaulting in case we're called in a deleted interp */
+ return;
+ }
+ statsPtr = &(iPtr->stats);
statsPtr->numCompilations++;
- statsPtr->totalSrcBytes += (double) codePtr->numSrcBytes;
- statsPtr->totalByteCodeBytes += (double) codePtr->structureSize;
- statsPtr->currentSrcBytes += (double) codePtr->numSrcBytes;
+ statsPtr->totalSrcBytes += (double) codePtr->numSrcBytes;
+ statsPtr->totalByteCodeBytes += (double) codePtr->structureSize;
+ statsPtr->currentSrcBytes += (double) codePtr->numSrcBytes;
statsPtr->currentByteCodeBytes += (double) codePtr->structureSize;
-
+
statsPtr->srcCount[TclLog2(codePtr->numSrcBytes)]++;
- statsPtr->byteCodeCount[TclLog2(codePtr->structureSize)]++;
-
- statsPtr->currentInstBytes += (double) codePtr->numCodeBytes;
- statsPtr->currentLitBytes +=
- (double) (codePtr->numLitObjects * sizeof(Tcl_Obj *));
- statsPtr->currentExceptBytes +=
- (double) (codePtr->numExceptRanges * sizeof(ExceptionRange));
- statsPtr->currentAuxBytes +=
- (double) (codePtr->numAuxDataItems * sizeof(AuxData));
+ statsPtr->byteCodeCount[TclLog2((int) codePtr->structureSize)]++;
+
+ statsPtr->currentInstBytes += (double) codePtr->numCodeBytes;
+ statsPtr->currentLitBytes += (double)
+ codePtr->numLitObjects * sizeof(Tcl_Obj *);
+ statsPtr->currentExceptBytes += (double)
+ codePtr->numExceptRanges * sizeof(ExceptionRange);
+ statsPtr->currentAuxBytes += (double)
+ codePtr->numAuxDataItems * sizeof(AuxData);
statsPtr->currentCmdMapBytes += (double) codePtr->numCmdLocBytes;
}
#endif /* TCL_COMPILE_STATS */
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * tab-width: 8
+ * End:
+ */