Search code examples
tclframeinterpreterslave

Getting invokation line number when using aliases


I have a proc MYPROC which is called from slave interpreter (with name MYPRO) using alias and unknown mechanisms.

#include <tcl.h>
#include <iostream>

int main()
{
    Tcl_Interp* interp0 = Tcl_CreateInterp();
    Tcl_Interp* interp1 = Tcl_CreateSlave(interp0, "sl", false);
    const char* script1 =
       "proc MYPROC {a} {                              \n"
       "    puts [info frame]                          \n"
       "    global tcl_version                         \n"
       "    if { $tcl_version >= \"8.5\" } {           \n"
       "        puts \"frame 0 [info frame 0]\"        \n"
       "        puts \"frame 1 [info frame 1]\"        \n"
       "        puts \"frame 2 [info frame 2]\"        \n"
       "        puts \"frame 3 [info frame 3]\"        \n"
       "    }                                          \n"
       "}                                              \n"
       "proc m_unknown {cmd args} {                    \n"
       "    ${cmd}C $args                              \n"
       "}                                              \n";

    Tcl_Eval(interp0, script1);

    Tcl_CreateAlias(interp1, "unknown", interp0, "m_unknown", NULL, NULL);
    const char* script2 =
       "set a 1       \n"
       "set b 2       \n"
       "MYPRO {""}    \n";

    if (Tcl_Eval(interp1, script2) == TCL_ERROR) {
        Tcl_Eval(interp1, "puts $errorInfo");
        Tcl_Eval(interp0, "puts $errorInfo");
    }

    return 0;
}

Inside body of MYPROC I need to get line number of MYPRO invocation (I.e. line number of “MYPRO {""}” in script2 which is 3)

Here is an output of this sample

2
frame 0 type proc line 5 cmd {info frame 0} proc ::MYPROC level 0
frame 1 type proc line -1 cmd {${cmd}C $args                              } proc ::m_unknown level 1
frame 2 type proc line 7 cmd {info frame 2} proc ::MYPROC level 0
bad level "3"
    while executing
"info frame 3"
    (procedure "MYPROC" line 8)
    invoked from within
"${cmd}C $args                              "
    (procedure "m_unknown" line 2)
    invoked from within
"MYPRO {}    "
bad level "3"
    while executing
"info frame 3"
    (procedure "MYPROC" line 8)
    invoked from within
"${cmd}C $args                              "
    (procedure "m_unknown" line 2)

Here there is no any frame information about MYPRO invocation line since frame count is 2. Also from error trace of interp1 you can see that proc and line information is missing for MYPRO {} entry.


Solution

  • The problem (for you) is that info frame doesn't describe anything about stack frames in other interpreters. This is by design; interpreters are strongly walled off from each other.

    I've reproduced your issue without any of that C code. The interpreters are named foo and bar, but you could replace those names with anything else.

    % interp create foo
    foo
    % interp eval foo {
      proc MYPROC {a} {
        puts [info frame]   
        global tcl_version
        if { $tcl_version >= "8.5" } {
          puts "frame 1 [info frame 1]"
          puts "frame 2 [info frame 2]"
          puts "frame 3 [info frame 3]"
        }
      }
      proc m_unknown {cmd args} {
        ${cmd}C {*}$args
      }
    }
    % interp create bar
    bar
    % interp alias  bar unknown  foo m_unknown
    unknown
    % interp eval bar {
      set a 1
      set b 2 
      MYPRO {""}
    }
    2
    frame 1 type proc line -1 cmd {${cmd}C {*}$args} proc ::m_unknown level 1
    frame 2 type proc line 7 cmd {info frame 2} proc ::MYPROC level 0
    bad level "3"
    

    That appears to be functioning correctly. (BTW info frame 0 isn't useful output here; it's counting the frame stack from the other end, just as info level 0 does. I've removed it.)

    You'll need to try asking the info frame questions in the interpreter that can answer them. Let's do that (in this case using an alias, but I think interp eval would also be a suitable mechanism):

    % interp alias  foo what  bar info frame
    what
    % interp eval foo {
      proc MYPROC {a} {
        puts [what]
        puts "frame 1 [what 1]"   
        puts "frame 2 [what 2]"
        puts "frame 3 [what 3]"
      }
    }
    % interp eval bar {
      set a 1
      set b 2
      MYPRO ""
    }
    1
    frame 1 type eval line 4 cmd {MYPRO ""} level 0
    bad level "2"
    

    There we go, we're now able to see the correct line number. I assume you'll be able to sort things out from there.