diff options
-rw-r--r-- | jim.c | 235 | ||||
-rw-r--r-- | jim.h | 10 | ||||
-rw-r--r-- | tests/alias.test | 11 |
3 files changed, 134 insertions, 122 deletions
@@ -124,11 +124,11 @@ static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf); static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags); static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, int flags); +static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands); static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, const char *prefix, const char *const *tablePtr, const char *name); -static void JimDeleteLocalProcs(Jim_Interp *interp); static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameObj, int linenr, int argc, Jim_Obj *const *argv); static int JimEvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv, @@ -3475,10 +3475,6 @@ static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) Jim_FreeHashTable(cmdPtr->u.proc.staticVars); Jim_Free(cmdPtr->u.proc.staticVars); } - if (cmdPtr->u.proc.prevCmd) { - /* Delete any pushed command too */ - JimDecrCmdRefCount(interp, cmdPtr->u.proc.prevCmd); - } } else { /* native (C) */ @@ -3486,6 +3482,10 @@ static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData); } } + if (cmdPtr->prevCmd) { + /* Delete any pushed command too */ + JimDecrCmdRefCount(interp, cmdPtr->prevCmd); + } Jim_Free(cmdPtr); } } @@ -3532,30 +3532,59 @@ static const Jim_HashTableType JimCommandsHashTableType = { /* ------------------------- Commands related functions --------------------- */ -int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName, - Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc) +static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd) { - Jim_Cmd *cmdPtr; + /* It may already exist, so we try to delete the old one. + * Note that reference count means that it won't be deleted yet if + * it exists in the call stack. + * + * BUT, if 'local' is in force, instead of deleting the existing + * proc, we stash a reference to the old proc here. + */ + Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name); + if (he) { + /* There was an old cmd with the same name, + * so this requires a 'proc epoch' update. */ - if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) { - /* Command existed so incr proc epoch */ + /* If a procedure with the same name didn't exist there is no need + * to increment the 'proc epoch' because creation of a new procedure + * can never affect existing cached commands. We don't do + * negative caching. */ Jim_InterpIncrProcEpoch(interp); } - cmdPtr = Jim_Alloc(sizeof(*cmdPtr)); + if (he && interp->local) { + /* Push this command over the top of the previous one */ + cmd->prevCmd = he->u.val; + he->u.val = cmd; + } + else { + if (he) { + /* Replace the existing command */ + Jim_DeleteHashEntry(&interp->commands, name); + } + + //printf("%s:%d add %s\n", __FILE__, __LINE__, cmdname); + Jim_AddHashEntry(&interp->commands, name, cmd); + } + return JIM_OK; +} + + +int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr, + Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc) +{ + Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr)); - /* Store the new details for this proc */ + /* Store the new details for this command */ memset(cmdPtr, 0, sizeof(*cmdPtr)); cmdPtr->inUse = 1; cmdPtr->u.native.delProc = delProc; cmdPtr->u.native.cmdProc = cmdProc; cmdPtr->u.native.privData = privData; - Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr); + JimCreateCommand(interp, cmdNameStr, cmdPtr); - /* There is no need to increment the 'proc epoch' because - * creation of a new procedure can never affect existing - * cached commands. We don't do negative caching. */ return JIM_OK; } @@ -3624,7 +3653,6 @@ static int JimCreateProcedure(Jim_Interp *interp, Jim_Obj *cmdName, Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr) { Jim_Cmd *cmdPtr; - Jim_HashEntry *he; int argListLen; int i; @@ -3706,39 +3734,7 @@ static int JimCreateProcedure(Jim_Interp *interp, Jim_Obj *cmdName, } /* Add the new command */ - - /* It may already exist, so we try to delete the old one. - * Note that reference count means that it won't be deleted yet if - * it exists in the call stack. - * - * BUT, if 'local' is in force, instead of deleting the existing - * proc, we stash a reference to the old proc here. - */ - he = Jim_FindHashEntry(&interp->commands, Jim_String(cmdName)); - if (he) { - /* There was an old procedure with the same name, this requires - * a 'proc epoch' update. */ - - /* If a procedure with the same name didn't existed there is no need - * to increment the 'proc epoch' because creation of a new procedure - * can never affect existing cached commands. We don't do - * negative caching. */ - Jim_InterpIncrProcEpoch(interp); - } - - if (he && interp->local) { - /* Just push this proc over the top of the previous one */ - cmdPtr->u.proc.prevCmd = he->u.val; - he->u.val = cmdPtr; - } - else { - if (he) { - /* Replace the existing proc */ - Jim_DeleteHashEntry(&interp->commands, Jim_String(cmdName)); - } - - Jim_AddHashEntry(&interp->commands, Jim_String(cmdName), cmdPtr); - } + JimCreateCommand(interp, Jim_String(cmdName), cmdPtr); /* Unlike Tcl, set the name of the proc as the result */ Jim_SetResult(interp, cmdName); @@ -3851,8 +3847,8 @@ Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags) return NULL; } cmd = objPtr->internalRep.cmdValue.cmdPtr; - while (cmd->isproc && cmd->u.proc.upcall) { - cmd = cmd->u.proc.prevCmd; + while (cmd->u.proc.upcall) { + cmd = cmd->prevCmd; } return cmd; } @@ -4517,7 +4513,7 @@ static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *pare if (interp->freeFramesList) { cf = interp->freeFramesList; - interp->freeFramesList = cf->nextFramePtr; + interp->freeFramesList = cf->next; } else { cf = Jim_Alloc(sizeof(*cf)); @@ -4525,14 +4521,16 @@ static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *pare } cf->id = interp->callFrameEpoch++; - cf->parentCallFrame = parent; + cf->parent = parent; cf->level = parent ? parent->level + 1 : 0; cf->argv = NULL; cf->argc = 0; cf->procArgsObjPtr = NULL; cf->procBodyObjPtr = NULL; - cf->nextFramePtr = NULL; + cf->next = NULL; cf->staticVars = NULL; + cf->localCommands = NULL; + if (cf->vars.table == NULL) Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp); return cf; @@ -4544,6 +4542,43 @@ static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf) cf->id = interp->callFrameEpoch++; } +static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands) +{ + /* Delete any local procs */ + if (localCommands) { + Jim_Obj *cmdNameObj; + + while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) { + Jim_HashEntry *he; + + he = Jim_FindHashEntry(&interp->commands, Jim_String(cmdNameObj)); + + if (he) { + Jim_Cmd *cmd = he->u.val; + if (cmd->prevCmd) { + Jim_Cmd *prevCmd = cmd->prevCmd; + cmd->prevCmd = NULL; + + /* Delete the old command */ + JimDecrCmdRefCount(interp, cmd); + + /* And restore the original */ + he->u.val = prevCmd; + } + else { + Jim_DeleteHashEntry(&interp->commands, Jim_String(cmdNameObj)); + Jim_InterpIncrProcEpoch(interp); + } + } + Jim_DecrRefCount(interp, cmdNameObj); + } + Jim_FreeStack(localCommands); + Jim_Free(localCommands); + } + return JIM_OK; +} + + #define JIM_FCF_NONE 0 /* no flags */ #define JIM_FCF_NOHT 1 /* don't free the hash table */ static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags) @@ -4574,10 +4609,15 @@ static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags) } cf->vars.used = 0; } - cf->nextFramePtr = interp->freeFramesList; + + JimDeleteLocalProcs(interp, cf->localCommands); + + cf->next = interp->freeFramesList; interp->freeFramesList = cf; + } + /* ----------------------------------------------------------------------------- * References * ---------------------------------------------------------------------------*/ @@ -5079,11 +5119,10 @@ void Jim_FreeInterp(Jim_Interp *i) Jim_FreeHashTable(&i->packages); Jim_Free(i->prngState); Jim_FreeHashTable(&i->assocData); - JimDeleteLocalProcs(i); /* Free the call frames list */ while (cf) { - prevcf = cf->parentCallFrame; + prevcf = cf->parent; JimFreeCallFrame(i, cf, JIM_FCF_NONE); cf = prevcf; } @@ -5119,7 +5158,7 @@ void Jim_FreeInterp(Jim_Interp *i) /* Free cached CallFrame structures */ cf = i->freeFramesList; while (cf) { - nextcf = cf->nextFramePtr; + nextcf = cf->next; if (cf->vars.table != NULL) Jim_Free(cf->vars.table); Jim_Free(cf); @@ -5181,7 +5220,7 @@ Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr) } if (level > 0) { /* Lookup */ - for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parentCallFrame) { + for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) { if (framePtr->level == level) { return framePtr; } @@ -5211,7 +5250,7 @@ static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *leve } /* Lookup */ - for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parentCallFrame) { + for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) { if (framePtr->level == level) { return framePtr; } @@ -9728,39 +9767,6 @@ static void JimAddErrorToStack(Jim_Interp *interp, int retcode, Jim_Obj *fileNam } } -/* And delete any local procs */ -static void JimDeleteLocalProcs(Jim_Interp *interp) -{ - if (interp->localProcs) { - char *procname; - - while ((procname = Jim_StackPop(interp->localProcs)) != NULL) { - /* If there is a pushed command, find it */ - Jim_Cmd *prevCmd = NULL; - Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, procname); - if (he) { - Jim_Cmd *cmd = (Jim_Cmd *)he->u.val; - if (cmd->isproc && cmd->u.proc.prevCmd) { - prevCmd = cmd->u.proc.prevCmd; - cmd->u.proc.prevCmd = NULL; - } - } - - /* Delete the local proc */ - Jim_DeleteCommand(interp, procname); - - if (prevCmd) { - /* And restore the pushed command */ - Jim_AddHashEntry(&interp->commands, procname, prevCmd); - } - Jim_Free(procname); - } - Jim_FreeStack(interp->localProcs); - Jim_Free(interp->localProcs); - interp->localProcs = NULL; - } -} - static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr) { Jim_Obj *objPtr; @@ -10165,7 +10171,7 @@ static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argVa Jim_Obj *objPtr; Jim_CallFrame *savedCallFrame = interp->framePtr; - interp->framePtr = interp->framePtr->parentCallFrame; + interp->framePtr = interp->framePtr->parent; objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG); interp->framePtr = savedCallFrame; if (!objPtr) { @@ -10175,7 +10181,7 @@ static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argVa /* It exists, so perform the binding. */ objPtr = Jim_NewStringObj(interp, varname + 1, -1); Jim_IncrRefCount(objPtr); - retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parentCallFrame); + retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent); Jim_DecrRefCount(interp, objPtr); } else { @@ -10235,8 +10241,8 @@ static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameO Jim_Obj *const *argv) { Jim_CallFrame *callFramePtr; - Jim_Stack *prevLocalProcs; int i, d, retcode, optargs; + Jim_Stack *localCommands; /* Check arity */ if (argc - 1 < cmd->u.proc.reqArity || @@ -10264,10 +10270,6 @@ static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameO Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); interp->framePtr = callFramePtr; - /* Install a new stack for local procs */ - prevLocalProcs = interp->localProcs; - interp->localProcs = NULL; - /* How many optional args are available */ optargs = (argc - 1 - cmd->u.proc.reqArity); @@ -10315,13 +10317,18 @@ static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameO badargset: /* Destroy the callframe */ - interp->framePtr = interp->framePtr->parentCallFrame; + /* But first remove the local commands */ + localCommands = callFramePtr->localCommands; + callFramePtr->localCommands = NULL; + + interp->framePtr = interp->framePtr->parent; if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) { JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE); } else { JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT); } + /* Handle the JIM_EVAL return code */ while (retcode == JIM_EVAL) { Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp); @@ -10352,9 +10359,8 @@ badargset: Jim_IncrRefCount(interp->errorProc); } - /* Delete any local procs */ - JimDeleteLocalProcs(interp); - interp->localProcs = prevLocalProcs; + /* Finally delete local procs */ + JimDeleteLocalProcs(interp, localCommands); return retcode; } @@ -12516,17 +12522,18 @@ static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar /* If OK, and the result is a proc, add it to the list of local procs */ if (retcode == 0) { - const char *procname = Jim_String(Jim_GetResult(interp)); + Jim_Obj *cmdNameObj = Jim_GetResult(interp); - if (Jim_FindHashEntry(&interp->commands, procname) == NULL) { - Jim_SetResultFormatted(interp, "not a proc: \"%s\"", procname); + if (Jim_FindHashEntry(&interp->commands, Jim_String(cmdNameObj)) == NULL) { + Jim_SetResultFormatted(interp, "not a command: \"%#s\"", cmdNameObj); return JIM_ERR; } - if (interp->localProcs == NULL) { - interp->localProcs = Jim_Alloc(sizeof(*interp->localProcs)); - Jim_InitStack(interp->localProcs); + if (interp->framePtr->localCommands == NULL) { + interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands)); + Jim_InitStack(interp->framePtr->localCommands); } - Jim_StackPush(interp->localProcs, Jim_StrDup(procname)); + Jim_IncrRefCount(cmdNameObj); + Jim_StackPush(interp->framePtr->localCommands, cmdNameObj); } return retcode; @@ -12543,8 +12550,8 @@ static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a int retcode; Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); - if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->u.proc.prevCmd) { - Jim_SetResultFormatted(interp, "no previous proc: \"%#s\"", argv[1]); + if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) { + Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]); return JIM_ERR; } /* OK. Mark this command as being in an upcall */ @@ -440,14 +440,15 @@ typedef struct Jim_CallFrame { int level; /* Level of this call frame. 0 = global */ struct Jim_HashTable vars; /* Where local vars are stored */ struct Jim_HashTable *staticVars; /* pointer to procedure static vars */ - struct Jim_CallFrame *parentCallFrame; + struct Jim_CallFrame *parent; /* The parent callframe */ Jim_Obj *const *argv; /* object vector of the current procedure call. */ int argc; /* number of args of the current procedure call. */ Jim_Obj *procArgsObjPtr; /* arglist object of the running procedure */ Jim_Obj *procBodyObjPtr; /* body object of the running procedure */ - struct Jim_CallFrame *nextFramePtr; + struct Jim_CallFrame *next; /* Callframes are in a linked list */ Jim_Obj *fileNameObj; /* file and line of caller of this proc (if available) */ int line; + Jim_Stack *localCommands; /* commands to be destroyed when the call frame is destroyed */ } Jim_CallFrame; /* The var structure. It just holds the pointer of the referenced @@ -475,6 +476,7 @@ typedef void (*Jim_DelCmdProc)(struct Jim_Interp *interp, void *privData); typedef struct Jim_Cmd { int inUse; /* Reference count */ int isproc; /* Is this a procedure? */ + struct Jim_Cmd *prevCmd; /* Previous command defn if cmd created 'local' */ union { struct { /* native (C) command */ @@ -487,7 +489,6 @@ typedef struct Jim_Cmd { Jim_Obj *argListObjPtr; Jim_Obj *bodyObjPtr; Jim_HashTable *staticVars; /* Static vars hash table. NULL if no statics. */ - struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */ int argListLen; /* Length of argListObjPtr */ int reqArity; /* Number of required parameters */ int optArity; /* Number of optional parameters */ @@ -562,8 +563,9 @@ typedef struct Jim_Interp { struct Jim_HashTable assocData; /* per-interp storage for use by packages */ Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */ struct Jim_HashTable packages; /* Provided packages hash table */ - Jim_Stack *localProcs; /* procs to be destroyed on end of evaluation */ Jim_Stack *loadHandles; /* handles of loaded modules [load] */ + Jim_Obj *rewriteNameObj; /* Replaces the name of the current command for error reporting */ + int rewriteNameCount; /* How many elements of the current command name to replace */ } Jim_Interp; /* Currently provided as macro that performs the increment. diff --git a/tests/alias.test b/tests/alias.test index 00d3327..c193a42 100644 --- a/tests/alias.test +++ b/tests/alias.test @@ -95,7 +95,10 @@ test local-1.5 "local proc in proc" { } {{} 2} test local-1.6 "local lambda in lsort" { - lsort -command [local lambda {a b} {string compare $a $b}] {d a f g} + proc a {} { + lsort -command [local lambda {a b} {string compare $a $b}] {d a f g} + } + a } {a d f g} test local-1.7 "check no reference procs" { @@ -104,7 +107,7 @@ test local-1.7 "check no reference procs" { test local-1.8 "local on non-proc" { list [catch {local set x blah} msg] $msg -} {1 {not a proc: "blah"}} +} {1 {not a command: "blah"}} test local-1.9 "local on existing proc" { proc x {} { @@ -220,11 +223,11 @@ test upcall-1.3 "double upcall" { test upcall-1.4 "upcall errors" { proc a {} {return 1} list [catch {upcall a} msg] $msg -} {1 {no previous proc: "a"}} +} {1 {no previous command: "a"}} test upcall-1.4 "upcall errors" { proc a {} {upcall a} list [catch a msg] $msg -} {1 {no previous proc: "a"}} +} {1 {no previous command: "a"}} testreport |