Example TJC Generated Code

The following examples show TJC generated code for some simple Tcl commands.

Assume the compiler input is the following Tcl source file:

$ cat simple.tcl
proc simple {} {
    return "SIMPLE"
}

The TJC module file simple.tjc is defined as:

$ cat simple.tjc
PACKAGE simple

SOURCE simple.tcl
INIT_SOURCE simple.tcl

OPTIONS +inline-containers +inline-controls

Assuming that the tjc executable is already on the PATH, the TJC compiler is invoke via:

$ tjc simple.tjc

If no error is generated, then nothing is printed and the files simple.jar and simplesrc.jar are created in the current directory.

The contents of the generated simplesrc.jar are as follows:

$ jar -tf simplesrc.jar
META-INF/
META-INF/MANIFEST.MF
simple/
simple/library/
simple/library/simple.tcl
simple/SimpleCmd.java
simple/TJCExtension.java


Extract the contents of simplesrc.jar as follows:

$ mkdir simplesrc
$ cd simplesrc
$ jar -xf ../simplesrc.jar
$ cd ..

$ cat simplesrc/simple/SimpleCmd.java
// TJC implementation of procedure simple
package simple;
import tcl.lang.*;

public class SimpleCmd extends TJC.CompiledCommand {
    public void cmdProc(
        Interp interp,
        TclObject[] objv)
            throws TclException
    {
        if (!initCmd) { initCmd(interp); }
        CallFrame callFrame = TJC.pushLocalCallFrame(interp, wcmd.ns);
        try {
        if (objv.length != 1) {
            throw new TclNumArgsException(interp, 1, objv, "");
        }
        { // Invoke: return "SIMPLE"
            interp.resetResult();
            interp.setResult( const0 );
            if ( true ) { return; }
        } // End Invoke: return
        } catch (TclException te) {
            TJC.checkTclException(interp, te, "simple");
        } finally {
            TJC.popLocalCallFrame(interp, callFrame);
        }
    }

    TclObject const0;

    protected void initConstants(Interp interp) throws TclException {
        const0 = TclString.newInstance("SIMPLE");
        const0.preserve(); const0.preserve();
    }
} // end class SimpleCmd


The Java class above implements the proc simple defined in simple.tcl. The implementation will push a call frame, set the interp result to a class constant for the string SIMPLE, and then return after popping the call frame.

The example above shows how a trivial Tcl command is compiled, the next example demonstrates loop inlining and invocation of the puts command. Delete the generate JAR files and the directory and modify the simple.tcl file as follows:

$ cat simple.tcl
proc simple {} {
    foreach elem {1 2 3 4} {
        puts "elem is $elem"
    }
}

Recompiling and extracting the generated file results in:

$ cat simplesrc/simple/SimpleCmd.java
// TJC implementation of procedure simple
package simple;
import tcl.lang.*;

public class SimpleCmd extends TJC.CompiledCommand {
    public void cmdProc(
        Interp interp,
        TclObject[] objv)
            throws TclException
    {
        if (!initCmd) { initCmd(interp); }
        CallFrame callFrame = TJC.pushLocalCallFrame(interp, wcmd.ns);
        try {
        if (objv.length != 1) {
            throw new TclNumArgsException(interp, 1, objv, "");
        }
        { // Invoke: foreach elem {1 2 3 4} ...
            TclObject tmp0 = null;
            try {
                tmp0 = const0;
                tmp0.preserve();
                final int tmp0_length = TclList.getLength(interp, tmp0);

                for ( int index1 = 0 ; index1 < tmp0_length ; index1++ ) {
                    TclObject tmp2 = TclList.index(interp, tmp0, index1);
                    try {
                        interp.setVar("elem", null, tmp2, 0);
                    } catch (TclException ex) {
                        TJC.foreachVarErr(interp, "elem");
                    }

                    try {
                    if ( false ) { throw (TclException) null; }
                    { // Invoke: puts "elem is $elem"
                        TclObject[] objv3 = TJC.grabObjv(interp, 2);
                        try {
                            TclObject tmp4;
                            // Arg 0 constant: puts
                            tmp4 = const1;
                            tmp4.preserve();
                            objv3[0] = tmp4;
                            // Arg 1 word: "elem is $elem"
                            StringBuffer sbtmp5 = new StringBuffer(64);
                            sbtmp5.append("elem is ");
                            tmp4 = interp.getVar("elem", null, 0);
                            sbtmp5.append(tmp4.toString());
                            tmp4 = TclString.newInstance(sbtmp5);
                            tmp4.preserve();
                            objv3[1] = tmp4;
                            TJC.invoke(interp, null, objv3, 0);
                        } finally {
                            TJC.releaseObjv(interp, objv3, 2);
                        }
                    } // End Invoke: puts
                    } catch (TclException ex) {
                        int type = ex.getCompletionCode();
                        if (type == TCL.BREAK) {
                            break;
                        } else if (type == TCL.CONTINUE) {
                            continue;
                        } else {
                            throw ex;
                        }
                    }
                }
                interp.resetResult();
            } finally {
                if ( tmp0 != null ) {
                    tmp0.release();
                }
            }
        } // End Invoke: foreach
        } catch (TclException te) {
            TJC.checkTclException(interp, te, "simple");
        } finally {
            TJC.popLocalCallFrame(interp, callFrame);
        }
    }

    TclObject const0;
    TclObject const1;

    protected void initConstants(Interp interp) throws TclException {
        const0 = TclString.newInstance("1 2 3 4");
        const0.preserve(); const0.preserve();
        const1 = TclString.newInstance("puts");
        const1.preserve(); const1.preserve();
    }
} // end class SimpleCmd


The generated code above demonstrates a number of important features. First, the Tcl foreach loop is inlined as a Java for loop. A list element is assigned to the variable elem and then the puts command is invoked. Before the puts command can be invoked, the "elem is $elem" argument string is created with the current value of the elem variable substituted into the string. The loop then continues with the next iteration. The for loop also includes code to detect and deal with Tcl's break and continue control flow commands inside the foreach loop. The constant pool includes the list {1 2 3 4} and an entry that will be passed as the first argument to the puts command.

The next example demonstrates compiled expression logic and the if command. Delete the generated JAR files and the directory and modify the simple.tcl file as follows:

$ cat simple.tcl
proc simple {} {
    set i 10
    if {($i % 2) == 0} {
       puts even
    } else {
       puts odd
    }
}

Recompiling and extracting the generated file results in:

$ cat simplesrc/simple/SimpleCmd.java
// TJC implementation of procedure simple
package simple;
import tcl.lang.*;

public class SimpleCmd extends TJC.CompiledCommand {
    public void cmdProc(
        Interp interp,
        TclObject[] objv)
            throws TclException
    {
        if (!initCmd) { initCmd(interp); }
        CallFrame callFrame = TJC.pushLocalCallFrame(interp, wcmd.ns);
        try {
        if (objv.length != 1) {
            throw new TclNumArgsException(interp, 1, objv, "");
        }
        { // Invoke: set i 10
            TclObject[] objv0 = TJC.grabObjv(interp, 3);
            try {
                TclObject tmp1;
                // Arg 0 constant: set
                tmp1 = const0;
                tmp1.preserve();
                objv0[0] = tmp1;
                // Arg 1 constant: i
                tmp1 = const1;
                tmp1.preserve();
                objv0[1] = tmp1;
                // Arg 2 constant: 10
                tmp1 = const2;
                tmp1.preserve();
                objv0[2] = tmp1;
                TJC.invoke(interp, null, objv0, 0);
            } finally {
                TJC.releaseObjv(interp, objv0, 3);
            }
        } // End Invoke: set
        { // Invoke: if {($i % 2) == 0} ... else ...
            // Binary operator: () == 0
            // Binary operator: $i % 2
            TclObject tmp2 = interp.getVar("i", null, 0);
            ExprValue tmp3 = TJC.exprGetValue(interp, tmp2);
            ExprValue tmp4 = TJC.exprGetValue(interp, 2, null);
            TJC.exprBinaryOperator(interp, TJC.EXPR_OP_MOD, tmp3, tmp4);
            // End Binary operator: %
            ExprValue tmp5 = TJC.exprGetValue(interp, 0, null);
            TJC.exprBinaryOperator(interp, TJC.EXPR_OP_EQUAL, tmp3, tmp5);
            // End Binary operator: ==
            boolean tmp6 = tmp3.getBooleanValue(interp);
            TJC.exprReleaseValue(interp, tmp3);
            if ( tmp6 ) {
                { // Invoke: puts even
                    TclObject[] objv7 = TJC.grabObjv(interp, 2);
                    try {
                        TclObject tmp8;
                        // Arg 0 constant: puts
                        tmp8 = const3;
                        tmp8.preserve();
                        objv7[0] = tmp8;
                        // Arg 1 constant: even
                        tmp8 = const4;
                        tmp8.preserve();
                        objv7[1] = tmp8;
                        TJC.invoke(interp, null, objv7, 0);
                    } finally {
                        TJC.releaseObjv(interp, objv7, 2);
                    }
                } // End Invoke: puts
            } else {
                { // Invoke: puts odd
                    TclObject[] objv9 = TJC.grabObjv(interp, 2);
                    try {
                        TclObject tmp10;
                        // Arg 0 constant: puts
                        tmp10 = const3;
                        tmp10.preserve();
                        objv9[0] = tmp10;
                        // Arg 1 constant: odd
                        tmp10 = const5;
                        tmp10.preserve();
                        objv9[1] = tmp10;
                        TJC.invoke(interp, null, objv9, 0);
                    } finally {
                        TJC.releaseObjv(interp, objv9, 2);
                    }
                } // End Invoke: puts
            }
        } // End Invoke: if
        } catch (TclException te) {
            TJC.checkTclException(interp, te, "simple");
        } finally {
            TJC.popLocalCallFrame(interp, callFrame);
        }
    }

    TclObject const0;
    TclObject const1;
    TclObject const2;
    TclObject const3;
    TclObject const4;
    TclObject const5;

    protected void initConstants(Interp interp) throws TclException {
        const0 = TclString.newInstance("set");
        const0.preserve(); const0.preserve();
        const1 = TclString.newInstance("i");
        const1.preserve(); const1.preserve();
        const2 = TclInteger.newInstance(10);
        const2.preserve(); const2.preserve();
        const3 = TclString.newInstance("puts");
        const3.preserve(); const3.preserve();
        const4 = TclString.newInstance("even");
        const4.preserve(); const4.preserve();
        const5 = TclString.newInstance("odd");
        const5.preserve(); const5.preserve();
    }
} // end class SimpleCmd


The generated code above will invoke the set command and then evaluate inlined if command logic. The inlined if command will evaluate the {($i % 2) == 0} expression into a boolean value and save it in the local variable tmp4. The expression evaluation invokes the binary operators % and ==. The tmp6 local is checked in a Java if statement and then the {puts even} command is invoked. The method then returns normally.

In the examples above, a Tcl command name is resolved to a Tcl command implementation each time the command is invoked. TJC compiled commands are able to cache this command lookup so that a command need not be resolved each time it is invoked. The next example demonstrates how the command cache is implemented. Delete the generated JAR files and the directory and modify the simple.tcl file as follows:

$ cat simple.tcl
proc simple {} {
    cmd1
    cmd2
    cmd1
}

The TJC module file simple.tjc is defined as:

$ cat simple.tjc
PACKAGE simple

SOURCE simple.tcl
INIT_SOURCE simple.tcl

OPTIONS +inline-containers +inline-controls +cache-commands

Recompiling and extracting the generated file results in:

$ cat simplesrc/simple/SimpleCmd.java
// TJC implementation of procedure simple
package simple;
import tcl.lang.*;

public class SimpleCmd extends TJC.CompiledCommand {
    public void cmdProc(
        Interp interp,
        TclObject[] objv)
            throws TclException
    {
        if (!initCmd) { initCmd(interp); }
        CallFrame callFrame = TJC.pushLocalCallFrame(interp, wcmd.ns);
        try {
        if (objv.length != 1) {
            throw new TclNumArgsException(interp, 1, objv, "");
        }
        { // Invoke: cmd1
            TclObject[] objv0 = TJC.grabObjv(interp, 1);
            try {
                TclObject tmp1;
                // Arg 0 constant: cmd1
                tmp1 = const0;
                tmp1.preserve();
                objv0[0] = tmp1;
                if ( wcmd_cmdEpoch != wcmd.cmdEpoch ) {
                    updateCmdCache(interp, 0);
                }
                TJC.invoke(interp, ((cmdcache1_cmdEpoch == cmdcache1.cmdEpoch)
                    ? cmdcache1.cmd : null), objv0, 0);
                if ( cmdcache1_cmdEpoch != cmdcache1.cmdEpoch ) {
                    updateCmdCache(interp, 1);
                }
            } finally {
                TJC.releaseObjv(interp, objv0, 1);
            }
        } // End Invoke: cmd1
        { // Invoke: cmd2
            TclObject[] objv2 = TJC.grabObjv(interp, 1);
            try {
                TclObject tmp3;
                // Arg 0 constant: cmd2
                tmp3 = const1;
                tmp3.preserve();
                objv2[0] = tmp3;
                if ( wcmd_cmdEpoch != wcmd.cmdEpoch ) {
                    updateCmdCache(interp, 0);
                }
                TJC.invoke(interp, ((cmdcache2_cmdEpoch == cmdcache2.cmdEpoch)
                    ? cmdcache2.cmd : null), objv2, 0);
                if ( cmdcache2_cmdEpoch != cmdcache2.cmdEpoch ) {
                    updateCmdCache(interp, 2);
                }
            } finally {
                TJC.releaseObjv(interp, objv2, 1);
            }
        } // End Invoke: cmd2
        { // Invoke: cmd1
            TclObject[] objv4 = TJC.grabObjv(interp, 1);
            try {
                TclObject tmp5;
                // Arg 0 constant: cmd1
                tmp5 = const0;
                tmp5.preserve();
                objv4[0] = tmp5;
                if ( wcmd_cmdEpoch != wcmd.cmdEpoch ) {
                    updateCmdCache(interp, 0);
                }
                TJC.invoke(interp, ((cmdcache1_cmdEpoch == cmdcache1.cmdEpoch)
                    ? cmdcache1.cmd : null), objv4, 0);
                if ( cmdcache1_cmdEpoch != cmdcache1.cmdEpoch ) {
                    updateCmdCache(interp, 1);
                }
            } finally {
                TJC.releaseObjv(interp, objv4, 1);
            }
        } // End Invoke: cmd1
        } catch (TclException te) {
            TJC.checkTclException(interp, te, "simple");
        } finally {
            TJC.popLocalCallFrame(interp, callFrame);
        }
    }

