📄 tclexecute.c
字号:
{"double", 1, {TCL_EITHER}, ExprDoubleFunc, 0}, {"int", 1, {TCL_EITHER}, ExprIntFunc, 0}, {"rand", 0, {TCL_EITHER}, ExprRandFunc, 0}, /* NOTE: rand takes no args. */ {"round", 1, {TCL_EITHER}, ExprRoundFunc, 0}, {"srand", 1, {TCL_INT}, ExprSrandFunc, 0},#ifdef TCL_WIDE_INT_IS_LONG {"wide", 1, {TCL_EITHER}, ExprIntFunc, 0},#else {"wide", 1, {TCL_EITHER}, ExprWideFunc, 0},#endif /* TCL_WIDE_INT_IS_LONG */ {0},};/* *---------------------------------------------------------------------- * * InitByteCodeExecution -- * * This procedure is called once to initialize the Tcl bytecode * interpreter. * * Results: * None. * * Side effects: * This procedure initializes the array of instruction names. If * compiling with the TCL_COMPILE_STATS flag, it initializes the * array that counts the executions of each instruction and it * creates the "evalstats" command. It also establishes the link * between the Tcl "tcl_traceExec" and C "tclTraceExec" variables. * *---------------------------------------------------------------------- */static voidInitByteCodeExecution(interp) Tcl_Interp *interp; /* Interpreter for which the Tcl variable * "tcl_traceExec" is linked to control * instruction tracing. */{#ifdef TCL_COMPILE_DEBUG if (Tcl_LinkVar(interp, "tcl_traceExec", (char *) &tclTraceExec, TCL_LINK_INT) != TCL_OK) { panic("InitByteCodeExecution: can't create link for tcl_traceExec variable"); }#endif#ifdef TCL_COMPILE_STATS Tcl_CreateObjCommand(interp, "evalstats", EvalStatsCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);#endif /* TCL_COMPILE_STATS */}/* *---------------------------------------------------------------------- * * TclCreateExecEnv -- * * This procedure creates a new execution environment for Tcl bytecode * execution. An ExecEnv points to a Tcl evaluation stack. An ExecEnv * is typically created once for each Tcl interpreter (Interp * structure) and recursively passed to TclExecuteByteCode to execute * ByteCode sequences for nested commands. * * Results: * A newly allocated ExecEnv is returned. This points to an empty * evaluation stack of the standard initial size. * * Side effects: * The bytecode interpreter is also initialized here, as this * procedure will be called before any call to TclExecuteByteCode. * *---------------------------------------------------------------------- */#define TCL_STACK_INITIAL_SIZE 2000ExecEnv *TclCreateExecEnv(interp) Tcl_Interp *interp; /* Interpreter for which the execution * environment is being created. */{ ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv)); Tcl_Obj **stackPtr; stackPtr = (Tcl_Obj **) ckalloc((size_t) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *))); /* * Use the bottom pointer to keep a reference count; the * execution environment holds a reference. */ stackPtr++; eePtr->stackPtr = stackPtr; stackPtr[-1] = (Tcl_Obj *) ((char *) 1); eePtr->stackTop = -1; eePtr->stackEnd = (TCL_STACK_INITIAL_SIZE - 2); eePtr->errorInfo = Tcl_NewStringObj("::errorInfo", -1); Tcl_IncrRefCount(eePtr->errorInfo); eePtr->errorCode = Tcl_NewStringObj("::errorCode", -1); Tcl_IncrRefCount(eePtr->errorCode); Tcl_MutexLock(&execMutex); if (!execInitialized) { TclInitAuxDataTypeTable(); InitByteCodeExecution(interp); execInitialized = 1; } Tcl_MutexUnlock(&execMutex); return eePtr;}#undef TCL_STACK_INITIAL_SIZE/* *---------------------------------------------------------------------- * * TclDeleteExecEnv -- * * Frees the storage for an ExecEnv. * * Results: * None. * * Side effects: * Storage for an ExecEnv and its contained storage (e.g. the * evaluation stack) is freed. * *---------------------------------------------------------------------- */voidTclDeleteExecEnv(eePtr) ExecEnv *eePtr; /* Execution environment to free. */{ if (eePtr->stackPtr[-1] == (Tcl_Obj *) ((char *) 1)) { ckfree((char *) (eePtr->stackPtr-1)); } else { panic("ERROR: freeing an execEnv whose stack is still in use.\n"); } TclDecrRefCount(eePtr->errorInfo); TclDecrRefCount(eePtr->errorCode); ckfree((char *) eePtr);}/* *---------------------------------------------------------------------- * * TclFinalizeExecution -- * * Finalizes the execution environment setup so that it can be * later reinitialized. * * Results: * None. * * Side effects: * After this call, the next time TclCreateExecEnv will be called * it will call InitByteCodeExecution. * *---------------------------------------------------------------------- */voidTclFinalizeExecution(){ Tcl_MutexLock(&execMutex); execInitialized = 0; Tcl_MutexUnlock(&execMutex); TclFinalizeAuxDataTypeTable();}/* *---------------------------------------------------------------------- * * GrowEvaluationStack -- * * This procedure grows a Tcl evaluation stack stored in an ExecEnv. * * Results: * None. * * Side effects: * The size of the evaluation stack is doubled. * *---------------------------------------------------------------------- */static voidGrowEvaluationStack(eePtr) register ExecEnv *eePtr; /* Points to the ExecEnv with an evaluation * stack to enlarge. */{ /* * The current Tcl stack elements are stored from eePtr->stackPtr[0] * to eePtr->stackPtr[eePtr->stackEnd] (inclusive). */ int currElems = (eePtr->stackEnd + 1); int newElems = 2*currElems; int currBytes = currElems * sizeof(Tcl_Obj *); int newBytes = 2*currBytes; Tcl_Obj **newStackPtr = (Tcl_Obj **) ckalloc((unsigned) newBytes); Tcl_Obj **oldStackPtr = eePtr->stackPtr; /* * We keep the stack reference count as a (char *), as that * works nicely as a portable pointer-sized counter. */ char *refCount = (char *) oldStackPtr[-1]; /* * Copy the existing stack items to the new stack space, free the old * storage if appropriate, and record the refCount of the new stack * held by the environment. */ newStackPtr++; memcpy((VOID *) newStackPtr, (VOID *) oldStackPtr, (size_t) currBytes); if (refCount == (char *) 1) { ckfree((VOID *) (oldStackPtr-1)); } else { /* * Remove the reference corresponding to the * environment pointer. */ oldStackPtr[-1] = (Tcl_Obj *) (refCount-1); } eePtr->stackPtr = newStackPtr; eePtr->stackEnd = (newElems - 2); /* index of last usable item */ newStackPtr[-1] = (Tcl_Obj *) ((char *) 1); }/* *-------------------------------------------------------------- * * Tcl_ExprObj -- * * Evaluate an expression in a Tcl_Obj. * * Results: * A standard Tcl object result. If the result is other than TCL_OK, * then the interpreter's result contains an error message. If the * result is TCL_OK, then a pointer to the expression's result value * object is stored in resultPtrPtr. In that case, the object's ref * count is incremented to reflect the reference returned to the * caller; the caller is then responsible for the resulting object * and must, for example, decrement the ref count when it is finished * with the object. * * Side effects: * Any side effects caused by subcommands in the expression, if any. * The interpreter result is not modified unless there is an error. * *-------------------------------------------------------------- */intTcl_ExprObj(interp, objPtr, resultPtrPtr) Tcl_Interp *interp; /* Context in which to evaluate the * expression. */ register Tcl_Obj *objPtr; /* Points to Tcl object containing * expression to evaluate. */ Tcl_Obj **resultPtrPtr; /* Where the Tcl_Obj* that is the expression * result is stored if no errors occur. */{ Interp *iPtr = (Interp *) interp; CompileEnv compEnv; /* Compilation environment structure * allocated in frame. */ LiteralTable *localTablePtr = &(compEnv.localLitTable); register ByteCode *codePtr = NULL; /* Tcl Internal type of bytecode. * Initialized to avoid compiler warning. */ AuxData *auxDataPtr; LiteralEntry *entryPtr; Tcl_Obj *saveObjPtr; char *string; int length, i, result; /* * First handle some common expressions specially. */ string = Tcl_GetStringFromObj(objPtr, &length); if (length == 1) { if (*string == '0') { *resultPtrPtr = Tcl_NewLongObj(0); Tcl_IncrRefCount(*resultPtrPtr); return TCL_OK; } else if (*string == '1') { *resultPtrPtr = Tcl_NewLongObj(1); Tcl_IncrRefCount(*resultPtrPtr); return TCL_OK; } } else if ((length == 2) && (*string == '!')) { if (*(string+1) == '0') { *resultPtrPtr = Tcl_NewLongObj(1); Tcl_IncrRefCount(*resultPtrPtr); return TCL_OK; } else if (*(string+1) == '1') { *resultPtrPtr = Tcl_NewLongObj(0); Tcl_IncrRefCount(*resultPtrPtr); return TCL_OK; } } /* * Get the ByteCode from the object. If it exists, make sure it hasn't * been invalidated by, e.g., someone redefining a command with a * compile procedure (this might make the compiled code wrong). If * necessary, convert the object to be a ByteCode object and compile it. * Also, if the code was compiled in/for a different interpreter, we * recompile it. * * Precompiled expressions, however, are immutable and therefore * they are not recompiled, even if the epoch has changed. * */ if (objPtr->typePtr == &tclByteCodeType) { codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; if (((Interp *) *codePtr->interpHandle != iPtr) || (codePtr->compileEpoch != iPtr->compileEpoch)) { if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { if ((Interp *) *codePtr->interpHandle != iPtr) { panic("Tcl_ExprObj: compiled expression jumped interps"); } codePtr->compileEpoch = iPtr->compileEpoch; } else { (*tclByteCodeType.freeIntRepProc)(objPtr); objPtr->typePtr = (Tcl_ObjType *) NULL; } } } if (objPtr->typePtr != &tclByteCodeType) { TclInitCompileEnv(interp, &compEnv, string, length); result = TclCompileExpr(interp, string, length, &compEnv); /* * Free the compilation environment's literal table bucket array if * it was dynamically allocated. */ if (localTablePtr->buckets != localTablePtr->staticBuckets) { ckfree((char *) localTablePtr->buckets); } if (result != TCL_OK) { /* * Compilation errors. Free storage allocated for compilation. */#ifdef TCL_COMPILE_DEBUG TclVerifyLocalLiteralTable(&compEnv);#endif /*TCL_COMPILE_DEBUG*/ 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++; } TclFreeCompileEnv(&compEnv); return result; } /* * Successful compilation. If the expression yielded no * instructions, push an zero object as the expression's result. */ if (compEnv.codeNext == compEnv.codeStart) { TclEmitPush(TclRegisterLiteral(&compEnv, "0", 1, /*onHeap*/ 0), &compEnv); } /* * Add a "done" instruction as the last instruction and change the * object into a ByteCode object. Ownership of the literal objects * and aux data items is given to the ByteCode object. */ compEnv.numSrcBytes = iPtr->termOffset; TclEmitOpcode(INST_DONE, &compEnv); TclInitByteCodeObj(objPtr, &compEnv); TclFreeCompileEnv(&compEnv); codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;#ifdef TCL_COMPILE_DEBUG if (tclTraceCompile == 2) { TclPrintByteCodeObj(interp, objPtr); }#endif /* TCL_COMPILE_DEBUG */ } /* * Execute the expression after first saving the interpreter's result. */ saveObjPtr = Tcl_GetObjResult(interp); Tcl_IncrRefCount(saveObjPtr); Tcl_ResetResult(interp); /* * Increment the code's ref count while it is being executed. If * afterwards no references to it remain, free the code. */ codePtr->refCount++; result = TclExecuteByteCode(interp, codePtr); codePtr->refCount--; if (codePtr->refCount <= 0) { TclCleanupByteCode(codePtr); objPtr->typePtr = NULL; objPtr->internalRep.otherValuePtr = NULL; } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -