TJC Package Manual

Usage:
package require TJC

TJC::package package

TJC::command procname classname
TJC::compile cmd

TJC is implemented as part of Jacl and includes runtime support for TJC compiled procedures. The TJC package is loaded into Jacl via the following Tcl command:

% package require TJC
1.0

The TJC package defines the TJC namespace and adds three Tcl commands to the TJC namespace. The TJC::package command is used to load a TJC compiled package into the Jacl interpreter. Loading a TJC compiled package will source each of the Tcl scripts included in the package and define each of the compiled Tcl commands included in the package. In the following example, a TJC compiled package named foo.bar.baz is loaded.

% TJC::package foo.bar.baz

A TJC package is initialized by loading a class named TJCExtension from the named package. This class implements the standard Extension API for Jacl extensions, so one could also load a TJC package via the java::load command as follows:

% java::load foo.bar.baz.TJCExtension

The benefit of the TJC::package command is that it is simpler and it can be used even when the java package has not been loaded into Jacl.

The TJC::command Tcl command is normally used only by the internal implementation of TJC. End users will not need to invoke the this command. The command is used to associate a Tcl command name with a compiled Java class file. When a Tcl script is parsed, it is scanned for proc declaration that can be compiled. The code for compiled proc declarations is then replaced with a call to TJC::command.

Original Tcl Script:
    set i 0
    proc hello {} { return "Hello World" }

Modified Tcl Script:
    set i 0
    TJC::command hello foo.bar.baz.HelloCmd

At runtime, a [TJC::package foo.bar.baz] command would cause the modified Tcl script to be evaluated. The modified Tcl script would define the hello command using the compiled implementation in the Java class named HelloCmd.

Compiling Tcl procs at runtime

Support for compiling Tcl procedures at runtime is included as part of the TJC package. Using TJC::compile, one can compile a Tcl proc into Java source code and then into Java byte code. The Java byte code implementation of the command will then be loaded into the interpreter. Note that this runtime compilation is not started by default and there is no means to automatically compile all Tcl procs that are defined in the interpreter. The user needs to explicitly indicate which procs should be compiled after the procs have been defined. The most simple usage is the following.

package require TJC

proc hello {} {
    return "Hello World"
}

TJC::compile hello

The above invocation will start a second thread to do the compilation. When the command has been compiled, the Tcl proc hello will be replaced with a compiled version that performs the same operations as the Tcl proc. Most users will only ever need to use the TJC::compile command as shown above.

In the example above, the user has no way of knowing when the compile job is finished. If the user needs to be informed when the compile job is done, the TJC::compile command supports notification via a callback or via a variable. The following shows how a callback can be invoked when a compile job is done.

package require TJC

proc hello {} {
    return "Hello World"
}

proc my_hello_ready { status cmd msg } {
    if {$status == "OK"} {
        puts "command \"$cmd\" was compiled"
    } else {
        puts "command \"$cmd\" could not be compiled: $msg"
    }
}

TJC::compile hello -readycmd my_hello_ready

(after waiting a moment, the following should be printed)
command "::hello" was compiled

In the example above, the callback is invoked with a status string that can be "OK" or "ERROR". The cmd argument is the fully qualified name of the compiled command. The msg argument is a string indicating a compiler error if status is not "OK".

In following example, notification is implemented with a variable. The example sets a variable when the command has been compiled and installed. The value that the ready variable is set to is the same as the arguments to the -readycmd command.

package require TJC

proc hello {} {
    return "Hello World"
}

TJC::compile hello -readyvar my_hello_ready
vwait my_hello_ready
puts $my_hello_ready

(after a moment, the following should be printed)
OK ::hello {}


Compiling Java classes at runtime

TJC::command can also be used to compile source code for a Java class into byte code at runtime. This functionality makes it easy to add dynamic generation of Java code to an application. It can be extremely powerfull when used with the java package. The following example shows how a static Java method can be compiled and invoked.

package require TJC
package require java

set jclass mytest.dynamic.Test1
set jsrc {
package mytest.dynamic;

public class Test1 {
    public static String call() {
        return "CALLED";
    }
}
}

set jinfo [list $jclass $jsrc]
TJC::compile -java $jinfo -readyvar my_class_ready

vwait my_class_ready
puts $my_class_ready

(after a moment, the following should be printed)
OK mytest.dynamic.Test1 {}


(since the class has been loaded, its methods can be invoked)
java::call mytest.dynamic.Test1 call
CALLED


Note that when -java is passed to TJC::command, either a -readyvar or -readycmd argument pair must also be passed. One or more classes generated from the Java class declaration are loaded into the current class loader. Be aware that the class loader can only load a given Java class once. Redefining or unloading a class is not supported. Also note that when a -readycmd or -readyvar notification is done with a -java argument, the Java classes that were loaded are passed in place of the cmd element.