    TclObject const0;
    TclObject const1;

    protected void initConstants(Interp interp) throws TclException {
        const0 = TclString.newInstance("cmd1");
        const0.preserve(); const0.preserve();
        const1 = TclString.newInstance("cmd2");
        const1.preserve(); const1.preserve();
    }

    int wcmd_cmdEpoch = 0;
    WrappedCommand cmdcache1;
    int cmdcache1_cmdEpoch;
    WrappedCommand cmdcache2;
    int cmdcache2_cmdEpoch;

    void updateCmdCache(Interp interp, int cacheId) throws TclException {
        String cmdName;
        switch ( cacheId ) {
            case 0: {
                cmdcache1 = TJC.INVALID_COMMAND_CACHE;
                cmdcache1_cmdEpoch = 0;
                cmdcache2 = TJC.INVALID_COMMAND_CACHE;
                cmdcache2_cmdEpoch = 0;
                wcmd_cmdEpoch = wcmd.cmdEpoch;
                return;
            }
            case 1: {
                cmdName = "cmd1";
                break;
            }
            case 2: {
                cmdName = "cmd2";
                break;
            }
            default: {
                throw new TclRuntimeError("default: cacheId " + cacheId);
            }
        }
        WrappedCommand lwcmd = TJC.resolveCmd(interp, cmdName);
        int cmdEpoch;
        if ( lwcmd == null ) {
            lwcmd = TJC.INVALID_COMMAND_CACHE;
            cmdEpoch = 0;
        } else {
            cmdEpoch = lwcmd.cmdEpoch;
        }
        switch ( cacheId ) {
            case 1: {
                cmdcache1 = lwcmd;
                cmdcache1_cmdEpoch = cmdEpoch;
                break;
            }
            case 2: {
                cmdcache2 = lwcmd;
                cmdcache2_cmdEpoch = cmdEpoch;
                break;
            }
        }
    }
} // end class SimpleCmd


The generated code above will invoke the Tcl command cmd1 and then save a cached reference to the command. The command cmd2 is invoked and then the command cmd1 is invoked a second time. The second invocation of cmd1 will execute more quickly than the first since it will make use of the cached command reference saved in the cmdcache1 variable. The simple command then returns normally. If the simple command were invoked again, all three command invocations would make use of a cached command reference. Much of code related to the updateCmdCache() method is needed to update or flush the command cache when a Tcl command is added, renamed, or deleted.

The next example enables all optimizations including variable cache and command inline.

$ cat simple.tcl
proc simple {} {
    global foobar
    set foobar 10
    set count 0
    foreach var {1 2 3 4} {
        set foobar $var
        incr count
    }
    return [list $count $foobar]
}

The TJC module file simple.tjc is defined as:

$ cat simple.tjc
PACKAGE simple

SOURCE simple.tcl
INIT_SOURCE simple.tcl

OPTIONS +O

Recompiling and extracting the generated file results in:

$ cat simplesrc/simple/SimpleCmd.java
// TJC implementation of procedure simple
package simple;
import tcl.lang.*;

