tclproc.c
来自「tcl是工具命令语言」· C语言 代码 · 共 1,707 行 · 第 1/4 页
C
1,707 行
/* * Decrement the ref counts on the objv elements since we are done * with them. */ for (i = 0; i < argc; i++) { objPtr = objv[i]; TclDecrRefCount(objPtr); } /* * Free the objv array if malloc'ed storage was used. */ if (objv != objStorage) { ckfree((char *) objv); } return result;#undef NUM_ARGS}/* *---------------------------------------------------------------------- * * TclObjInterpProc -- * * When a Tcl procedure gets invoked during bytecode evaluation, this * object-based routine gets invoked to interpret the procedure. * * Results: * A standard Tcl object result value. * * Side effects: * Depends on the commands in the procedure. * *---------------------------------------------------------------------- */intTclObjInterpProc(clientData, interp, objc, objv) ClientData clientData; /* Record describing procedure to be * interpreted. */ register Tcl_Interp *interp; /* Interpreter in which procedure was * invoked. */ int objc; /* Count of number of arguments to this * procedure. */ Tcl_Obj *CONST objv[]; /* Argument value objects. */{ Interp *iPtr = (Interp *) interp; register Proc *procPtr = (Proc *) clientData; Namespace *nsPtr = procPtr->cmdPtr->nsPtr; CallFrame frame; register CallFrame *framePtr = &frame; register Var *varPtr; register CompiledLocal *localPtr; char *procName; int nameLen, localCt, numArgs, argCt, i, result; Tcl_Obj *objResult = Tcl_GetObjResult(interp); /* * This procedure generates an array "compiledLocals" that holds the * storage for local variables. It starts out with stack-allocated space * but uses dynamically-allocated storage if needed. */#define NUM_LOCALS 20 Var localStorage[NUM_LOCALS]; Var *compiledLocals = localStorage; /* * Get the procedure's name. */ procName = Tcl_GetStringFromObj(objv[0], &nameLen); /* * If necessary, compile the procedure's body. The compiler will * allocate frame slots for the procedure's non-argument local * variables. Note that compiling the body might increase * procPtr->numCompiledLocals if new local variables are found * while compiling. */ result = TclProcCompileProc(interp, procPtr, procPtr->bodyPtr, nsPtr, "body of proc", procName); if (result != TCL_OK) { return result; } /* * Create the "compiledLocals" array. Make sure it is large enough to * hold all the procedure's compiled local variables, including its * formal parameters. */ localCt = procPtr->numCompiledLocals; if (localCt > NUM_LOCALS) { compiledLocals = (Var *) ckalloc((unsigned) localCt * sizeof(Var)); } /* * Set up and push a new call frame for the new procedure invocation. * This call frame will execute in the proc's namespace, which might * be different than the current namespace. The proc's namespace is * that of its command, which can change if the command is renamed * from one namespace to another. */ result = Tcl_PushCallFrame(interp, (Tcl_CallFrame *) framePtr, (Tcl_Namespace *) nsPtr, /*isProcCallFrame*/ 1); if (result != TCL_OK) { return result; } framePtr->objc = objc; framePtr->objv = objv; /* ref counts for args are incremented below */ /* * Initialize and resolve compiled variable references. */ framePtr->procPtr = procPtr; framePtr->numCompiledLocals = localCt; framePtr->compiledLocals = compiledLocals; TclInitCompiledLocals(interp, framePtr, nsPtr); /* * Match and assign the call's actual parameters to the procedure's * formal arguments. The formal arguments are described by the first * numArgs entries in both the Proc structure's local variable list and * the call frame's local variable array. */ numArgs = procPtr->numArgs; varPtr = framePtr->compiledLocals; localPtr = procPtr->firstLocalPtr; argCt = objc; for (i = 1, argCt -= 1; i <= numArgs; i++, argCt--) { if (!TclIsVarArgument(localPtr)) { panic("TclObjInterpProc: local variable %s is not argument but should be", localPtr->name); return TCL_ERROR; } if (TclIsVarTemporary(localPtr)) { panic("TclObjInterpProc: local variable %d is temporary but should be an argument", i); return TCL_ERROR; } /* * Handle the special case of the last formal being "args". When * it occurs, assign it a list consisting of all the remaining * actual arguments. */ if ((i == numArgs) && ((localPtr->name[0] == 'a') && (strcmp(localPtr->name, "args") == 0))) { Tcl_Obj *listPtr = Tcl_NewListObj(argCt, &(objv[i])); varPtr->value.objPtr = listPtr; Tcl_IncrRefCount(listPtr); /* local var is a reference */ TclClearVarUndefined(varPtr); argCt = 0; break; /* done processing args */ } else if (argCt > 0) { Tcl_Obj *objPtr = objv[i]; varPtr->value.objPtr = objPtr; TclClearVarUndefined(varPtr); Tcl_IncrRefCount(objPtr); /* since the local variable now has * another reference to object. */ } else if (localPtr->defValuePtr != NULL) { Tcl_Obj *objPtr = localPtr->defValuePtr; varPtr->value.objPtr = objPtr; TclClearVarUndefined(varPtr); Tcl_IncrRefCount(objPtr); /* since the local variable now has * another reference to object. */ } else { goto incorrectArgs; } varPtr++; localPtr = localPtr->nextPtr; } if (argCt > 0) { incorrectArgs: /* * Build up equivalent to Tcl_WrongNumArgs message for proc */ Tcl_ResetResult(interp); Tcl_AppendStringsToObj(objResult, "wrong # args: should be \"", procName, (char *) NULL); localPtr = procPtr->firstLocalPtr; for (i = 1; i <= numArgs; i++) { if (localPtr->defValuePtr != NULL) { Tcl_AppendStringsToObj(objResult, " ?", localPtr->name, "?", (char *) NULL); } else { Tcl_AppendStringsToObj(objResult, " ", localPtr->name, (char *) NULL); } localPtr = localPtr->nextPtr; } Tcl_AppendStringsToObj(objResult, "\"", (char *) NULL); result = TCL_ERROR; goto procDone; } /* * Invoke the commands in the procedure's body. */#ifdef TCL_COMPILE_DEBUG if (tclTraceExec >= 1) { fprintf(stdout, "Calling proc "); for (i = 0; i < objc; i++) { TclPrintObject(stdout, objv[i], 15); fprintf(stdout, " "); } fprintf(stdout, "\n"); fflush(stdout); }#endif /*TCL_COMPILE_DEBUG*/ iPtr->returnCode = TCL_OK; procPtr->refCount++; result = TclCompEvalObj(interp, procPtr->bodyPtr); procPtr->refCount--; if (procPtr->refCount <= 0) { TclProcCleanupProc(procPtr); } if (result != TCL_OK) { result = ProcessProcResultCode(interp, procName, nameLen, result); } /* * Pop and free the call frame for this procedure invocation, then * free the compiledLocals array if malloc'ed storage was used. */ procDone: Tcl_PopCallFrame(interp); if (compiledLocals != localStorage) { ckfree((char *) compiledLocals); } return result;#undef NUM_LOCALS}/* *---------------------------------------------------------------------- * * TclProcCompileProc -- * * Called just before a procedure is executed to compile the * body to byte codes. If the type of the body is not * "byte code" or if the compile conditions have changed * (namespace context, epoch counters, etc.) then the body * is recompiled. Otherwise, this procedure does nothing. * * Results: * None. * * Side effects: * May change the internal representation of the body object * to compiled code. * *---------------------------------------------------------------------- */ intTclProcCompileProc(interp, procPtr, bodyPtr, nsPtr, description, procName) Tcl_Interp *interp; /* Interpreter containing procedure. */ Proc *procPtr; /* Data associated with procedure. */ Tcl_Obj *bodyPtr; /* Body of proc. (Usually procPtr->bodyPtr, * but could be any code fragment compiled * in the context of this procedure.) */ Namespace *nsPtr; /* Namespace containing procedure. */ CONST char *description; /* string describing this body of code. */ CONST char *procName; /* Name of this procedure. */{ Interp *iPtr = (Interp*)interp; int result; Tcl_CallFrame frame; Proc *saveProcPtr; ByteCode *codePtr = (ByteCode *) bodyPtr->internalRep.otherValuePtr; /* * If necessary, compile the procedure's body. The compiler will * allocate frame slots for the procedure's non-argument local * variables. If the ByteCode already exists, make sure it hasn't been * invalidated by someone redefining a core command (this might make the * compiled code wrong). Also, if the code was compiled in/for a * different interpreter, we recompile it. Note that compiling the body * might increase procPtr->numCompiledLocals if new local variables are * found while compiling. * * Precompiled procedure bodies, however, are immutable and therefore * they are not recompiled, even if things have changed. */ if (bodyPtr->typePtr == &tclByteCodeType) { if (((Interp *) *codePtr->interpHandle != iPtr) || (codePtr->compileEpoch != iPtr->compileEpoch) || (codePtr->nsPtr != nsPtr)) { if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { if ((Interp *) *codePtr->interpHandle != iPtr) { Tcl_AppendResult(interp, "a precompiled script jumped interps", NULL); return TCL_ERROR; } codePtr->compileEpoch = iPtr->compileEpoch; codePtr->nsPtr = nsPtr; } else { (*tclByteCodeType.freeIntRepProc)(bodyPtr); bodyPtr->typePtr = (Tcl_ObjType *) NULL; } } } if (bodyPtr->typePtr != &tclByteCodeType) { int numChars; char *ellipsis; #ifdef TCL_COMPILE_DEBUG if (tclTraceCompile >= 1) { /* * Display a line summarizing the top level command we * are about to compile. */ numChars = strlen(procName); ellipsis = ""; if (numChars > 50) { numChars = 50; ellipsis = "..."; } fprintf(stdout, "Compiling %s \"%.*s%s\"\n", description, numChars, procName, ellipsis); }#endif /* * Plug the current procPtr into the interpreter and coerce * the code body to byte codes. The interpreter needs to * know which proc it's compiling so that it can access its * list of compiled locals. * * TRICKY NOTE: Be careful to push a call frame with the * proper namespace context, so that the byte codes are * compiled in the appropriate class context. */ saveProcPtr = iPtr->compiledProcPtr; iPtr->compiledProcPtr = procPtr; result = Tcl_PushCallFrame(interp, &frame, (Tcl_Namespace*)nsPtr, /* isProcCallFrame */ 0); if (result == TCL_OK) { result = tclByteCodeType.setFromAnyProc(interp, bodyPtr); Tcl_PopCallFrame(interp); } iPtr->compiledProcPtr = saveProcPtr; if (result != TCL_OK) { if (result == TCL_ERROR) { char buf[100 + TCL_INTEGER_SPACE]; numChars = strlen(procName); ellipsis = ""; if (numChars > 50) { numChars = 50; ellipsis = "..."; } sprintf(buf, "\n (compiling %s \"%.*s%s\", line %d)", description, numChars, procName, ellipsis, interp->errorLine); Tcl_AddObjErrorInfo(interp, buf, -1); } return result; } } else if (codePtr->nsEpoch != nsPtr->resolverEpoch) { register CompiledLocal *localPtr; /* * The resolver epoch has changed, but we only need to invalidate * the resolver cache. */ for (localPtr = procPtr->firstLocalPtr; localPtr != NULL; localPtr = localPtr->nextPtr) { localPtr->flags &= ~(VAR_RESOLVED); if (localPtr->resolveInfo) { if (localPtr->resolveInfo->deleteProc) { localPtr->resolveInfo->deleteProc(localPtr->resolveInfo); } else { ckfree((char*)localPtr->resolveInfo); } localPtr->resolveInfo = NULL; } } } return TCL_OK;}/* *---------------------------------------------------------------------- * * ProcessProcResultCode -- * * Procedure called by TclObjInterpProc to process a return code other * than TCL_OK returned by a Tcl procedure. * * Results: * Depending on the argument return code, the result returned is * another return code and the interpreter's result is set to a value * to supplement that return code. * * Side effects: * If the result returned is TCL_ERROR, traceback information about * the procedure just executed is appended to the interpreter's * "errorInfo" variable. * *----------------------------------------------------------------------
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?