aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jim.c47
-rw-r--r--jim.h1
-rw-r--r--tclcompat.tcl7
-rw-r--r--tests/alias.test43
4 files changed, 90 insertions, 8 deletions
diff --git a/jim.c b/jim.c
index 3f858ab..58fcb84 100644
--- a/jim.c
+++ b/jim.c
@@ -3297,8 +3297,8 @@ int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
/* Store the new details for this proc */
+ memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
- cmdPtr->isproc = 0;
cmdPtr->u.native.delProc = delProc;
cmdPtr->u.native.cmdProc = cmdProc;
cmdPtr->u.native.privData = privData;
@@ -3319,6 +3319,7 @@ static int JimCreateProcedure(Jim_Interp *interp, const char *cmdName,
Jim_HashEntry *he;
cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
+ memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
cmdPtr->isproc = 1;
cmdPtr->u.proc.argListObjPtr = argListObjPtr;
@@ -3331,6 +3332,7 @@ static int JimCreateProcedure(Jim_Interp *interp, const char *cmdName,
cmdPtr->u.proc.rightArity = rightArity;
cmdPtr->u.proc.staticVars = NULL;
cmdPtr->u.proc.prevCmd = NULL;
+ cmdPtr->inUse = 1;
/* Create the statics hash table. */
if (staticsListObjPtr) {
@@ -3517,9 +3519,14 @@ int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
* stored in objPtr. It tries to specialize the objPtr to contain
* a cached info instead to perform the lookup into the hash table
* every time. The information cached may not be uptodate, in such
- * a case the lookup is performed and the cache updated. */
+ * a case the lookup is performed and the cache updated.
+ *
+ * Respects the 'upcall' setting
+ */
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
+ Jim_Cmd *cmd;
+
if ((objPtr->typePtr != &commandObjType ||
objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
SetCommandFromAny(interp, objPtr) == JIM_ERR) {
@@ -3528,7 +3535,11 @@ Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
}
return NULL;
}
- return objPtr->internalRep.cmdValue.cmdPtr;
+ cmd = objPtr->internalRep.cmdValue.cmdPtr;
+ while (cmd->isproc && cmd->u.proc.upcall) {
+ cmd = cmd->u.proc.prevCmd;
+ }
+ return cmd;
}
/* -----------------------------------------------------------------------------
@@ -12254,6 +12265,35 @@ static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
return retcode;
}
+/* [upcall] */
+static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ if (argc < 2) {
+ Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
+ return JIM_ERR;
+ }
+ else {
+ 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]);
+ return JIM_ERR;
+ }
+ /* OK. Mark this command as being in an upcall */
+ cmdPtr->u.proc.upcall++;
+ JimIncrCmdRefCount(cmdPtr);
+
+ /* Invoke the command as normal */
+ retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
+
+ /* No longer in an upcall */
+ cmdPtr->u.proc.upcall--;
+ JimDecrCmdRefCount(interp, cmdPtr);
+
+ return retcode;
+ }
+}
/* [concat] */
static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
@@ -13957,6 +13997,7 @@ static const struct {
{"rand", Jim_RandCoreCommand},
{"tailcall", Jim_TailcallCoreCommand},
{"local", Jim_LocalCoreCommand},
+ {"upcall", Jim_UpcallCoreCommand},
{NULL, NULL},
};
diff --git a/jim.h b/jim.h
index e968c89..f19bf1a 100644
--- a/jim.h
+++ b/jim.h
@@ -494,6 +494,7 @@ typedef struct Jim_Cmd {
int rightArity; /* Required args assigned from the right */
int args; /* True if 'args' specified */
struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */
+ int upcall; /* True if proc is currently in upcall */
} proc;
} u;
} Jim_Cmd;
diff --git a/tclcompat.tcl b/tclcompat.tcl
index 262aae8..8559edc 100644
--- a/tclcompat.tcl
+++ b/tclcompat.tcl
@@ -162,11 +162,9 @@ proc popen {cmd {mode r}} {
}
# A wrapper around 'pid' which can return the pids for 'popen'
-if {[info commands pid] ne ""} {
-rename pid .pid
-proc pid {{chan {}}} {
+local proc pid {{chan {}}} {
if {$chan eq ""} {
- tailcall .pid
+ tailcall upcall pid
}
if {[catch {$chan tell}]} {
return -code error "can not find channel named \"$chan\""
@@ -176,7 +174,6 @@ proc pid {{chan {}}} {
}
return $pids
}
-}
# try/on/finally conceptually similar to Tcl 8.6
#
diff --git a/tests/alias.test b/tests/alias.test
index bcafe04..abbc2a0 100644
--- a/tests/alias.test
+++ b/tests/alias.test
@@ -192,4 +192,47 @@ test local-2.3 "local proc over existing proc" {
lappend x [a 5]
} {6 4 6}
+test upcall-1.1 "upcall pushed proc" {
+ proc a {b} {incr b}
+ local proc a {b} {
+ incr b 10
+ # invoke the original defn via upcall
+ return [upcall a $b]
+ }
+ # Should call the new defn which will call the original defn
+ a 3
+} 14
+
+test upcall-1.2 "upcall in proc" {
+ proc a {b} {incr b}
+ proc t {c} {
+ local proc a {b} {
+ incr b 10
+ return [upcall a $b]
+ }
+ a $c
+ }
+ unset -nocomplain x
+ lappend x [t 5]
+ lappend x [a 5]
+ set x
+} {16 6}
+
+test upcall-1.3 "double upcall" {
+ proc a {} {return 1}
+ local proc a {} {list 2 {*}[upcall a]}
+ local proc a {} {list 3 {*}[upcall a]}
+ a
+} {3 2 1}
+
+test upcall-1.4 "upcall errors" {
+ proc a {} {return 1}
+ list [catch {upcall a} msg] $msg
+} {1 {no previous proc: "a"}}
+
+test upcall-1.4 "upcall errors" {
+ proc a {} {upcall a}
+ list [catch a msg] $msg
+} {1 {no previous proc: "a"}}
+
testreport