public class SimpleCmd extends TJC.CompiledCommand {
    public void cmdProc(
        Interp interp,
        TclObject[] objv)
            throws TclException
    {
        if (!initCmd) {
            inlineCmds = true;
            initCmd(interp);
        }
        CallFrame callFrame = TJC.pushLocalCallFrame(interp, wcmd.ns);
        try {
        if (objv.length != 1) {
            throw new TclNumArgsException(interp, 1, objv, "");
        }
        { // Invoke: global foobar
            interp.resetResult();
            TJC.makeGlobalLinkVar(interp, "foobar", "foobar");
        } // End Invoke: global
        { // Invoke: set foobar 10
            TclObject tmp0;
            tmp0 = setVarScalar(interp, "foobar", const0, 0, varcache1, 1);
            interp.setResult(tmp0);
        } // End Invoke: set
        { // Invoke: set count 0
            TclObject tmp1;
            tmp1 = setVarScalar(interp, "count", const1, 0, varcache2, 2);
            interp.setResult(tmp1);
        } // End Invoke: set
        { // Invoke: foreach var {1 2 3 4} ...
            TclObject tmp2 = null;
            try {
                tmp2 = const2;
                tmp2.preserve();
                final int tmp2_length = TclList.getLength(interp, tmp2);

                for ( int index3 = 0 ; index3 < tmp2_length ; index3++ ) {
                    TclObject tmp4 = TclList.index(interp, tmp2, index3);
                    try {
                        setVarScalar(interp, "var", tmp4, 0, varcache3, 3);
                    } catch (TclException ex) {
                        TJC.foreachVarErr(interp, "var");
                    }

                    try {
                    if ( false ) { throw (TclException) null; }
                    { // Invoke: set foobar $var
                        TclObject tmp5;
                        tmp5 = getVarScalar(interp, "var", 0, varcache3, 3);
                        tmp5 = setVarScalar(interp, "foobar", tmp5, 0, varcache1, 1);
                        interp.setResult(tmp5);
                    } // End Invoke: set
                    { // Invoke: incr count
                        TclObject tmp6 = incrVarScalar(interp, "count", 1, 0, varcache2, 2);
                        interp.setResult(tmp6);
                    } // End Invoke: incr
                    } catch (TclException ex) {
                        int type = ex.getCompletionCode();
                        if (type == TCL.BREAK) {
                            break;
                        } else if (type == TCL.CONTINUE) {
                            continue;
                        } else {
                            throw ex;
                        }
                    }
                }
                interp.resetResult();
            } finally {
                if ( tmp2 != null ) {
                    tmp2.release();
                }
            }
        } // End Invoke: foreach
        { // Invoke: return [...]
            { // Invoke: list $count $foobar
                TclObject tmp7 = TclList.newInstance();
                TclObject tmp8;
                try {
                    tmp8 = getVarScalar(interp, "count", 0, varcache2, 2);
                    TclList.append(interp, tmp7, tmp8);
                    tmp8 = getVarScalar(interp, "foobar", 0, varcache1, 1);
                    TclList.append(interp, tmp7, tmp8);
                } catch (TclException ex) {
                    tmp7.release();
                    throw ex;
                }
                interp.setResult(tmp7);
            } // End Invoke: list
            if ( true ) { return; }
        } // End Invoke: return
        } catch (TclException te) {
            TJC.checkTclException(interp, te, "simple");
        } finally {
            TJC.popLocalCallFrame(interp, callFrame);
            updateVarCache(interp, 0);
        }
    }

    TclObject const0;
    TclObject const1;
    TclObject const2;

    protected void initConstants(Interp interp) throws TclException {
        const0 = TclInteger.newInstance(10);
        const0.preserve(); const0.preserve();
        const1 = TclInteger.newInstance(0);
        const1.preserve(); const1.preserve();
        const2 = TclString.newInstance("1 2 3 4");
        const2.preserve(); const2.preserve();
    }

    Var varcache1 = null;
    Var varcache2 = null;
    Var varcache3 = null;

    protected
    Var updateVarCache(
        Interp interp,
        int cacheId)
    {
        String part1;
        String part2 = null;
        int flags = 0;
        Var lvar;

        switch ( cacheId ) {
            case 0: {
                varcache1 = null;
                varcache2 = null;
                varcache3 = null;
                return null;
            }
            case 1: {
                part1 = "foobar";
                break;
            }
            case 2: {
                part1 = "count";
                break;
            }
            case 3: {
                part1 = "var";
                break;
            }
            default: {
                throw new TclRuntimeError("default: cacheId " + cacheId);
            }
        }

        lvar = TJC.resolveVarScalar(interp, part1, flags);

        switch ( cacheId ) {
            case 1: {
                varcache1 = lvar;
                break;
            }
            case 2: {
                varcache2 = lvar;
                break;
            }
            case 3: {
                varcache3 = lvar;
                break;
            }
        }
        return lvar;
    }
} // end class SimpleCmd