summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2004-01-13 23:15:02 (GMT)
committerdgp <dgp@users.sourceforge.net>2004-01-13 23:15:02 (GMT)
commitc9bd8ac5b1219903842bb5ad5e3f52220aa60701 (patch)
treef2698cb54a86867e9b452e99687cf78e827059d2
parent09c3e3c827f50de5bf0960ebae0ba665da9a0a77 (diff)
downloadtcl-c9bd8ac5b1219903842bb5ad5e3f52220aa60701.zip
tcl-c9bd8ac5b1219903842bb5ad5e3f52220aa60701.tar.gz
tcl-c9bd8ac5b1219903842bb5ad5e3f52220aa60701.tar.bz2
Patch 876451: restores performance of [return]. Also allows forms
such as [return -code error $msg] to be bytecompiled. * generic/tclInt.h: Factored Tcl_ReturnObjCmd() into two pieces: * generic/tclCmdMZ.c: TclMergeReturnOptions(), which can parse the options to [return], check their validity, and create the corresponding return options dictionary, and TclProcessReturn(), which takes that return options dictionary and performs the [return] operation. * generic/tclCompCmds.c: Rewrote TclCompileReturnCmd() to call TclMergeReturnOptions() at compile time so the return options dictionary is computed at compile time (when it is fully known). The dictionary is pushed on the stack along with the result, and the code and level values are included in the bytecode as operands. Also supports optimized compilation of un-[catch]ed [return]s from procs with default options into the INST_DONE instruction. * generic/tclExecute.c: Rewrote INST_RETURN instruction to retrieve the code and level operands, pop the return options from the stack, and call TclProcessReturn() to perform the [return] operation. * generic/tclCompile.h: New utilities include TclEmitInt4 macro * generic/tclCompile.c: and TclWordKnownAtCompileTime().
-rw-r--r--ChangeLog27
-rw-r--r--generic/tclCmdMZ.c218
-rw-r--r--generic/tclCompCmds.c160
-rw-r--r--generic/tclCompile.c86
-rw-r--r--generic/tclCompile.h22
-rw-r--r--generic/tclExecute.c23
-rw-r--r--generic/tclInt.h8
7 files changed, 410 insertions, 134 deletions
diff --git a/ChangeLog b/ChangeLog
index 6d423ae..6762c3c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,32 @@
2004-01-13 Don Porter <dgp@users.sourceforge.net>
+ Patch 876451: restores performance of [return]. Also allows forms
+ such as [return -code error $msg] to be bytecompiled.
+
+ * generic/tclInt.h: Factored Tcl_ReturnObjCmd() into two pieces:
+ * generic/tclCmdMZ.c: TclMergeReturnOptions(), which can parse the
+ options to [return], check their validity, and create the
+ corresponding return options dictionary, and TclProcessReturn(),
+ which takes that return options dictionary and performs the
+ [return] operation.
+
+ * generic/tclCompCmds.c: Rewrote TclCompileReturnCmd() to
+ call TclMergeReturnOptions() at compile time so the return options
+ dictionary is computed at compile time (when it is fully known).
+ The dictionary is pushed on the stack along with the result, and
+ the code and level values are included in the bytecode as operands.
+ Also supports optimized compilation of un-[catch]ed [return]s from
+ procs with default options into the INST_DONE instruction.
+
+ * generic/tclExecute.c: Rewrote INST_RETURN instruction to retrieve
+ the code and level operands, pop the return options from the stack,
+ and call TclProcessReturn() to perform the [return] operation.
+
+ * generic/tclCompile.h: New utilities include TclEmitInt4 macro
+ * generic/tclCompile.c: and TclWordKnownAtCompileTime().
+
+ End Patch 876451.
+
* generic/tclFileName.c (Tcl_GlobObjCmd): Latest changes to
management of the interp result by Tcl_GetIndexFromObj() exposed
improper interp result management in the [glob] command procedure.
diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c
index 1077418..f880057 100644
--- a/generic/tclCmdMZ.c
+++ b/generic/tclCmdMZ.c
@@ -15,7 +15,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCmdMZ.c,v 1.98 2003/12/24 04:18:18 davygrvy Exp $
+ * RCS: @(#) $Id: tclCmdMZ.c,v 1.99 2004/01/13 23:15:02 dgp Exp $
*/
#include "tclInt.h"
@@ -844,29 +844,138 @@ Tcl_ReturnObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- Interp *iPtr = (Interp *) interp;
int code, level;
+ Tcl_Obj *returnOpts;
+
+ /*
+ * General syntax: [return ?-option value ...? ?result?]
+ * An even number of words means an explicit result argument is present.
+ */
+ int explicitResult = (0 == (objc % 2));
+ int numOptionWords = objc - 1 - explicitResult;
+
+ if (TCL_ERROR == TclMergeReturnOptions(interp, numOptionWords, objv+1,
+ &returnOpts, &code, &level)) {
+ return TCL_ERROR;
+ }
+
+ code = TclProcessReturn(interp, code, level, returnOpts);
+ if (explicitResult) {
+ Tcl_SetObjResult(interp, objv[objc-1]);
+ }
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclProcessReturn --
+ *
+ * Does the work of the [return] command based on the code,
+ * level, and returnOpts arguments. Note that the code argument
+ * must agree with the -code entry in returnOpts and the level
+ * argument must agree with the -level entry in returnOpts, as
+ * is the case for values returned from TclMergeReturnOptions.
+ *
+ * Results:
+ * Returns the return code the [return] command should return.
+ *
+ * Side effects:
+ * When the return code is TCL_ERROR, the values of ::errorInfo
+ * and ::errorCode may be updated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclProcessReturn(interp, code, level, returnOpts)
+ Tcl_Interp *interp;
+ int code;
+ int level;
+ Tcl_Obj *returnOpts;
+{
+ Interp *iPtr = (Interp *) interp;
Tcl_Obj *valuePtr;
- /* Start with the default options */
- if (iPtr->returnOpts != iPtr->defaultReturnOpts) {
+ /* Store the merged return options */
+ if (iPtr->returnOpts != returnOpts) {
Tcl_DecrRefCount(iPtr->returnOpts);
- iPtr->returnOpts = iPtr->defaultReturnOpts;
+ iPtr->returnOpts = returnOpts;
Tcl_IncrRefCount(iPtr->returnOpts);
}
- objv++, objc--;
- if (objc) {
- /* We're going to add our options, so manage Tcl_Obj sharing */
- Tcl_DecrRefCount(iPtr->returnOpts);
- iPtr->returnOpts = Tcl_DuplicateObj(iPtr->returnOpts);
- Tcl_IncrRefCount(iPtr->returnOpts);
+ if (level == 0) {
+ if (code == TCL_ERROR) {
+ valuePtr = NULL;
+ Tcl_DictObjGet(NULL, iPtr->returnOpts,
+ iPtr->returnErrorinfoKey, &valuePtr);
+ if (valuePtr != NULL) {
+ int infoLen;
+ CONST char *info = Tcl_GetStringFromObj(valuePtr,&infoLen);
+ if (infoLen) {
+ Tcl_AddObjErrorInfo(interp, info, infoLen);
+ iPtr->flags |= ERR_ALREADY_LOGGED;
+ }
+ }
+ valuePtr = NULL;
+ Tcl_DictObjGet(NULL, iPtr->returnOpts,
+ iPtr->returnErrorcodeKey, &valuePtr);
+ if (valuePtr != NULL) {
+ Tcl_SetVar2Ex(interp, "errorCode", NULL,
+ valuePtr, TCL_GLOBAL_ONLY);
+ iPtr->flags |= ERROR_CODE_SET;
+ }
+ }
+ } else {
+ code = TCL_RETURN;
}
-
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclMergeReturnOptions --
+ *
+ * Parses, checks, and stores the options to the [return] command.
+ *
+ * Results:
+ * Returns TCL_ERROR is any of the option values are invalid.
+ * Otherwise, returns TCL_OK, and writes the returnOpts, code,
+ * and level values to the pointers provided.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclMergeReturnOptions(interp, objc, objv, optionsPtrPtr, codePtr, levelPtr)
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+ Tcl_Obj **optionsPtrPtr; /* If not NULL, points to space for a
+ * (Tcl_Obj *) where the pointer to the
+ * merged return options dictionary should
+ * be written */
+ int *codePtr; /* If not NULL, points to space where the
+ * -code value should be written */
+ int *levelPtr; /* If not NULL, points to space where the
+ * -level value should be written */
+{
+ Interp *iPtr = (Interp *) interp;
+ int code, level, size;
+ Tcl_Obj *valuePtr;
+ Tcl_Obj *returnOpts = Tcl_DuplicateObj(iPtr->defaultReturnOpts);
+
for (; objc > 1; objv += 2, objc -= 2) {
int optLen;
CONST char *opt = Tcl_GetStringFromObj(objv[0], &optLen);
- if ((optLen == 8) && (*opt == '-') && (strcmp(opt, "-options") == 0)) {
+ int compareLen;
+ CONST char *compare =
+ Tcl_GetStringFromObj(iPtr->returnOptionsKey, &compareLen);
+
+ if ((optLen == compareLen) && (strcmp(opt, compare) == 0)) {
Tcl_DictSearch search;
int done = 0;
Tcl_Obj *keyPtr;
@@ -876,38 +985,33 @@ Tcl_ReturnObjCmd(dummy, interp, objc, objv)
if (TCL_ERROR == Tcl_DictObjFirst(NULL, dict,
&search, &keyPtr, &valuePtr, &done)) {
/* Value is not a legal dictionary */
- Tcl_DecrRefCount(iPtr->returnOpts);
- iPtr->returnOpts = iPtr->defaultReturnOpts;
- Tcl_IncrRefCount(iPtr->returnOpts);
Tcl_ResetResult(interp);
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "bad -options value: expected dictionary but got \"",
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad ",
+ compare, " value: expected dictionary but got \"",
Tcl_GetString(objv[1]), "\"", (char *) NULL);
return TCL_ERROR;
}
while (!done) {
- Tcl_DictObjPut(NULL, iPtr->returnOpts, keyPtr, valuePtr);
+ Tcl_DictObjPut(NULL, returnOpts, keyPtr, valuePtr);
Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);
}
valuePtr = NULL;
- Tcl_DictObjGet(NULL, iPtr->returnOpts,
- iPtr->returnOptionsKey, &valuePtr);
+ Tcl_DictObjGet(NULL, returnOpts, iPtr->returnOptionsKey, &valuePtr);
if (valuePtr != NULL) {
dict = valuePtr;
- Tcl_DictObjRemove(NULL, iPtr->returnOpts,
- iPtr->returnOptionsKey);
+ Tcl_DictObjRemove(NULL, returnOpts, iPtr->returnOptionsKey);
goto nestedOptions;
}
} else {
- Tcl_DictObjPut(NULL, iPtr->returnOpts, objv[0], objv[1]);
+ Tcl_DictObjPut(NULL, returnOpts, objv[0], objv[1]);
}
}
/* Check for bogus -code value */
- Tcl_DictObjGet(NULL, iPtr->returnOpts, iPtr->returnCodeKey, &valuePtr);
+ Tcl_DictObjGet(NULL, returnOpts, iPtr->returnCodeKey, &valuePtr);
if (TCL_ERROR == Tcl_GetIntFromObj(NULL, valuePtr, &code)) {
static CONST char *returnCodes[] = {
"ok", "error", "return", "break", "continue", NULL
@@ -916,9 +1020,6 @@ Tcl_ReturnObjCmd(dummy, interp, objc, objv)
if (TCL_ERROR == Tcl_GetIndexFromObj(NULL, valuePtr, returnCodes,
NULL, TCL_EXACT, &code)) {
/* Value is not a legal return code */
- Tcl_DecrRefCount(iPtr->returnOpts);
- iPtr->returnOpts = iPtr->defaultReturnOpts;
- Tcl_IncrRefCount(iPtr->returnOpts);
Tcl_ResetResult(interp);
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad completion code \"",
@@ -928,17 +1029,14 @@ Tcl_ReturnObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
/* Have a legal string value for a return code; convert to integer */
- Tcl_DictObjPut(NULL, iPtr->returnOpts,
+ Tcl_DictObjPut(NULL, returnOpts,
iPtr->returnCodeKey, Tcl_NewIntObj(code));
}
/* Check for bogus -level value */
- Tcl_DictObjGet(NULL, iPtr->returnOpts, iPtr->returnLevelKey, &valuePtr);
+ Tcl_DictObjGet(NULL, returnOpts, iPtr->returnLevelKey, &valuePtr);
if (TCL_ERROR == Tcl_GetIntFromObj(NULL, valuePtr, &level) || (level < 0)) {
/* Value is not a legal level */
- Tcl_DecrRefCount(iPtr->returnOpts);
- iPtr->returnOpts = iPtr->defaultReturnOpts;
- Tcl_IncrRefCount(iPtr->returnOpts);
Tcl_ResetResult(interp);
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad -level value: expected non-negative integer but got \"",
@@ -952,43 +1050,35 @@ Tcl_ReturnObjCmd(dummy, interp, objc, objv)
*/
if (code == TCL_RETURN) {
level++;
- Tcl_DictObjPut(NULL, iPtr->returnOpts,
+ Tcl_DictObjPut(NULL, returnOpts,
iPtr->returnLevelKey, Tcl_NewIntObj(level));
- Tcl_DictObjPut(NULL, iPtr->returnOpts,
+ Tcl_DictObjPut(NULL, returnOpts,
iPtr->returnCodeKey, Tcl_NewIntObj(TCL_OK));
}
- if (level == 0) {
- if (code == TCL_ERROR) {
- valuePtr = NULL;
- Tcl_DictObjGet(NULL, iPtr->returnOpts,
- iPtr->returnErrorinfoKey, &valuePtr);
- if (valuePtr != NULL) {
- int infoLen;
- CONST char *info = Tcl_GetStringFromObj(valuePtr,&infoLen);
- if (infoLen) {
- Tcl_AddObjErrorInfo(interp, info, infoLen);
- iPtr->flags |= ERR_ALREADY_LOGGED;
- }
- }
- valuePtr = NULL;
- Tcl_DictObjGet(NULL, iPtr->returnOpts,
- iPtr->returnErrorcodeKey, &valuePtr);
- if (valuePtr != NULL) {
- Tcl_SetVar2Ex(interp, "errorCode", NULL,
- valuePtr, TCL_GLOBAL_ONLY);
- iPtr->flags |= ERROR_CODE_SET;
- }
- }
- } else {
- code = TCL_RETURN;
+ /*
+ * Check if we just have the default options. If so, use them.
+ * A dictionary equality test would be more robust, but seems
+ * tricky, to say the least.
+ */
+ Tcl_DictObjSize(NULL, returnOpts, &size);
+ if (size == 2 && code == TCL_OK && level == 1) {
+ Tcl_DecrRefCount(returnOpts);
+ returnOpts = iPtr->defaultReturnOpts;
}
-
- if (objc == 1) {
- Tcl_SetObjResult(interp, objv[0]);
+ if (codePtr != NULL) {
+ *codePtr = code;
}
- return code;
-
+ if (levelPtr != NULL) {
+ *levelPtr = level;
+ }
+ if ((optionsPtrPtr == NULL) && (returnOpts != iPtr->defaultReturnOpts)) {
+ /* not passing back the options (?!), so clean them up */
+ Tcl_DecrRefCount(returnOpts);
+ } else {
+ *optionsPtrPtr = returnOpts;
+ }
+ return TCL_OK;
}
/*
diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c
index e3e9eb3..d5cceb4 100644
--- a/generic/tclCompCmds.c
+++ b/generic/tclCompCmds.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompCmds.c,v 1.52 2003/12/24 04:18:19 davygrvy Exp $
+ * RCS: @(#) $Id: tclCompCmds.c,v 1.53 2004/01/13 23:15:02 dgp Exp $
*/
#include "tclInt.h"
@@ -2346,12 +2346,9 @@ TclCompileRegexpCmd(interp, parsePtr, envPtr)
*
* Results:
* The return value is a standard Tcl result, which is TCL_OK if the
- * compilation was successful. If the particular return command is
- * too complex for this function (ie, return with any flags like "-code"
- * or "-errorinfo"), TCL_OUT_LINE_COMPILE is returned, indicating that
- * the command should be compiled "out of line" (eg, not byte compiled).
- * If an error occurs then the interpreter's result contains a standard
- * error message.
+ * compilation was successful. If analysis concludes that the
+ * command cannot be bytecompiled effectively, a return code of
+ * TCL__OUT_LINE_COMPILE is returned.
*
* Side effects:
* Instructions are added to envPtr to execute the "return" command
@@ -2367,65 +2364,114 @@ TclCompileReturnCmd(interp, parsePtr, envPtr)
* command created by Tcl_ParseCommand. */
CompileEnv *envPtr; /* Holds resulting instructions. */
{
- Tcl_Token *varTokenPtr;
- int code;
+ /*
+ * General syntax: [return ?-option value ...? ?result?]
+ * An even number of words means an explicit result argument is present.
+ */
+ int level = 1, code = TCL_OK, status = TCL_OK;
+ int numWords = parsePtr->numWords;
+ int explicitResult = (0 == (numWords % 2));
+ int numOptionWords = numWords - 1 - explicitResult;
+ Interp *iPtr = (Interp *) interp;
+ Tcl_Obj *returnOpts = iPtr->defaultReturnOpts;
+ Tcl_Token *wordTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
- switch (parsePtr->numWords) {
- case 1: {
- /*
- * Simple case: [return]
- * Just push the literal string "".
- */
- TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
- break;
+ if (numOptionWords > 0) {
+ /*
+ * Scan through the return options. If any are unknown at compile
+ * time, there is no value in bytecompiling. Save the option values
+ * known in an objv array for merging into a return options dictionary.
+ */
+ int objc;
+ Tcl_Obj **objv = (Tcl_Obj **)
+ ckalloc(numOptionWords * sizeof(Tcl_Obj *));
+ for (objc = 0; objc < numOptionWords; objc++) {
+ objv[objc] = Tcl_NewObj();
+ Tcl_IncrRefCount(objv[objc]);
+ if (!TclWordKnownAtCompileTime(wordTokenPtr, objv[objc])) {
+ objc++;
+ status = TCL_ERROR;
+ goto cleanup;
+ }
+ wordTokenPtr += wordTokenPtr->numComponents + 1;
+ }
+ status = TclMergeReturnOptions(interp, objc, objv,
+ &returnOpts, &code, &level);
+ cleanup:
+ while (--objc >= 0) {
+ Tcl_DecrRefCount(objv[objc]);
+ }
+ ckfree((char *)objv);
+ if (TCL_ERROR == status) {
+ /* Something was bogus in the return options. Clear the
+ * error message, and report back to the compiler that this
+ * must be interpreted at runtime. */
+ Tcl_ResetResult(interp);
+ return TCL_OUT_LINE_COMPILE;
}
- case 2: {
- /*
- * More complex cases:
- * [return "foo"]
- * [return $value]
- * [return [otherCmd]]
- */
- varTokenPtr = parsePtr->tokenPtr
- + (parsePtr->tokenPtr->numComponents + 1);
- if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
- /*
- * [return "foo"] case: the parse token is a simple word,
- * so just push it.
- */
- TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start,
- varTokenPtr[1].size), envPtr);
- } else {
- /*
- * Parse token is more complex, so compile it; this handles the
- * variable reference and nested command cases. If the
- * parse token can be byte-compiled, then this instance of
- * "return" will be byte-compiled; otherwise it will be
- * out line compiled.
- */
- code = TclCompileTokens(interp, varTokenPtr+1,
- varTokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- return code;
- }
+ }
+
+ /* All options are known at compile time, so we're going to
+ * bytecompile. Emit instructions to push the result on
+ * the stack */
+
+ if (explicitResult) {
+ if (wordTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ /* Explicit result is a simple word, so we can compile quickly to
+ * a simple push */
+ TclEmitPush(TclRegisterNewLiteral(envPtr, wordTokenPtr[1].start,
+ wordTokenPtr[1].size), envPtr);
+ } else {
+ /* More complex tokens get compiled */
+ status = TclCompileTokens(interp, wordTokenPtr+1,
+ wordTokenPtr->numComponents, envPtr);
+ if (TCL_OK != status) {
+ return status;
}
- break;
}
- default: {
- /*
- * Most complex return cases: everything else, including
- * [return -code error], etc.
- */
- return TCL_OUT_LINE_COMPILE;
+ } else {
+ /* No explict result argument, so default result is empty string */
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ }
+
+ /*
+ * Check for optimization: When [return] is in a proc, and there's
+ * no enclosing [catch], and the default return options are in effect,
+ * then the INST_DONE instruction is equivalent, and considerably more
+ * efficient.
+ */
+ if (returnOpts == iPtr->defaultReturnOpts) {
+ /* We have default return options... */
+ if (envPtr->procPtr != NULL) {
+ /* ... and we're in a proc ... */
+ int index = envPtr->exceptArrayNext - 1;
+ int enclosingCatch = 0;
+ while (index >= 0) {
+ ExceptionRange range = envPtr->exceptArrayPtr[index];
+ if ((range.type == CATCH_EXCEPTION_RANGE)
+ && (range.catchOffset == -1)) {
+ enclosingCatch = 1;
+ break;
+ }
+ index--;
+ }
+ if (!enclosingCatch) {
+ /* ... and there is no enclosing catch. */
+ TclEmitOpcode(INST_DONE, envPtr);
+ return TCL_OK;
+ }
}
}
/*
- * The INST_RETURN opcode triggers the branching out of the
- * subroutine, and takes the top stack item as the return result
- * (which is why we pushed the value above).
+ * Could not use the optimization, so we push the return options
+ * dictionary, and emit the INST_RETURN instruction with code
+ * and level as operands.
*/
- TclEmitOpcode(INST_RETURN, envPtr);
+ TclEmitPush(TclAddLiteralObj(envPtr, returnOpts, NULL), envPtr);
+ TclEmitInstInt4(INST_RETURN, code, envPtr);
+ TclEmitInt4(level, envPtr);
return TCL_OK;
}
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index 4946ec2..3f76988 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -11,7 +11,7 @@
* 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.55 2003/12/24 04:18:19 davygrvy Exp $
+ * RCS: @(#) $Id: tclCompile.c,v 1.56 2004/01/13 23:15:02 dgp Exp $
*/
#include "tclInt.h"
@@ -269,8 +269,9 @@ InstructionDesc tclInstructionTable[] = {
* stacked objs: stktop is old value, next is new element value, next
* come (operand-2) indices; pushes the new value.
*/
- {"return", 1, -1, 0, {OPERAND_NONE}},
- /* return TCL_RETURN code. */
+ {"return", 1, -2, 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) */
{"listverify", 1, 0, 0, {OPERAND_NONE}},
@@ -781,6 +782,85 @@ TclFreeCompileEnv(envPtr)
/*
*----------------------------------------------------------------------
*
+ * 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(tokenPtr, valuePtr)
+ 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->start, tokenPtr->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);
+ }
+ continue;
+
+ case TCL_TOKEN_BS:
+ if (tempPtr != NULL) {
+ char utfBuf[TCL_UTF_MAX];
+ int length =
+ Tcl_UtfBackslash(tokenPtr->start, NULL, utfBuf);
+ Tcl_AppendToObj(tempPtr, utfBuf, length);
+ }
+ continue;
+
+ default:
+ if (tempPtr != NULL) {
+ Tcl_DecrRefCount(tempPtr);
+ }
+ return 0;
+ }
+ }
+ if (valuePtr != NULL) {
+ Tcl_AppendObjToObj(valuePtr, tempPtr);
+ Tcl_DecrRefCount(tempPtr);
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclCompileScript --
*
* Compile a Tcl script in a string.
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index 869c7ad..99d719d 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompile.h,v 1.39 2003/11/14 20:44:44 dgp Exp $
+ * RCS: @(#) $Id: tclCompile.h,v 1.40 2004/01/13 23:15:03 dgp Exp $
*/
#ifndef _TCLCOMPILATION
@@ -828,6 +828,8 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
#endif
EXTERN int TclCompileVariableCmd _ANSI_ARGS_((
Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr));
+EXTERN int TclWordKnownAtCompileTime _ANSI_ARGS_((
+ Tcl_Token *tokenPtr, Tcl_Obj *valuePtr));
/*
*----------------------------------------------------------------
@@ -885,10 +887,11 @@ EXTERN int TclCompileVariableCmd _ANSI_ARGS_((
TclUpdateStackReqs(op, 0, envPtr)
/*
- * Macro to emit an integer operand.
- * The ANSI C "prototype" for this macro is:
+ * Macros to emit an integer operand.
+ * The ANSI C "prototype" for these macros are:
*
* EXTERN void TclEmitInt1 _ANSI_ARGS_((int i, CompileEnv *envPtr));
+ * EXTERN void TclEmitInt4 _ANSI_ARGS_((int i, CompileEnv *envPtr));
*/
#define TclEmitInt1(i, envPtr) \
@@ -896,6 +899,19 @@ EXTERN int TclCompileVariableCmd _ANSI_ARGS_((
TclExpandCodeArray(envPtr); \
*(envPtr)->codeNext++ = (unsigned char) ((unsigned int) (i))
+#define TclEmitInt4(i, envPtr) \
+ if (((envPtr)->codeNext + 4) > (envPtr)->codeEnd) { \
+ TclExpandCodeArray(envPtr); \
+ } \
+ *(envPtr)->codeNext++ = \
+ (unsigned char) ((unsigned int) (i) >> 24); \
+ *(envPtr)->codeNext++ = \
+ (unsigned char) ((unsigned int) (i) >> 16); \
+ *(envPtr)->codeNext++ = \
+ (unsigned char) ((unsigned int) (i) >> 8); \
+ *(envPtr)->codeNext++ = \
+ (unsigned char) ((unsigned int) (i) )
+
/*
* Macros to emit an instruction with signed or unsigned integer operands.
* Four byte integers are stored in "big-endian" order with the high order
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index 61d444b..84e5aee 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclExecute.c,v 1.119 2004/01/12 03:23:31 msofer Exp $
+ * RCS: @(#) $Id: tclExecute.c,v 1.120 2004/01/13 23:15:03 dgp Exp $
*/
#include "tclInt.h"
@@ -1231,12 +1231,23 @@ TclExecuteByteCode(interp, codePtr)
switch (*pc) {
case INST_RETURN:
- if (iPtr->returnOpts != iPtr->defaultReturnOpts) {
- Tcl_DecrRefCount(iPtr->returnOpts);
- iPtr->returnOpts = iPtr->defaultReturnOpts;
- Tcl_IncrRefCount(iPtr->returnOpts);
+ {
+ int code = TclGetInt4AtPtr(pc+1);
+ int level = TclGetUInt4AtPtr(pc+5);
+ Tcl_Obj *returnOpts = POP_OBJECT();
+
+ DECACHE_STACK_INFO();
+ Tcl_ResetResult(interp);
+ result = TclProcessReturn(interp, code, level, returnOpts);
+ CACHE_STACK_INFO();
+ Tcl_DecrRefCount(returnOpts);
+ if (result != TCL_OK) {
+ Tcl_SetObjResult(interp, *tosPtr);
+ cleanup = 1;
+ goto processExceptionReturn;
+ }
+ NEXT_INST_F(9, 0, 0);
}
- result = TCL_RETURN;
case INST_DONE:
if (tosPtr <= eePtr->stackPtr + initStackTop) {
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 4734cd7..6423e4f 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -12,7 +12,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclInt.h,v 1.139 2003/12/24 04:20:05 davygrvy Exp $
+ * RCS: @(#) $Id: tclInt.h,v 1.140 2004/01/13 23:15:03 dgp Exp $
*/
#ifndef _TCLINT
@@ -1702,6 +1702,10 @@ EXTERN Tcl_Obj * TclLsetFlat _ANSI_ARGS_((Tcl_Interp* interp,
Tcl_Obj *CONST indexArray[],
Tcl_Obj* valuePtr
));
+EXTERN int TclMergeReturnOptions _ANSI_ARGS_((Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[],
+ Tcl_Obj **optionsPtrPtr, int *codePtr,
+ int *levelPtr));
EXTERN int TclParseBackslash _ANSI_ARGS_((CONST char *src,
int numBytes, int *readPtr, char *dst));
EXTERN int TclParseHex _ANSI_ARGS_((CONST char *src, int numBytes,
@@ -1710,6 +1714,8 @@ EXTERN int TclParseInteger _ANSI_ARGS_((CONST char *string,
int numBytes));
EXTERN int TclParseWhiteSpace _ANSI_ARGS_((CONST char *src,
int numBytes, Tcl_Parse *parsePtr, char *typePtr));
+EXTERN int TclProcessReturn _ANSI_ARGS_((Tcl_Interp *interp,
+ int code, int level, Tcl_Obj *returnOpts));
EXTERN int TclpObjAccess _ANSI_ARGS_((Tcl_Obj *filename,
int mode));
EXTERN int TclpObjLstat _ANSI_ARGS_((Tcl_Obj *pathPtr,