summaryrefslogtreecommitdiffstats
path: root/generic/tclBasic.c
diff options
context:
space:
mode:
authorMiguel Sofer <miguel.sofer@gmail.com>2008-07-29 05:30:25 (GMT)
committerMiguel Sofer <miguel.sofer@gmail.com>2008-07-29 05:30:25 (GMT)
commit2eec1c8e78758156521c033507b1a4513e80d1be (patch)
tree4c1271ec62dc5e1d48fc5559a5b9e8320ba3522a /generic/tclBasic.c
parentf0e9c26da804fcb46360eebe2164bf251f89f4e3 (diff)
downloadtcl-2eec1c8e78758156521c033507b1a4513e80d1be.zip
tcl-2eec1c8e78758156521c033507b1a4513e80d1be.tar.gz
tcl-2eec1c8e78758156521c033507b1a4513e80d1be.tar.bz2
Completely revamped NRE implementation, with (almost) unchanged API.
Diffstat (limited to 'generic/tclBasic.c')
-rw-r--r--generic/tclBasic.c770
1 files changed, 278 insertions, 492 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index 87a36b3..cb96099 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -16,7 +16,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.332 2008/07/28 21:06:09 andreas_kupries Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.333 2008/07/29 05:30:25 msofer Exp $
*/
#include "tclInt.h"
@@ -107,7 +107,7 @@ static int DTraceObjCmd(ClientData dummy, Tcl_Interp *interp, int objc,
MODULE_SCOPE const TclStubs * const tclConstStubsPtr;
/*
- * Block for Tcl_EvalObjv helpers
+ * Tcl_EvalObjv helpers
*/
static void TEOV_SwitchVarFrame(Tcl_Interp *interp);
@@ -120,8 +120,6 @@ static int TEOV_NotFound(Tcl_Interp *interp, int objc,
static int TEOV_RunEnterTraces(Tcl_Interp *interp,
Command **cmdPtrPtr, int objc,
Tcl_Obj *const objv[], Namespace *lookupNsPtr);
-static int NRPostProcess(Tcl_Interp *interp, int result,
- int objc, Tcl_Obj *const objv[]);
static Tcl_NRPostProc TEOV_RestoreVarFrame;
static Tcl_NRPostProc TEOV_RunLeaveTraces;
static Tcl_NRPostProc TEOV_Exception;
@@ -129,7 +127,26 @@ static Tcl_NRPostProc TEOV_Error;
static Tcl_NRPostProc TEOEx_ListCallback;
static Tcl_NRPostProc TEOEx_ByteCodeCallback;
-static Tcl_NRPostProc TailcallCallback;
+static Tcl_NRPostProc NRCommand;
+static Tcl_NRPostProc NRRunObjProc;
+
+static Tcl_NRPostProc EvalTailcall;
+
+#define NR_IS_COMMAND(callbackPtr) \
+ (callbackPtr \
+ && (callbackPtr->procPtr == NRCommand) \
+ && (PTR2INT(callbackPtr->data[1])))
+
+#define NR_CLEAR_COMMAND(interp) \
+ TEOV_callback *callbackPtr = TOP_CB(interp); \
+ \
+ while (!NR_IS_COMMAND(callbackPtr)) { \
+ callbackPtr = callbackPtr->nextPtr; \
+ } \
+ if (callbackPtr) { \
+ callbackPtr->data[1] = INT2PTR(0); \
+ }
+
/*
* The following structure define the commands in the Tcl core.
@@ -894,7 +911,7 @@ Tcl_CreateInterp(void)
Tcl_Panic(Tcl_GetString(Tcl_GetObjResult(interp)));
}
- TOP_RECORD(iPtr) = NULL;
+ TOP_CB(iPtr) = NULL;
return interp;
}
@@ -3950,7 +3967,7 @@ Tcl_CancelEval(
* TCL_ERROR. A result or error message is left in interp's result.
*
* Side effects:
- * Depends on the command.
+ * Always pushes a callback. Other side effects depend on the command.
*
*----------------------------------------------------------------------
*/
@@ -3967,11 +3984,15 @@ Tcl_EvalObjv(
* TCL_EVAL_GLOBAL, TCL_EVAL_INVOKE and
* TCL_EVAL_NOERR are currently supported. */
{
- return TclEvalObjv(interp, objc, objv, flags, NULL);
+ int result;
+ TEOV_callback *rootPtr = TOP_CB(interp);
+
+ result = TclNREvalObjv(interp, objc, objv, flags, NULL);
+ return TclNRRunCallbacks(interp, result, rootPtr, 0);
}
int
-TclEvalObjv(
+TclNREvalObjv(
Tcl_Interp *interp, /* Interpreter in which to evaluate the
* command. Also used for error reporting. */
int objc, /* Number of words in command. */
@@ -3987,40 +4008,38 @@ TclEvalObjv(
{
Interp *iPtr = (Interp *) interp;
int result;
- Namespace *lookupNsPtr = NULL;
- TEOV_record *rootPtr = TOP_RECORD(iPtr);
- TEOV_record *recordPtr;
+ Namespace *lookupNsPtr = iPtr->lookupNsPtr;
Tcl_ObjCmdProc *objProc;
ClientData objClientData;
- int tebcCall = TEBC_CALL(iPtr);
-
- TEBC_CALL(iPtr) = 0;
-
- if (cmdPtr) {
- if (iPtr->lookupNsPtr) {
- iPtr->lookupNsPtr = NULL;
- }
- PUSH_RECORD(interp, recordPtr);
- goto commandFound;
- }
+ Command **cmdPtrPtr;
+
+ iPtr->lookupNsPtr = NULL;
+
+ /*
+ * Push a callback with cleanup tasks for commands; the cmdPtr at data[0]
+ * will be filled later when the command is found: save its address at
+ * objProcPtr.
+ *
+ * data[1] stores a marker for use by tailcalls; it will be reset to 0 by
+ * command redirectors (imports, alias, ensembles) so that tailcalls
+ * finishes the source command and not just the target.
+ */
- restartAtTop:
+ TclNRAddCallback(interp, NRCommand, NULL, INT2PTR(1),
+ NULL, NULL);
+ cmdPtrPtr = (Command **) &(TOP_CB(interp)->data[0]);
+
TclResetCancellation(interp, 0);
iPtr->numLevels++;
result = TclInterpReady(interp);
if ((result != TCL_OK) || (objc == 0)) {
- iPtr->lookupNsPtr = NULL;
- iPtr->numLevels--;
- goto done;
+ return result;
}
- /*
- * Always push a record for the command (avoid queuing callbacks for an
- * older command!)
- */
-
- PUSH_RECORD(interp, recordPtr);
+ if (cmdPtr) {
+ goto commandFound;
+ }
/*
* Push records for task to be done on return, in INVERSE order. First, if
@@ -4035,12 +4054,9 @@ TclEvalObjv(
* Configure evaluation context to match the requested flags.
*/
- lookupNsPtr = iPtr->lookupNsPtr;
if ((flags & TCL_EVAL_INVOKE) || lookupNsPtr) {
if (!lookupNsPtr) {
lookupNsPtr = iPtr->globalNsPtr;
- } else {
- iPtr->lookupNsPtr = NULL;
}
} else {
if (flags & TCL_EVAL_GLOBAL) {
@@ -4063,15 +4079,12 @@ TclEvalObjv(
if (!cmdPtr) {
notFound:
result = TEOV_NotFound(interp, objc, objv, lookupNsPtr);
- iPtr->numLevels--;
- goto done;
+ return result;
}
iPtr->cmdCount++;
if (TclLimitExceeded(iPtr->limit)) {
- result = TCL_ERROR;
- iPtr->numLevels--;
- goto done;
+ return TCL_ERROR;
}
/*
@@ -4090,8 +4103,7 @@ TclEvalObjv(
goto notFound;
}
if (result != TCL_OK) {
- iPtr->numLevels--;
- goto done;
+ return result;
}
}
@@ -4115,154 +4127,38 @@ TclEvalObjv(
}
/*
- * Finally, invoke the command's Tcl_ObjCmdProc.
- *
- * Do the NR dance right here:
- * - for non-NR enabled commands, just sigh and call the objProc
- * - for NR-enabled commands call the part1, decide what to do with the
- * continuation:
- * . if it is a bytecode AND we were called by TEBC, pass it back.
- * Otherwise just call a new TEBC on it. Don't register the
- * callback, TEBC handles those.
- * . if it is a command and it has a callback, push the callback
- * into the TODO list, set the params as needed and restart at the
- * top.
- *
- * Note that I removed the DTRACE thing: I have not really thought about
- * where it really belongs, and do not really know what it does either.
+ * Fix the original callback to point to the now known cmdPtr. Insure that
+ * the Command struct lives until the command returns.
*/
- objProc = cmdPtr->nreProc;
- if (!objProc) {
- objProc = cmdPtr->objProc;
- }
- objClientData = cmdPtr->objClientData;
-
- COMPLETE_RECORD(recordPtr);
+ *cmdPtrPtr = cmdPtr;
cmdPtr->refCount++;
-
- /*
- * If this is an NR-enabled command, find the real objProc.
- */
-
- result = (*objProc)(objClientData, interp, objc, objv);
- if (result != TCL_OK) {
-#if 0
- TclStackPurge(interp, recordPtr->tosPtr);
-#endif
- goto done;
- }
-
+
/*
- * We got a valid callback request: let us complete the corresponding
- * record and proceed with the next call.
+ * Find the objProc to call: nreProc if available, objProc otherwise. Push
+ * a callback to do the actual running.
*/
- callbackReentryPoint:
- switch(recordPtr->type) {
- case TCL_NR_NO_TYPE:
- break;
- case TCL_NR_BC_TYPE:
- tcl_nr_bc_type:
- if (USE_NR_TEBC && tebcCall) {
- return TCL_OK;
- }
-
- /*
- * No TEBC atop - we'll just have to instantiate a new one and do the
- * callback on return.
- */
-
- result = TclExecuteByteCode(interp, recordPtr->data.codePtr);
- goto done;
- case TCL_NR_TAILCALL_TYPE:
- /*
- * Proceed to cleanup the current command, the tailcall will be run
- * from the callbacks.
- */
-
- if (USE_NR_TEBC && tebcCall) {
- return TCL_OK;
- }
- recordPtr->type = TCL_NR_NO_TYPE;
- break;
- case TCL_NR_CMD_TYPE: {
- /*
- * We got an unshared canonical list to eval , do it from here.
- */
-
- Tcl_Obj *objPtr = recordPtr->data.obj.objPtr;
- Tcl_Obj **elemPtr;
-
- flags = recordPtr->data.obj.flags;
- Tcl_ListObjGetElements(NULL, objPtr, &objc, &elemPtr);
- objv = elemPtr;
- if (objc != 0) {
- goto restartAtTop;
- }
- goto done;
- }
- case TCL_NR_SCRIPT_TYPE: {
- Tcl_Obj *objPtr = recordPtr->data.obj.objPtr;
-
- flags = recordPtr->data.obj.flags;
- if (USE_NR_TEBC && tebcCall) {
- result = TclNREvalObjEx(interp, objPtr, flags, NULL, 0);
- if (result == TCL_OK) {
- switch (recordPtr->type) {
- case TCL_NR_BC_TYPE:
- goto tcl_nr_bc_type;
- case TCL_NR_NO_TYPE:
- goto done;
- default:
- Tcl_Panic("TEOEx called from TEOV returns unexpected record type: %d",
- recordPtr->type);
- }
- }
- } else {
- result = TclEvalObjEx(interp, objPtr, flags, NULL, 0);
- }
- goto done;
- }
- case TCL_NR_CMDSWAP_TYPE:
- /*
- * This is a cmdPtr swap like ns-import does.
- */
-
- cmdPtr = recordPtr->cmdPtr;
- objc = recordPtr->data.objcv.objc;
- objv = recordPtr->data.objcv.objv;
- recordPtr->type = TCL_NR_NO_TYPE;
- goto commandFound;
- default:
- Tcl_Panic("TEOV: unknown NR-request type %i!", recordPtr->type);
- }
-
- done:
- result = TclEvalObjv_NR2(interp, result, rootPtr);
- recordPtr = TOP_RECORD(iPtr);
- if (recordPtr == rootPtr) {
- return result;
+ objProc = cmdPtr->nreProc;
+ if (!objProc) {
+ objProc = cmdPtr->objProc;
}
+ objClientData = cmdPtr->objClientData;
- /*
- * A callback scheduled a new evaluation! Deal with it.
- * Note that recordPtr was already updated right above.
- */
-
- assert((result == TCL_OK));
- goto callbackReentryPoint;
+ TclNRAddCallback(interp, NRRunObjProc, objProc, objClientData,
+ INT2PTR(objc), (ClientData) objv);
+ return TCL_OK;
}
int
-TclEvalObjv_NR2(
+TclNRRunCallbacks(
Tcl_Interp *interp,
int result,
- struct TEOV_record *rootPtr)
+ struct TEOV_callback *rootPtr,
+ int tebcCall)
{
Interp *iPtr = (Interp *) interp;
- TEOV_record *recordPtr;
- TEOV_callback *callbackPtr;
+ TEOV_callback *callbackPtr = TOP_CB(interp);
/*
* If the interpreter has a non-empty string result, the result object is
@@ -4278,68 +4174,57 @@ TclEvalObjv_NR2(
(void) Tcl_GetObjResult(interp);
}
- restart:
- while ((recordPtr = TOP_RECORD(iPtr)) != rootPtr) {
- while (recordPtr->callbackPtr) {
- callbackPtr = recordPtr->callbackPtr;
- recordPtr->callbackPtr = callbackPtr->nextPtr;
- result = callbackPtr->procPtr(callbackPtr->data, interp, result);
- TclSmallFree(callbackPtr);
-
- if (recordPtr != TOP_RECORD(iPtr)) {
-
- if (result != TCL_OK) {
- goto restart;
- }
+ while (TOP_CB(interp) != rootPtr) {
+ callbackPtr = TOP_CB(interp);
+ if (tebcCall) {
+ if ((callbackPtr->procPtr == NRRunBytecode) ||
+ (callbackPtr->procPtr == NRDropCommand)) {
/*
- * A callback scheduled a new evaluation; return so that our
- * caller can run it.
+ * TEBC pass thru: let the caller tebc handle and get rid of
+ * this callback.
*/
- switch(recordPtr->type) {
- case TCL_NR_NO_TYPE:
- goto restart;
- case TCL_NR_BC_TYPE:
- case TCL_NR_CMD_TYPE:
- case TCL_NR_SCRIPT_TYPE:
- case TCL_NR_CMDSWAP_TYPE:
- goto done;
- case TCL_NR_TAILCALL_TYPE:
- Tcl_Panic("Tailcall called from a callback!");
- default:
- Tcl_Panic("TEOV_NR2: invalid record type: %d",
- recordPtr->type);
- }
+ return TCL_OK;
}
}
- TOP_RECORD(iPtr) = recordPtr->nextPtr;
-
- if (!CHECK_EXTRA(iPtr, recordPtr)) {
- Tcl_Panic("TclEvalObjv_NR2: wrong tosPtr?");
- /* TclStackPurge(interp, recordPtr->tosPtr); */
- }
/*
- * Decrement the reference count of cmdPtr and deallocate it if it has
- * dropped to zero. The level only needs fixing for records that
- * pushed a cmdPtr.
+ * IMPLEMENTATION REMARKS (FIXME)
+ *
+ * Add here other direct handling possibilities for optimisation?
+ * One could handle the very frequent NRCommand and NRRunObjProc right
+ * here to save an indirect function call and improve icache
+ * management. Would it? Test it, time it ...
*/
- if (recordPtr->cmdPtr) {
- TclCleanupCommandMacro(recordPtr->cmdPtr);
- iPtr->numLevels--;
- }
-
- FREE_RECORD(iPtr, recordPtr);
+ TOP_CB(interp) = callbackPtr->nextPtr;
+ result = callbackPtr->procPtr(callbackPtr->data, interp, result);
+ TCLNR_FREE(interp, callbackPtr);
}
+ return result;
+}
- /*
- * Do not interrupt a series of cleanups with async or limit checks: just
- * check at the end.
- */
+static int
+NRCommand(
+ ClientData data[],
+ Tcl_Interp *interp,
+ int result)
+{
+ Interp *iPtr = (Interp *) interp;
+ Command *cmdPtr = data[0];
+ /* int cmdStart = PTR2INT(data[1]); NOT USED HERE */
- done:
+
+ if (cmdPtr) {
+ TclCleanupCommandMacro(cmdPtr);
+ }
+ ((Interp *)interp)->numLevels--;
+
+ /* OPT ??
+ * Do not interrupt a series of cleanups with async or limit checks:
+ * just check at the end?
+ */
if (TclAsyncReady(iPtr)) {
result = Tcl_AsyncInvoke(interp, result);
@@ -4350,6 +4235,52 @@ TclEvalObjv_NR2(
if (result == TCL_OK && TclLimitReady(iPtr->limit)) {
result = Tcl_LimitCheck(interp);
}
+ return result;
+}
+
+static int
+NRRunObjProc(
+ ClientData data[],
+ Tcl_Interp *interp,
+ int result)
+{
+ /* OPT: do not call? */
+
+ Tcl_ObjCmdProc *objProc = data[0];
+ ClientData objClientData = data[1];
+ int objc = PTR2INT(data[2]);
+ Tcl_Obj **objv = data[3];
+
+ if (result == TCL_OK) {
+ return (*objProc)(objClientData, interp, objc, objv);
+ }
+ return result;
+}
+
+int
+NRRunBytecode(
+ ClientData data[],
+ Tcl_Interp *interp,
+ int result)
+{
+ ByteCode *codePtr = data[0];
+
+ if (result == TCL_OK) {
+ return TclExecuteByteCode(interp, codePtr);
+ }
+ return result;
+}
+
+int
+NRDropCommand(
+ ClientData data[],
+ Tcl_Interp *interp,
+ int result)
+{
+ /* For tailcalls!
+ * drop all callbacks until the last command start: nothing to do here,
+ * just need this to be able to pass it up to tebc.
+ */
return result;
}
@@ -4670,7 +4601,7 @@ TEOV_RunLeaveTraces(
Tcl_DecrRefCount(commandPtr);
/*
- * As cmdPtr is set, TclEvalObjv_NR2 is about to reduce the numlevels.
+ * As cmdPtr is set, TclNRRunCallbacks is about to reduce the numlevels.
* Prevent that by resetting the cmdPtr field and dealing right here with
* cmdPtr->refCount.
*/
@@ -5110,7 +5041,7 @@ TclEvalEx(
TclArgumentEnter(interp, objv, objectsUsed, eeFramePtr);
iPtr->cmdFramePtr = eeFramePtr;
- code = TclEvalObjv(interp, objectsUsed, objv, TCL_EVAL_NOERR, NULL);
+ code = Tcl_EvalObjv(interp, objectsUsed, objv, TCL_EVAL_NOERR);
iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;
TclArgumentRelease(interp, objv, objectsUsed);
@@ -5706,17 +5637,10 @@ TclEvalObjEx(
int word) /* Index of the word which is in objPtr. */
{
int result = TCL_OK;
- TEOV_record *recordPtr;
-
- /*
- * Push an empty record. If this is an NR call, it will modify it
- * accordingly.
- */
+ TEOV_callback *rootPtr = TOP_CB(interp);
- PUSH_RECORD(interp, recordPtr);
result = TclNREvalObjEx(interp, objPtr, flags, invoker, word);
- assert((TOP_RECORD(interp) == recordPtr));
- return NRPostProcess(interp, result, 0, NULL);
+ return TclNRRunCallbacks(interp, result, rootPtr, 0);
}
int
@@ -5759,28 +5683,15 @@ TclNREvalObjEx(
if (objPtr->bytes == NULL || /* ...without a string rep */
listRepPtr->canonicalFlag) { /* ...or that is canonical */
+ Tcl_Obj *listPtr = objPtr;
+ CmdFrame *eoFramePtr = NULL;
+ int objc;
+ Tcl_Obj **objv;
+
/*
* TIP #280 Structures for tracking lines. As we know that this is
* dynamic execution we ignore the invoker, even if known.
- */
-
- CmdFrame *eoFramePtr;
-
- eoFramePtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame));
- eoFramePtr->nline = 0;
- eoFramePtr->line = NULL;
-
- eoFramePtr->type = TCL_LOCATION_EVAL_LIST;
- eoFramePtr->level = (iPtr->cmdFramePtr == NULL?
- 1 : iPtr->cmdFramePtr->level + 1);
- eoFramePtr->numLevels = iPtr->numLevels;
- eoFramePtr->framePtr = iPtr->framePtr;
- eoFramePtr->nextPtr = iPtr->cmdFramePtr;
-
- eoFramePtr->cmd.listPtr = objPtr;
- eoFramePtr->data.eval.path = NULL;
-
- /*
+ *
* TIP #280. We do _not_ compute all the line numbers for the
* words in the command. For the eval of a pure list the most
* sensible choice is to put all words on line 1. Given that we
@@ -5788,13 +5699,44 @@ TclNREvalObjEx(
* left NULL. The two places using this information (TclInfoFrame,
* and TclInitCompileEnv), are special-cased to use the proper
* line number directly instead of accessing the 'line' array.
+ *
+ * Note that we use (word==INTMIN) to signal that no command frame
+ * should be pushed, as needed by alias and ensemble redirections.
*/
- iPtr->cmdFramePtr = eoFramePtr;
+ if (word != INT_MIN) {
+ eoFramePtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame));
+ eoFramePtr->nline = 0;
+ eoFramePtr->line = NULL;
+
+ eoFramePtr->type = TCL_LOCATION_EVAL_LIST;
+ eoFramePtr->level = (iPtr->cmdFramePtr == NULL?
+ 1 : iPtr->cmdFramePtr->level + 1);
+ eoFramePtr->numLevels = iPtr->numLevels;
+ eoFramePtr->framePtr = iPtr->framePtr;
+ eoFramePtr->nextPtr = iPtr->cmdFramePtr;
+
+ eoFramePtr->cmd.listPtr = objPtr;
+ eoFramePtr->data.eval.path = NULL;
+
+ iPtr->cmdFramePtr = eoFramePtr;
+ }
+
+ /*
+ * Shimmer protection! Always pass an unshared obj. The caller could
+ * incr the refCount of objPtr AFTER calling us! To be completely safe
+ * we always make a copy.
+ *
+ * FIXME OPT: preserve just the internal rep?
+ */
+ listPtr = TclListObjCopy(interp, objPtr);
+ Tcl_IncrRefCount(listPtr);
TclNRAddCallback(interp, TEOEx_ListCallback, objPtr, eoFramePtr,
- NULL, NULL);
- return Tcl_NREvalObj(interp, objPtr, flags);
+ listPtr, NULL);
+
+ ListObjGetElements(listPtr, objc, objv);
+ return TclNREvalObjv(interp, objc, objv, flags, NULL);
}
}
@@ -5806,7 +5748,7 @@ TclNREvalObjEx(
* We transfer this to the byte code compiler.
*/
- ByteCode *newCodePtr;
+ ByteCode *codePtr;
CallFrame *savedVarFramePtr = NULL; /* Saves old copy of
* iPtr->varFramePtr in case
* TCL_EVAL_GLOBAL was set. */
@@ -5815,18 +5757,12 @@ TclNREvalObjEx(
savedVarFramePtr = iPtr->varFramePtr;
iPtr->varFramePtr = iPtr->rootFramePtr;
}
+ codePtr = TclCompileObj(interp, objPtr, invoker, word);
+
TclNRAddCallback(interp, TEOEx_ByteCodeCallback, savedVarFramePtr,
objPtr, INT2PTR(allowExceptions), NULL);
-
- newCodePtr = TclCompileObj(interp, objPtr, invoker, word);
- if (newCodePtr) {
- TEOV_record *recordPtr = TOP_RECORD(interp);
-
- recordPtr->type = TCL_NR_BC_TYPE;
- recordPtr->data.codePtr = newCodePtr;
- return TCL_OK;
- }
- return TCL_ERROR;
+ TclNRAddCallback(interp, NRRunBytecode, codePtr, NULL, NULL, NULL);
+ return TCL_OK;
}
/*
@@ -5962,14 +5898,18 @@ TEOEx_ListCallback(
Interp *iPtr = (Interp *) interp;
Tcl_Obj *objPtr = data[0];
CmdFrame *eoFramePtr = data[1];
+ Tcl_Obj *listPtr = data[2];
/*
* Remove the cmdFrame
*/
- iPtr->cmdFramePtr = eoFramePtr->nextPtr;
- TclStackFree(interp, eoFramePtr);
+ if (eoFramePtr) {
+ iPtr->cmdFramePtr = eoFramePtr->nextPtr;
+ TclStackFree(interp, eoFramePtr);
+ }
TclDecrRefCount(objPtr);
+ TclDecrRefCount(listPtr);
return result;
}
@@ -7764,76 +7704,10 @@ Tcl_NRCallObjProc(
Tcl_Obj *const objv[])
{
int result = TCL_OK;
- TEOV_record *recordPtr;
+ TEOV_callback *rootPtr = TOP_CB(interp);
- /*
- * Push an empty record. If this is an NR call, it will modify it
- * accordingly.
- */
-
- PUSH_RECORD(interp, recordPtr);
result = (*objProc)(clientData, interp, objc, objv);
- return NRPostProcess(interp, result, objc, objv);
-}
-
-static int
-NRPostProcess(
- Tcl_Interp *interp,
- int result,
- int objc,
- Tcl_Obj *const objv[])
-{
- TEOV_record *recordPtr, *rootPtr = TOP_RECORD(interp)->nextPtr;
-
- restart:
- recordPtr = TOP_RECORD(interp);
- if (result == TCL_OK) {
- switch (recordPtr->type) {
- case TCL_NR_NO_TYPE:
- break;
- case TCL_NR_BC_TYPE:
- result = TclExecuteByteCode(interp, recordPtr->data.codePtr);
- break;
- case TCL_NR_TAILCALL_TYPE:
- Tcl_SetResult(interp,
- "impossible to tailcall from a non-NRE enabled command",
- TCL_STATIC);
- result = TCL_ERROR;
- break;
- case TCL_NR_CMD_TYPE: {
- Tcl_Obj *objPtr = recordPtr->data.obj.objPtr;
- int flags = recordPtr->data.obj.flags;
- Tcl_Obj **objv;
- int objc;
-
- Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv);
- result = TclEvalObjv(interp, objc, objv, flags, NULL);
- break;
- }
- case TCL_NR_SCRIPT_TYPE: {
- Tcl_Obj *objPtr = recordPtr->data.obj.objPtr;
- int flags = recordPtr->data.obj.flags;
-
- result = TclNREvalObjEx(interp, objPtr, flags, NULL, 0);
- break;
- }
- case TCL_NR_CMDSWAP_TYPE: {
- result = TclEvalObjv(interp, recordPtr->data.objcv.objc,
- recordPtr->data.objcv.objv, 0, recordPtr->cmdPtr);
- break;
- }
- default:
- Tcl_Panic("NRPostProcess: invalid record type: %d",
- recordPtr->type);
- }
- }
-
- result = TclEvalObjv_NR2(interp, result, rootPtr);
- if (TOP_RECORD(interp) != rootPtr) {
- assert((result == TCL_OK));
- goto restart;
- }
- return result;
+ return TclNRRunCallbacks(interp, result, rootPtr, 0);
}
/*
@@ -7891,31 +7765,18 @@ Tcl_NRCreateCommand(
return (Tcl_Command) cmdPtr;
}
-/*
- * These are the previous contents of tclNRE.c, part of the NRE api.
- *
- * TclNREvalCmd should only be called as an optimisation: when objPtr is known
- * to be a canonical list that is not (and will not!) be shared
- */
+/****************************************************************************
+ * Stuff for the public api
+ ****************************************************************************/
int
-TclNREvalCmd(
+Tcl_NREvalObj(
Tcl_Interp *interp,
Tcl_Obj *objPtr,
int flags)
{
- TEOV_record *recordPtr = TOP_RECORD(interp);
-
- Tcl_IncrRefCount(objPtr);
- recordPtr->type = TCL_NR_CMD_TYPE;
- recordPtr->data.obj.objPtr = objPtr;
- recordPtr->data.obj.flags = flags;
- return TCL_OK;
+ return TclNREvalObjEx(interp, objPtr, flags, NULL, INT_MIN);
}
-
-/****************************************************************************
- * Stuff for the public api
- ****************************************************************************/
int
Tcl_NREvalObjv(
@@ -7929,42 +7790,14 @@ Tcl_NREvalObjv(
* TCL_EVAL_GLOBAL, TCL_EVAL_INVOKE and
* TCL_EVAL_NOERR are currently supported. */
{
- Tcl_Obj *listPtr = Tcl_NewListObj(objc, objv);
-
- return TclNREvalCmd(interp, listPtr, flags);
+ return TclNREvalObjv(interp, objc, objv, flags, NULL);
}
-int
-Tcl_NREvalObj(
- Tcl_Interp *interp,
- Tcl_Obj *objPtr,
- int flags)
+void
+TclNRClearCommandFlag(
+ Tcl_Interp *interp)
{
- TEOV_record *recordPtr = TOP_RECORD(interp);
- List *listRep = objPtr->internalRep.twoPtrValue.ptr1;
-
- Tcl_IncrRefCount(objPtr);
- if ((objPtr->typePtr == &tclListType)
- && (!objPtr->bytes || listRep->canonicalFlag)) {
- /*
- * Shimmer protection! Always pass an unshared obj. The caller could
- * incr the refCount of objPtr AFTER calling us! To be completely safe
- * we always make a copy.
- */
-
- Tcl_Obj *origPtr = objPtr;
-
- objPtr = TclListObjCopy(NULL, origPtr);
- Tcl_IncrRefCount(objPtr);
- TclDecrRefCount(origPtr);
-
- recordPtr->type = TCL_NR_CMD_TYPE;
- } else {
- recordPtr->type = TCL_NR_SCRIPT_TYPE;
- }
- recordPtr->data.obj.objPtr = objPtr;
- recordPtr->data.obj.flags = flags;
- return TCL_OK;
+ NR_CLEAR_COMMAND(interp);
}
int
@@ -7972,16 +7805,14 @@ Tcl_NRCmdSwap(
Tcl_Interp *interp,
Tcl_Command cmd,
int objc,
- Tcl_Obj *const objv[])
+ Tcl_Obj *const objv[],
+ int flags)
{
- TEOV_record *recordPtr = TOP_RECORD(interp);
-
- recordPtr->type = TCL_NR_CMDSWAP_TYPE;
- recordPtr->cmdPtr = (Command *) cmd;
- recordPtr->data.objcv.objc = objc;
- recordPtr->data.objcv.objv = (Tcl_Obj **) objv;
+ int result;
- return TCL_OK;
+ result = TclNREvalObjv(interp, objc, objv, flags, (Command *)cmd);
+ NR_CLEAR_COMMAND(interp);
+ return result;
}
/*****************************************************************************
@@ -8016,85 +7847,82 @@ TclTailcallObjCmd(
Tcl_Obj *const objv[])
{
Interp *iPtr = (Interp *) interp;
- TEOV_record *rootPtr = TOP_RECORD(interp);
- TEOV_callback *headPtr, *tailPtr;
- TEOV_record *tmpPtr;
- Tcl_Obj *listPtr;
+ TEOV_callback *rootPtr = TOP_CB(interp);
+ TEOV_callback *tailPtr;
+ Tcl_Obj *scriptPtr;
Namespace *nsPtr = iPtr->varFramePtr->nsPtr;
-
- if (!iPtr->varFramePtr->isProcCallFrame) {
- /* FIXME! Why error? Just look if we have a TEOV above! */
- Tcl_SetResult(interp,
- "tailcall can only be called from a proc or lambda", TCL_STATIC);
- return TCL_ERROR;
+ int count;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "command ?arg ...?");
}
- nsPtr->activationCount++;
- listPtr = Tcl_NewListObj(objc-1, objv+1);
- rootPtr->type = TCL_NR_TAILCALL_TYPE;
-
/*
- * Add a callback to perform the tailcall as LAST item in the caller's
+ * Add a callback to perform the tailcall as LAST item in the CALLER's
* callback stack.
- * Find the first record for the caller: start at the one below the top
- * (the top being this command's record), and go back until you find
- * the one that contains the cmdPtr.
+ * Find the first record for the caller:
+ * 1. find the SECOND callback that contains a cmdPtr below the top (note
+ * that the FIRST one correspond to this TclTailcallObjCmd call)
+ * 2. set the callback for the tailcalled command below that
*/
- tmpPtr = rootPtr->nextPtr;
- while (tmpPtr->cmdPtr == NULL) {
- tmpPtr = tmpPtr->nextPtr;
+ tailPtr = rootPtr;
+ count = NR_IS_COMMAND(tailPtr);
+ while (tailPtr && tailPtr->nextPtr && (count < 2)) {
+ tailPtr = tailPtr->nextPtr;
+ count += NR_IS_COMMAND(tailPtr);
}
- /*
- * Now find the first and last callbacks in this record, and temporarily
- * set the callback list to empty.
- */
-
- headPtr = tailPtr = tmpPtr->callbackPtr;
- if (headPtr) {
- while (tailPtr->nextPtr) {
- tailPtr = tailPtr->nextPtr;
- }
- tmpPtr->callbackPtr = NULL;
+#if 1
+ if (!iPtr->varFramePtr->isProcCallFrame) {
+ /* FIXME! Why error? Just look if we have a TEOV above! */
+ Tcl_SetResult(interp,
+ "tailcall can only be called from a proc or lambda", TCL_STATIC);
+ return TCL_ERROR;
+ }
+#else
+ if (!tailPtr->nextPtr) {
+ /* FIXME! Is this the behaviour we want? */
+ Tcl_SetResult(interp,
+ "cannot tailcall: not running a command", TCL_STATIC);
+ return TCL_ERROR;
}
+#endif
/*
- * Temporarily put tmpPtr as the TOP_RECORD, register a callback, then
+ * Temporarily put NULL as the TOP_BC, register a callback, then
* replug things back the way they were.
*/
- TOP_RECORD(iPtr) = tmpPtr;
- TclNRAddCallback(interp, TailcallCallback, listPtr, nsPtr, NULL, NULL);
- TOP_RECORD(iPtr) = rootPtr;
-
- if (headPtr) {
- tailPtr->nextPtr = tmpPtr->callbackPtr;
- tmpPtr->callbackPtr = headPtr;
+ nsPtr->activationCount++;
+ if (objc == 2) {
+ scriptPtr = objv[1];
+ } else {
+ scriptPtr = Tcl_NewListObj(objc-1, objv+1);
}
+ TOP_CB(iPtr) = tailPtr->nextPtr;
+ TclNRAddCallback(interp, EvalTailcall, scriptPtr, nsPtr, NULL, NULL);
+ tailPtr->nextPtr = TOP_CB(iPtr);
+ TOP_CB(iPtr) = rootPtr;
+
+ TclNRAddCallback(interp, NRDropCommand, NULL, NULL, NULL, NULL);
return TCL_OK;
}
static int
-TailcallCallback(
+EvalTailcall(
ClientData data[],
Tcl_Interp *interp,
int result)
{
Interp *iPtr = (Interp *) interp;
- Tcl_Obj *listPtr = data[0], *namePtr;
+ Tcl_Obj *scriptPtr = data[0];
Namespace *nsPtr = data[1];
- TEOV_record *recordPtr = TOP_RECORD(iPtr);
- Command *cmdPtr = NULL;
- if (!recordPtr->cmdPtr || recordPtr->callbackPtr) {
- Tcl_Panic("TailcallCallback: should not happen!");
- }
-
- result = Tcl_ListObjIndex(interp, listPtr, 0, &namePtr);
if (result == TCL_OK) {
- cmdPtr = TEOV_LookupCmdFromObj(interp, namePtr, nsPtr);
+ iPtr->lookupNsPtr = nsPtr;
+ result = TclNREvalObjEx(interp, scriptPtr, 0, NULL, 0);
}
nsPtr->activationCount--;
@@ -8107,29 +7935,7 @@ TailcallCallback(
Tcl_DeleteNamespace((Tcl_Namespace *) nsPtr);
}
-
- if (!cmdPtr || (result != TCL_OK)) {
- Tcl_DecrRefCount(listPtr);
- Tcl_SetResult(interp,
- "the command to be tailcalled does not exist", TCL_STATIC);
- return TCL_ERROR;
- }
-
- /*
- * Take over the previous command's record.
- */
-
- TclCleanupCommandMacro(recordPtr->cmdPtr);
- recordPtr->cmdPtr = cmdPtr;
- cmdPtr->refCount++;
-
- /*
- * Push a new record to signal that a new command was scheduled.
- */
-
- PUSH_RECORD(iPtr, recordPtr);
- iPtr->lookupNsPtr = nsPtr;
- return TclNREvalCmd(interp, listPtr, 0);
+ return result;
}
void
@@ -8142,31 +7948,11 @@ Tcl_NRAddCallback(
ClientData data3)
{
if (!(postProcPtr)) {
- Tcl_Panic("Adding a callback without and objProc?!");
+ Tcl_Panic("Adding a callback without an objProc?!");
}
TclNRAddCallback(interp, postProcPtr, data0, data1, data2, data3);
}
-TEOV_record *
-TclNRPushRecord(
- Tcl_Interp *interp)
-{
- TEOV_record *recordPtr;
-
- PUSH_RECORD(interp, recordPtr);
- return recordPtr;
-}
-
-void
-TclNRPopAndFreeRecord(
- Tcl_Interp *interp)
-{
- TEOV_record *recordPtr;
-
- POP_RECORD(interp, recordPtr);
- FREE_RECORD(interp, recordPtr);
-}
-
/*
* Local Variables:
* mode: c