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 + -
显示快捷键?