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