summaryrefslogtreecommitdiffstats
path: root/generic/tclBasic.c
diff options
context:
space:
mode:
authorandreas_kupries <akupries@shaw.ca>2006-11-28 22:20:27 (GMT)
committerandreas_kupries <akupries@shaw.ca>2006-11-28 22:20:27 (GMT)
commit2cd91050a0972e257b9bc1a320d996030f01ce5d (patch)
treec4542b66e173006f66825f5cfb1617a4fd9766e1 /generic/tclBasic.c
parentde316a45d4f6dcf7815d5c199f65a0e636f20423 (diff)
downloadtcl-2cd91050a0972e257b9bc1a320d996030f01ce5d.zip
tcl-2cd91050a0972e257b9bc1a320d996030f01ce5d.tar.gz
tcl-2cd91050a0972e257b9bc1a320d996030f01ce5d.tar.bz2
* generic/tclBasic.c: TIP #280 implementation.
* generic/tclCmdAH.c: * generic/tclCmdIL.c: * generic/tclCmdMZ.c: * generic/tclCompCmds.c: * generic/tclCompExpr.c: * generic/tclCompile.c: * generic/tclCompile.h: * generic/tclExecute.c: * generic/tclIOUtil.c: * generic/tclInt.h: * generic/tclInterp.c: * generic/tclNamesp.c: * generic/tclObj.c: * generic/tclProc.c: * tests/compile.test: * tests/info.test: * tests/platform.test: * tests/safe.test:
Diffstat (limited to 'generic/tclBasic.c')
-rw-r--r--generic/tclBasic.c487
1 files changed, 463 insertions, 24 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index 127620d..ef01194 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclBasic.c,v 1.220 2006/11/23 15:24:28 dkf Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.221 2006/11/28 22:20:27 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -357,6 +357,17 @@ Tcl_CreateInterp(void)
iPtr->maxNestingDepth = MAX_NESTING_DEPTH;
iPtr->framePtr = NULL; /* initialise as soon as :: is available */
iPtr->varFramePtr = NULL; /* initialise as soon as :: is available */
+
+ /*
+ * TIP #280 - Initialize the arrays used to extend the ByteCode and
+ * Proc structures.
+ */
+ iPtr->cmdFramePtr = NULL;
+ iPtr->linePBodyPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable));
+ iPtr->lineBCPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable));
+ Tcl_InitHashTable(iPtr->linePBodyPtr, TCL_ONE_WORD_KEYS);
+ Tcl_InitHashTable(iPtr->lineBCPtr, TCL_ONE_WORD_KEYS);
+
iPtr->activeVarTracePtr = NULL;
iPtr->returnOpts = NULL;
@@ -1213,6 +1224,60 @@ DeleteInterpProc(
*/
TclDeleteLiteralTable(interp, &(iPtr->literalTable));
+
+ /* TIP #280 - Release the arrays for ByteCode/Proc extension, and contents.
+ */
+ {
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch hSearch;
+ CmdFrame* cfPtr;
+ ExtCmdLoc* eclPtr;
+ int i;
+
+ for (hPtr = Tcl_FirstHashEntry(iPtr->linePBodyPtr, &hSearch);
+ hPtr != NULL;
+ hPtr = Tcl_NextHashEntry(&hSearch)) {
+
+ cfPtr = (CmdFrame*) Tcl_GetHashValue (hPtr);
+
+ if (cfPtr->type == TCL_LOCATION_SOURCE) {
+ Tcl_DecrRefCount (cfPtr->data.eval.path);
+ }
+ ckfree ((char*) cfPtr->line);
+ ckfree ((char*) cfPtr);
+ Tcl_DeleteHashEntry (hPtr);
+
+ }
+ Tcl_DeleteHashTable (iPtr->linePBodyPtr);
+ ckfree ((char*) iPtr->linePBodyPtr);
+ iPtr->linePBodyPtr = NULL;
+
+ /* See also tclCompile.c, TclCleanupByteCode */
+
+ for (hPtr = Tcl_FirstHashEntry(iPtr->lineBCPtr, &hSearch);
+ hPtr != NULL;
+ hPtr = Tcl_NextHashEntry(&hSearch)) {
+
+ eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hPtr);
+
+ 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);
+ Tcl_DeleteHashEntry (hPtr);
+ }
+ Tcl_DeleteHashTable (iPtr->lineBCPtr);
+ ckfree((char*) iPtr->lineBCPtr);
+ iPtr->lineBCPtr = NULL;
+ }
ckfree((char *) iPtr);
}
@@ -3731,7 +3796,7 @@ Tcl_EvalTokensStandard(
int count) /* Number of tokens to consider at tokenPtr.
* Must be at least 1. */
{
- return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL);
+ return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL, 1);
}
/*
@@ -3785,7 +3850,7 @@ Tcl_EvalTokens(
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalEx --
+ * Tcl_EvalEx, TclEvalEx --
*
* This function evaluates a Tcl script without using the compiler or
* byte-code interpreter. It just parses the script, creates values for
@@ -3799,6 +3864,7 @@ Tcl_EvalTokens(
* Side effects:
* Depends on the script.
*
+ * TIP #280 : Keep public API, internally extended API.
*----------------------------------------------------------------------
*/
@@ -3814,18 +3880,44 @@ Tcl_EvalEx(
* evaluation of the script. Only
* TCL_EVAL_GLOBAL is currently supported. */
{
+ return TclEvalEx (interp, script, numBytes, flags, 1);
+}
+
+int
+TclEvalEx(interp, script, numBytes, flags, line)
+ Tcl_Interp *interp; /* Interpreter in which to evaluate the
+ * script. Also used for error reporting. */
+ CONST char *script; /* First character of script to evaluate. */
+ int numBytes; /* Number of bytes in script. If < 0, the
+ * script consists of all bytes up to the
+ * first null character. */
+ int flags; /* Collection of OR-ed bits that control
+ * the evaluation of the script. Only
+ * TCL_EVAL_GLOBAL is currently
+ * supported. */
+ int line; /* The line the script starts on. */
+{
Interp *iPtr = (Interp *) interp;
CONST char *p, *next;
Tcl_Parse parse;
#define NUM_STATIC_OBJS 20
Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv, **objvSpace;
- int expandStatic[NUM_STATIC_OBJS], *expand;
+ int expandStatic [NUM_STATIC_OBJS], *expand;
+ int linesStatic [NUM_STATIC_OBJS], *lines, *lineSpace;
Tcl_Token *tokenPtr;
- int i, code, commandLength, bytesLeft, expandRequested;
+ int code = TCL_OK;
+ int i, commandLength, bytesLeft, expandRequested;
CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case
* TCL_EVAL_GLOBAL was set. */
int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
+ /* TIP #280. The array 'expand' has become tri-valued.
+ * 0 = no expansion
+ * 1 = expansion, value is dynamically constructed ($var, [cmd]).
+ * 2 = NEW expansion of a literal value. Here the system determines
+ * the actual line numbers within the literal.
+ */
+
/*
* The variables below keep track of how much state has been allocated
* while evaluating the script, so that it can be freed properly if an
@@ -3834,6 +3926,9 @@ Tcl_EvalEx(
int gotParse = 0, objectsUsed = 0;
+ /* TIP #280 Structures for tracking of command locations. */
+ CmdFrame eeFrame;
+
if (numBytes < 0) {
numBytes = strlen(script);
}
@@ -3849,19 +3944,91 @@ Tcl_EvalEx(
* the script and then executes it.
*/
- objv = objvSpace = staticObjArray;
- expand = expandStatic;
- p = script;
+ objv = objvSpace = staticObjArray;
+ lines = lineSpace = linesStatic;
+ expand = expandStatic;
+ p = script;
bytesLeft = numBytes;
+
+ /* TIP #280 Initialize tracking. Do not push on the frame stack yet.
+ *
+ * We may cont. counting based on a specific context (CTX), or open a new
+ * context, either for a sourced script, or 'eval'. For sourced files we
+ * always have a path object, even if nothing was specified in the interp
+ * itself. That makes code using it simpler as NULL checks can be left
+ * out. Sourced file without path in the 'scriptFile' is possible during
+ * Tcl initialization.
+ */
+
+ if (iPtr->evalFlags & TCL_EVAL_CTX) {
+ /* Path information comes out of the context. */
+
+ eeFrame.type = TCL_LOCATION_SOURCE;
+ eeFrame.data.eval.path = iPtr->invokeCmdFramePtr->data.eval.path;
+ Tcl_IncrRefCount (eeFrame.data.eval.path);
+ } else if (iPtr->evalFlags & TCL_EVAL_FILE) {
+ /* Set up for a sourced file */
+
+ eeFrame.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) {
+ /* Error message in the interp result */
+ return TCL_ERROR;
+ }
+ eeFrame.data.eval.path = norm;
+ Tcl_IncrRefCount (eeFrame.data.eval.path);
+ } else {
+ eeFrame.data.eval.path = Tcl_NewStringObj ("",-1);
+ }
+ } else {
+ /* Set up for plain eval */
+
+ eeFrame.type = TCL_LOCATION_EVAL;
+ eeFrame.data.eval.path = NULL;
+ }
+
+ eeFrame.level = (iPtr->cmdFramePtr == NULL
+ ? 1
+ : iPtr->cmdFramePtr->level + 1);
+ eeFrame.framePtr = iPtr->framePtr;
+ eeFrame.nextPtr = iPtr->cmdFramePtr;
+ eeFrame.nline = 0;
+ eeFrame.line = NULL;
+
iPtr->evalFlags = 0;
do {
if (Tcl_ParseCommand(interp, p, bytesLeft, 0, &parse) != TCL_OK) {
code = TCL_ERROR;
goto error;
}
+
+ /*
+ * TIP #280 Track lines. The parser may have skipped text till it
+ * found the command we are now at. We have to count the lines in this
+ * block.
+ */
+
+ TclAdvanceLines (&line, p, parse.commandStart);
+
gotParse = 1;
if (parse.numWords > 0) {
/*
+ * TIP #280. Track lines within the words of the current
+ * command.
+ */
+
+ int wordLine = line;
+ CONST char* wordStart = parse.commandStart;
+
+ /*
* Generate an array of objects for the words of the command.
*/
@@ -3869,17 +4036,45 @@ Tcl_EvalEx(
if (parse.numWords > NUM_STATIC_OBJS) {
expand = (int *)
- ckalloc((unsigned) (parse.numWords * sizeof(int)));
+ ckalloc((unsigned) (parse.numWords * sizeof(int)));
objvSpace = (Tcl_Obj **)
- ckalloc((unsigned) (parse.numWords*sizeof(Tcl_Obj *)));
+ ckalloc((unsigned) (parse.numWords * sizeof(Tcl_Obj *)));
+ lineSpace = (int*)
+ ckalloc((unsigned) (parse.numWords * sizeof(int)));
}
expandRequested = 0;
- objv = objvSpace;
+ objv = objvSpace;
+ lines = lineSpace;
+
for (objectsUsed = 0, tokenPtr = parse.tokenPtr;
objectsUsed < parse.numWords;
objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) {
+
+ /*
+ * TIP #280. Track lines to current word. Save the
+ * information on a per-word basis, signaling dynamic words as
+ * needed. Make the information available to the recursively
+ * called evaluator as well, including the type of context
+ * (source vs. eval).
+ */
+
+ TclAdvanceLines (&wordLine, wordStart, tokenPtr->start);
+ wordStart = tokenPtr->start;
+
+ lines [objectsUsed] = ((TclWordKnownAtCompileTime (tokenPtr, NULL) ||
+ TclWordSimpleExpansion (tokenPtr))
+ ? wordLine
+ : -1);
+
+ if (eeFrame.type == TCL_LOCATION_SOURCE) {
+ iPtr->evalFlags |= TCL_EVAL_FILE;
+ }
+
code = TclSubstTokens(interp, tokenPtr+1,
- tokenPtr->numComponents, NULL);
+ tokenPtr->numComponents, NULL, wordLine);
+
+ iPtr->evalFlags = 0;
+
if (code != TCL_OK) {
goto error;
}
@@ -3901,31 +4096,67 @@ Tcl_EvalEx(
goto error;
}
expandRequested = 1;
- expand[objectsUsed] = 1;
+ expand[objectsUsed] = (TclWordSimpleExpansion (tokenPtr)
+ ? 2
+ : 1);
+
objectsNeeded += (numElements ? numElements : 1);
} else {
expand[objectsUsed] = 0;
objectsNeeded++;
}
- }
+ } /* for loop */
if (expandRequested) {
/*
* Some word expansion was requested. Check for objv resize.
*/
- Tcl_Obj **copy = objvSpace;
+ Tcl_Obj **copy = objvSpace;
+ int *lcopy = lineSpace;
int wordIdx = parse.numWords;
- int objIdx = objectsNeeded - 1;
+ int objIdx = objectsNeeded - 1;
if ((parse.numWords > NUM_STATIC_OBJS)
|| (objectsNeeded > NUM_STATIC_OBJS)) {
objv = objvSpace = (Tcl_Obj **) ckalloc((unsigned)
(objectsNeeded * sizeof(Tcl_Obj *)));
+ lines = lineSpace = (int*) ckalloc((unsigned)
+ (objectsNeeded * sizeof(int)));
}
objectsUsed = 0;
while (wordIdx--) {
- if (expand[wordIdx]) {
+ if (expand[wordIdx] == 2) {
+ /* TIP #280. The expansion is for a simple literal. Not only
+ * crack the list into its elements, determine the
+ * line numbers within it as well.
+ *
+ * The qualification of 'simple' ensures that the word
+ * does not contain backslash-subst, no way to get
+ * thrown off by embedded \n sequnces.
+ */
+
+ int numElements;
+ Tcl_Obj **elements, *temp = copy[wordIdx];
+ int* eline;
+
+ Tcl_ListObjGetElements(NULL, temp,
+ &numElements, &elements);
+
+ eline = (int*) ckalloc (numElements * sizeof(int));
+ TclListLines (TclGetString(temp),lcopy[wordIdx],
+ numElements, eline);
+
+ objectsUsed += numElements;
+ while (numElements--) {
+ lines[objIdx] = eline [numElements];
+ objv [objIdx--] = elements[numElements];
+ Tcl_IncrRefCount(elements[numElements]);
+ }
+ Tcl_DecrRefCount(temp);
+ ckfree((char*) eline);
+
+ } else if (expand[wordIdx]) {
int numElements;
Tcl_Obj **elements, *temp = copy[wordIdx];
@@ -3933,12 +4164,14 @@ Tcl_EvalEx(
&numElements, &elements);
objectsUsed += numElements;
while (numElements--) {
- objv[objIdx--] = elements[numElements];
+ lines[objIdx] = -1;
+ objv [objIdx--] = elements[numElements];
Tcl_IncrRefCount(elements[numElements]);
}
Tcl_DecrRefCount(temp);
} else {
- objv[objIdx--] = copy[wordIdx];
+ lines[objIdx] = lcopy[wordIdx];
+ objv [objIdx--] = copy [wordIdx];
objectsUsed++;
}
}
@@ -3947,16 +4180,41 @@ Tcl_EvalEx(
if (copy != staticObjArray) {
ckfree((char *) copy);
}
+ if (lcopy != linesStatic) {
+ ckfree((char *) lcopy);
+ }
}
/*
* Execute the command and free the objects for its words.
+ *
+ * TIP #280: Remember the command itself for 'info frame'. We
+ * shorten the visible command by one char to exclude the
+ * termination character, if necessary. Here is where we put our
+ * frame on the stack of frames too. _After_ the nested commands
+ * have been executed.
*/
+ eeFrame.cmd.str.cmd = parse.commandStart;
+ eeFrame.cmd.str.len = parse.commandSize;
+
+ if (parse.term == parse.commandStart + parse.commandSize - 1) {
+ eeFrame.cmd.str.len --;
+ }
+
+ eeFrame.nline = objectsUsed;
+ eeFrame.line = lines;
+
+ iPtr->cmdFramePtr = &eeFrame;
iPtr->numLevels++;
code = TclEvalObjvInternal(interp, objectsUsed, objv,
parse.commandStart, parse.commandSize, 0);
iPtr->numLevels--;
+ iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;
+
+ eeFrame.line = NULL;
+ eeFrame.nline = 0;
+
if (code != TCL_OK) {
goto error;
}
@@ -3967,6 +4225,8 @@ Tcl_EvalEx(
if (objvSpace != staticObjArray) {
ckfree((char *) objvSpace);
objvSpace = staticObjArray;
+ ckfree ((char*) lineSpace);
+ lineSpace = linesStatic;
}
/*
@@ -3982,16 +4242,21 @@ Tcl_EvalEx(
/*
* Advance to the next command in the script.
+ *
+ * TIP #280 Track Lines. Now we track how many lines were in the
+ * executed command.
*/
next = parse.commandStart + parse.commandSize;
bytesLeft -= next - p;
p = next;
+ TclAdvanceLines (&line, parse.commandStart, p);
Tcl_FreeParse(&parse);
gotParse = 0;
} while (bytesLeft > 0);
iPtr->varFramePtr = savedVarFramePtr;
- return TCL_OK;
+ code = TCL_OK;
+ goto cleanup_return;
error:
/*
@@ -4034,17 +4299,59 @@ Tcl_EvalEx(
}
if (objvSpace != staticObjArray) {
ckfree((char *) objvSpace);
+ ckfree ((char*) lineSpace);
}
if (expand != expandStatic) {
ckfree((char *) expand);
}
iPtr->varFramePtr = savedVarFramePtr;
+ cleanup_return:
+ /* TIP #280. Release the local CmdFrame, and its contents. */
+
+ if (eeFrame.line != NULL) {
+ ckfree ((char*) eeFrame.line);
+ }
+ if (eeFrame.type == TCL_LOCATION_SOURCE) {
+ Tcl_DecrRefCount (eeFrame.data.eval.path);
+ }
return code;
}
/*
*----------------------------------------------------------------------
*
+ * TclAdvanceLines --
+ *
+ * This procedure is a helper which counts the number of lines
+ * in a block of text and advances an external counter.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The specified counter is advanced per the number of lines found.
+ *
+ * TIP #280
+ *----------------------------------------------------------------------
+ */
+
+void
+TclAdvanceLines (line,start,end)
+ int* line;
+ CONST char* start;
+ CONST char* end;
+{
+ CONST char* p;
+ for (p = start; p < end; p++) {
+ if (*p == '\n') {
+ (*line) ++;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_Eval --
*
* Execute a Tcl command in a string. This function executes the script
@@ -4120,7 +4427,7 @@ Tcl_GlobalEvalObj(
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalObjEx --
+ * Tcl_EvalObjEx, TclEvalObjEx --
*
* Execute Tcl commands stored in a Tcl object. These commands are
* compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT is
@@ -4136,6 +4443,7 @@ Tcl_GlobalEvalObj(
* the bytecode instructions for the commands. Executing the commands
* will almost certainly have side effects that depend on those commands.
*
+ * TIP #280 : Keep public API, internally extended API.
*----------------------------------------------------------------------
*/
@@ -4149,6 +4457,24 @@ Tcl_EvalObjEx(
* evaluation of the script. Supported values
* are TCL_EVAL_GLOBAL and TCL_EVAL_DIRECT. */
{
+ return TclEvalObjEx (interp, objPtr, flags, NULL, 0);
+}
+
+int
+TclEvalObjEx(interp, objPtr, flags, invoker, word)
+ Tcl_Interp *interp; /* Token for command interpreter
+ * (returned by a previous call to
+ * Tcl_CreateInterp). */
+ register Tcl_Obj *objPtr; /* Pointer to object containing
+ * commands to execute. */
+ int flags; /* Collection of OR-ed bits that
+ * control the evaluation of the
+ * script. Supported values are
+ * TCL_EVAL_GLOBAL and
+ * TCL_EVAL_DIRECT. */
+ CONST CmdFrame* invoker; /* Frame of the command doing the eval */
+ int word; /* Index of the word which is in objPtr */
+{
register Interp *iPtr = (Interp *) interp;
char *script;
int numSrcBytes;
@@ -4185,17 +4511,53 @@ Tcl_EvalObjEx(
if (objPtr->bytes == NULL || /* ...without a string rep */
listRepPtr->canonicalFlag) {/* ...or that is canonical */
+ /* TIP #280 Structures for tracking lines.
+ * As we know that this is dynamic execution we ignore the
+ * invoker, even if known.
+ */
+ int line, i;
+ char* w;
+ CmdFrame eoFrame;
+ Tcl_Obj **elements = &listRepPtr->elements;
+
+ eoFrame.type = TCL_LOCATION_EVAL_LIST;
+ eoFrame.level = (iPtr->cmdFramePtr == NULL ?
+ 1 :
+ iPtr->cmdFramePtr->level + 1);
+ eoFrame.framePtr = iPtr->framePtr;
+ eoFrame.nextPtr = iPtr->cmdFramePtr;
+ eoFrame.nline = listRepPtr->elemCount;
+ eoFrame.line = (int*) ckalloc (eoFrame.nline * sizeof (int));
+
+ eoFrame.cmd.listPtr = objPtr;
+ Tcl_IncrRefCount (eoFrame.cmd.listPtr);
+ eoFrame.data.eval.path = NULL;
+
/*
* Increase the reference count of the List structure, to
* avoid a segfault if objPtr loses its List internal rep [Bug
* 1119369]
+ *
+ * TIP #280 Computes all the line numbers for the
+ * words in the command.
*/
listRepPtr->refCount++;
+ line = 1;
+ for (i=0; i < eoFrame.nline; i++) {
+ eoFrame.line [i] = line;
+ w = Tcl_GetString (elements[i]);
+ TclAdvanceLines (&line, w, w + strlen(w));
+ }
+
+ iPtr->cmdFramePtr = &eoFrame;
result = Tcl_EvalObjv(interp, listRepPtr->elemCount,
&listRepPtr->elements, flags);
+ iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;
+ Tcl_DecrRefCount (eoFrame.cmd.listPtr);
+
/*
* If we are the last users of listRepPtr, free it.
*/
@@ -4209,14 +4571,91 @@ Tcl_EvalObjEx(
}
ckfree((char *) listRepPtr);
}
+
+ ckfree ((char*) eoFrame.line);
+ eoFrame.line = NULL;
+ eoFrame.nline = 0;
+
goto done;
}
}
- script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
- result = Tcl_EvalEx(interp, script, numSrcBytes, flags);
+
+ /*
+ * TIP #280. Propagate context as much as we can. Especially if the
+ * script to evaluate is a single literal it makes sense to look if
+ * our context is one with absolute line numbers we can then track
+ * into the literal itself too.
+ *
+ * See also tclCompile.c, TclInitCompileEnv, for the equivalent code
+ * in the bytecode compiler.
+ */
+
+ if (invoker == NULL) {
+ /* No context, force opening of our own */
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ result = Tcl_EvalEx(interp, script, numSrcBytes, flags);
+ } else {
+ /* We have an invoker, describing the command asking for the
+ * evaluation of a subordinate script. This script may originate
+ * in a literal word, or from a variable, etc. Using the line
+ * array we now check if we have good line information for the
+ * relevant word. The type of context is relevant as well. In a
+ * non-'source' context we don't have to try tracking lines.
+ *
+ * First see if the word exists and is a literal. If not we go
+ * through the easy dynamic branch. No need to perform more
+ * complex invokations.
+ */
+
+ if ((invoker->nline <= word) || (invoker->line[word] < 0)) {
+ /* Dynamic script, or dynamic context, force our own
+ * context */
+
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ result = Tcl_EvalEx(interp, script, numSrcBytes, flags);
+
+ } else {
+ /* Try to get an absolute context for the evaluation
+ */
+
+ CmdFrame ctx = *invoker;
+ int pc = 0;
+
+ if (invoker->type == TCL_LOCATION_BC) {
+ /* Note: Type BC => ctx.data.eval.path is not used.
+ * ctx.data.tebc.codePtr is used instead.
+ */
+ TclGetSrcInfoForPc (&ctx);
+ pc = 1;
+ }
+
+ if (ctx.type == TCL_LOCATION_SOURCE) {
+ /* Absolute context to reuse. */
+
+ iPtr->invokeCmdFramePtr = &ctx;
+ iPtr->evalFlags |= TCL_EVAL_CTX;
+
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ result = TclEvalEx(interp, script, numSrcBytes, flags, ctx.line [word]);
+
+ if (pc) {
+ /* Death of SrcInfo reference */
+ Tcl_DecrRefCount (ctx.data.eval.path);
+ }
+ } else {
+ /* Dynamic context or script, easier to make our own as
+ * well */
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ result = Tcl_EvalEx(interp, script, numSrcBytes, flags);
+ }
+ }
+ }
} else {
/*
* Let the compiler/engine subsystem do the evaluation.
+ *
+ * TIP #280 The invoker provides us with the context for the
+ * script. We transfer this to the byte code compiler.
*/
savedVarFramePtr = iPtr->varFramePtr;
@@ -4224,7 +4663,7 @@ Tcl_EvalObjEx(
iPtr->varFramePtr = iPtr->rootFramePtr;
}
- result = TclCompEvalObj(interp, objPtr);
+ result = TclCompEvalObj(interp, objPtr, invoker, word);
/*
* If we are again at the top level, process any unusual return code