Java Code Compilation with JavaCode[]
JavaTools provides a couple of very powerful Java code compilation and debugging features. Although it cannot compare with the feature set of an IDE with all its bells and whistles, the Java code compilation features of JavaTools include the most important basic features directly from Mathematica:
◆ complete compilation of correct Java code
◆ immediate availability of the runnable Java code
◆ showing the programmer where the error is in case of non-compiling code: line numbers, arrows, and error types
◆ basic syntax highlighting
JavaCode[] makes it possible to write a (Mathematica) string with the code of a user-defined Java method and then use that Java method directly with a new symbol from Mathematica! (“auto symbol-loading”) This means the user can harness powerful Java technology directly from Mathematica, without the need of an external compiler or the creation of .class or .jar files and without the need to load external classes or libraries with JLink. After successful compilation Mathematica knows a new symbol that has the same name as the method name.
JavaTools supports any static Java method. But the method must be static and cannot depend on any imports, i. e. all classes must use the full class name. Instantiations within the static method are possible without limitations, see the examples below.
The string passed to JavaCode[] is simply the Java code of the method that contains the algorithm you want to implement in Java. This is PLAIN Java code, as you would write it in a Java IDE.
Alternatively, JavaTools supports a mechanism by which the Java code can be typed or pasted into a Mathematica cell that was turned into a “Java code cell” by typing JavaCell. For examples, see below.
Using JavaCode[] Directly
The following example creates a 1024 bit-strength DSA public/private key pair and signs a local file with a signature derived from the private key and outputs the signature, the encoded form of the public key, and the base of the public key.
In[23]:=
Using a Java Cell with JavaCell
You can create a JavaTools Java cell simply by typing JavaCell:
Upon submitting Shift+Enter the cell will be replaced by a new cell that we refer to as a “Java code cell” within JavaTools:
Enter Java code for a static Java method here.
You can now type the Java code for your static method directly in that cell:
final int add200(final int number){return 200+number;}
Upon submitting Shift+Enter JavaCode[] is called with the user’s code for the Java method, it is compiled (if no errors are detected), and if it contained no errors (compilation succeeds), a new symbol is set up that can be used directly, as with JavaCode[]:
In[21]:=
Out[21]=
The Java cell provides basic syntax highlighting and bracket/brace/parenthesis-matching. Reserved keywords are in magenta, numbers are in blue, and identifiers are in black (like in Eclipse), bracket/brace/parenthesis-matching happens by showing the matching bracket/brace/parenthesis in red. In the following example the cursor “stands” on the right parenthesis, note that this parenthesis and its matching left parenthesis are both in red:
final int add300(final int number){return 300+number;}
The syntax highlighting happens “live” as you type/edit the Java cell:
final int add400(final int num
You can copy/paste a Mathematica string with Java code into a Java cell. The following example shows the DSAsolution code from above, copy/pasted into a Java cell:
final String[] DSAsolution(String filename) throws Exception
{
java.security.KeyPairGenerator kpg = java.security.KeyPairGenerator.getInstance("DSA");
java.security.SecureRandom random = java.security.SecureRandom.getInstance("SHA1PRNG", "SUN");
kpg.initialize(1024, random);
java.security.KeyPair kp = kpg.genKeyPair();
java.security.KeyFactory fact = java.security.KeyFactory.getInstance("DSA");
java.security.spec.DSAPublicKeySpec pub = fact.getKeySpec(kp.getPublic(),java.security.spec.DSAPublicKeySpec.class);
java.security.PrivateKey privateKey = kp.getPrivate();
java.security.Signature dsa = java.security.Signature.getInstance("SHA1withDSA", "SUN");
dsa.initSign(privateKey);
java.io.FileInputStream fis = new java.io.FileInputStream(filename);
java.io.BufferedInputStream bufin = new java.io.BufferedInputStream(fis);
byte[] buffer = new byte[1024];
int len;
while ((len = bufin.read(buffer)) >= 0) {
dsa.update(buffer, 0, len);
};
bufin.close();
byte[] realSig = dsa.sign();
String[] returns=new String[3];
returns[0]=new String(realSig);
returns[1]=new String (kp.getPublic().getEncoded());
returns[2]=pub.getG().toString();
return returns;
}
Special thanks to Leonid Shifrin for providing the syntax highlighter to JavaTools!
Further Examples
Strings in the Java method code have to be escaped, as JavaCode[] needs a valid string to compile:
In[24]:=
If you have an error in your Java code that prevents compilation, JavaTools will pop up a dialog box informing you about the error. The dialog box shows all errors found, their line numbers, and the error types. Note the stray “n” character at the end of “return”:
As mentioned earlier, the use of JavaTools[] is not limited to math-arithmetic / numeric functions (this is, in fact, a major advantage over the Mathematica compiler, including its C compilation, see the discussion below). Any Java method that can be written as a static method can be used, including visual (Swing) classes, or others that require object instantiations (see first example on this page). Here we create a modal dialog box with a pull-down menu to let the user make a selection and return that selection, with just a few lines of Java code:
In[25]:=
The code HAS to be correct and compile fully through as Java code, or JavaCode[] will not set up a Mathematica symbol for the method name. Bear in mind, the string passed to JavaCode[] is ACTUAL Java code that is compiled and executed! Whatever you write in the Java method, it will execute on the JVM, and therefore has to compile correctly.
One could now even use Mathematica's advanced string manipulation functions to construct the string of Java code. For example, in the previous example the list of game choices could be a Mathematica string, or be computed as a Mathematica string, based on other functions written in Mathematica.
The following creates Java Swing code for four different Java Swing dialog box types and sets up the corresponding Mathematica symbols by mapping JavaCode[] over a list of Mathematica strings representing the dialog box message types:
In the following example we define the first 8 Chebyshev polynomials, which are defined as
and store them in Java as “indexed functions” or “associative arrays” with an integer index.
But C is so much faster
Since the introduction of C code compilation in version 8 many people believe that they could simply compile their Mathematica codes to C and get the fastest possible code execution and wouldn't have to worry about anything. This is generally not true. The author of JavaTools sees the following problems with automated Mathematica-to-C-code conversion:
◆ The Mathematica-to-C-code conversion requires an external compiler installed. Without an external compiler this is not possible.
◆ It's not possible to install a compiler on all machines. There can be technical restrictions or company policies barring the installation of an external C compiler. Thus it can be a deployment problem.
◆ C code is platform-dependent. This can be a deployment problem.
◆ SELinux may prevent the compilation and/or C code execution. Wolfram Research does not provide policy modules for SELinux.
◆ Mathematica-to-C-code conversion uses LibraryLink, which is not a stable link. Unlike MathLink, a bug/crash in ANY of the libraries will automatically crash the WHOLE kernel, not just the affected library or link. This creates a dependency and stability problem. Even minor problems in an external library can take down the whole kernel, along with all other open links (e. g. database links or web services links). In other words, LibraryLink is not resilient.
◆ Some Mathematica codes don't fully compile to C code although the programmer thinks they do. For example, when an If-statement is used that doesn't have a then-clause, the Mathematica-to-C-code conversion includes a statement that calls back to the Mathematica kernel, which is totally unnecessary. This can only be found by inspecting the machine code statements with CompilePrint. A seemingly simple If-statement in Mathematica makes unnecessary calls back to the kernel that should not happen and can only by found with CompilePrint.
Java code exeution, managed from Mathematica, however, provides the following advantages:
◆ Java is a managed environment. Unlike C code, Java code executes in a virtual machine which provides many advantages over raw machine code execution, such as stability, user-defined runtime options, memory management, garbage-collection, error-handling, array bounds checking, etc. C has no garbage collecctor and no array bounds checking.
◆ Java allows the user to configure the virtual machine with runtime options, for example for memory usage, method-inlining, aggressive compilation, etc. There is no equivalent for that with C or C# code (the .Net runtime can NOT be configured by the user).
◆ Although Java code executes in the virtual machine, a modern HotSpot VM automatically compiles the interpreted bytecode into machine code when it notices that a certain code branch is executed frequently (is "hot", hence the name HotSpot Virtual Machine). This is called "warm-up". Now the method executes with machine code speed. In addition, when parameters in the VM change over time (memory, other functions getting compiled to machine code, etc.) a HotSpot VM automatically recompiles the code to new machine code. This feature is called dynamic code (re)compilation. Unlike C or C# code, which is statically compiled (only once, for ever), a HotSpot VM recompiles the code occasionally as system and runtime parameters change. In fact, this can even happen WHILE a method executes, this is called OSR technology (On-Stack Replacement). This means due to dynamic code recompilation the machine code is dynamically adapted to the changing environment of the system. http://en.wikipedia.org/wiki/Dynamic_recompilation
◆ The slight speed disadvantage that Java still has compared to C code is getting slimmer and slimmer. As the Java VM matures, Java code automatically gets faster. Java 7 is slated for release later in 2011, and contains MAJOR speed enhancements. In general, Java is dynamically evolving, is, in part, open-source, and includes features and innovation from the world-wide Java community.
◆ The entire Java technology platform is at the programmer's disposal when using JavaCode[]. This includes, for example, the concurrency framework, the security framework, the cryptography framework, Swing (see example above), and many others. Such platforms are not available through the Mathematica-to-C-code conversion mechanism. The author of JavaTools has written several advanced applications that harness the concurrency framework of Java to accelerate numeric computations. These applications scale with the number of cores present on the machine on which JavaCode[] is executing according to Amdahl's Law. The corresponding parallel versions written for C are still a bit faster, but due to the stability and independence reasons mentioned above the author of JavaTools prefers to execute parallelized code with the Java concurrency framework, instead of parallel threads in C.
For a more detailed discussion of the advantages and limitations of the Mathematica compiler (including C compilation), the reader is referred to
http://mathematica.stackexchange.com/questions/1803/how-to-compile-effectively
Quoting in particular an incomplete list of limitations posted by Leonid Shifrin (thanks Leonid for your thorough top-level view):
◆ Can only accept regular arrays (tensors) of numerical or boolean types. This excludes ragged arrays and more general Mathematica expressions.
◆ In most cases, can only return a single tensor of some type
◆ Only machine-precision arithmetic
◆ From the user-defined functions, only pure functions are compilable, plus one can inline other compiled functions. Rules and “functions” defined with rules are inherently not compilable.
◆ No way to create functions with memory (a-la static variables in C)
◆ Only a small subset of built-in functions can be compiled to byte-code (or C)
◆ Possibilities for writing recursive compiled functions seem to be very limited, and most interesting cases seem to be ruled out
◆ No decent pass-by-reference semantics, which is a big deal (to me anyways)
◆ You can not really use indexed variables in Compile, although it may appear that you can.
◆ ...
The author of JavaTools strongly considers ragged arrays, functions with memory, recursion, pass-by-reference, index variables (sometimes called “associative arrays”, for example in perl) very important programming tools. The Mathematica compiler does not support them.
The author of JavaTools invites comments on this matter at info@lauschkeconsulting.com.