JPE User Guide.

Grad-Soft Ltd, Kiev. Ukraine.

http://www.gradsoft.ua



Table of Contents

Introduction

Invoking JPE

Command Line

Integration with Ant.

Optimizations.

Devirtualization.

Unreachable code elimination.

Document attributes.


Introduction


JPE is a tool for partial evaluation for java source code. Primary usage is a specialization of codebase (especially in J2ME area): we can look at the partial evaluator as 'the right way' to do preprocessing. Another usage is applying some set of optimization techniques, such as devirtualization of method calls or elimination of unused code.


So, let we have some program, which must exists in few different versions with slightly different behavior. For example when must exists few versions of the same program for different hardware platforms or must exists demo and production versions.


Usual approach is to maintain one codebase for all versions and perform conditional compilation with some form of preprocessing and file substitutions. This approach have few drawbacks, among them:


Using of JPE can greatly simplicity maintenance of such software by using Java source transformations instead another language preprocessing. How it works: user must describe set of constants, from which versions are depended as special class. (For example, name one 'CompileTimeConstants', than use this constants for performing conditional behavior in usual java statements. Than call java partial evaluator, pass to one which will eliminate all compile time constants in program and perform symbolic simplification of received code. So, it is possible to develop and debug generalized version of program, but deploy and distribute specialized version.


One simple example: let we have generate two java files:


Main.java:


package com.mycompany.test;
import static com.mycompany.test.CompileTimeConstants;

public class Main
{
public static void main(String[] args)
{
if (DEBUG) {
System.out.println(“Enter main”);
}
System.out.println(“My program, version “+VERSION);
if (DEBUG) {
System.out.println(“That's all”);
}
}
}

and CompileTimeConstants.java:




package test;

public class CompileTimeConstants
{
public static final boolean DEBUG = true;
public static final double VERSION = 3.1415926;
}

which situated in test subdirectory of some input directory.


After processing those directory with JPE with values, for example: DEBUG=true, VERSION=2.7186, in test subdirectory of output directory we will have:


Main.java:


package com.mycompany.test;

public class Main
{
public static void main(String[] args)
{
{
java.lang.System.out.println(“Enter main”);
}
java.lang.System.out.println(“My program, version 2.7186“);
{
java.lang.System.out.println(“That's all”);
}
}
}

and in CompileTimeConstants.java we will have:


package test;

public class CompileTimeConstants
{
public static final boolean DEBUG = true;
public static final double VERSION = 2.7186;
}

Now, if we will rerun JPE with values DEBUG=false, VERSION=1.1 then we receive:


Main.java:



 
package com.mycompany.test;

public class Main
{
public static void main(String[] args)
{
java.lang.System.out.println(“My program, version 1.1“);
}
}

CompileTimeConstants.java:


package test;

public class CompileTimeConstants
{
public static final boolean DEBUG = false;
public static final double VERSION = 1.1;
}

Additionally we can invoke global optimization, such as devirtualization and reachability analysis. In such case CompileTimeConstants.java will be eliminated as unreachable, Main will looks like:

package com.mycompany.test;

public final class Main
{
public static void main(String[] args)
{
java.lang.System.out.println(“My program, version 1.1“);
}
}

Invoking JPE


JPE can be invoked from command line or ant script. All requirements to programming environment: use JDK-1.6 or later.

Command Line


JPE can be invoked from command line with main class ua.gradsoft.jpe.Main , classpath from jars in lib subdirectory of distribution and next options:


--input-dir <argument>

Input directory to process.

--include-dir <argument>

Sources, from which processed sources are depend, are situated in directory <argument>. This option can be specified multiple times.

--include-jar <argument>

Specify <argument> as jar archive, where classes, from which processed sources are depend. This option can be specified multiple times.

--output-dir <argument>

Specify <argument> as directory, where result will be placed.

--jpehome <argument>

Specify <argument> as directory, where jpe is installed.

--ct-class <argument>

Specify <argument> as name of class, where compile-time constants are defined.

--value-pair <name> <value>

Specify <name> and <value> as name and value of compile time constant. In c-class must be defined field with name <name>. Value must be java literal. Note, that in demo version of JPE only two value-pair arguments can be handled.

--disable-class <classname>

Instruct JPE does not perform symbolic simplifications in class <classname>

--create-output-dir

Instruct JPE to create output dir if needed.

--silent

Be silent. Disabled in demo version.

--debugLevel <level>

Set debugLevel to <level>. Level must be integer from one to 10. Disabled in demo version.

--dump

Dump terms of input and transformed sources. (Disabled in demo version)

--devirtualization-opts

Devirtualization options. Will be described later in chapter about optimzations.

--elimination-opts

Options for code elimination. Will be described later




Integration with Ant.



It is possible to use JPE from ant. At first, we need to define task:



<taskdef name="jpe" classname="ua.gradsoft.jpe.ant.JPETask">
<classpath>
<fileset dir=”${jpehome}/lib” includes=”**/*.jar” />
</classpath>
</taskdef>

then we can use task with the following attributes:

And following nested elements:


Example: transformation for demo-sources of JPE itself:


    <target name="build-demo-sources">
<jpe jpehome="${jpehome}"
input="src"
output="output/jpe-demo"
createOutputDir="true"
>
<includejars>
<pathelement location="${jpehome}/lib/TermWare2.jar" />
<pathelement location="${jpehome}/lib/JavaChecker2.jar" />
<pathelement location="${jpehome}/lib/JavaChecker2Annotations.jar" />
<pathelement location="${jpehome}/lib/TermWareJPP.jar" />
<pathelement location="${jpehome}/lib/ant.jar" />
</includejars>
<ctconstant name="DEMO" value="true" />
<devirtualization enabled="true" />
<eliminateUnreachable enabled="true" >
<reachable classnamepattern="ua.gradsoft.jpe.Main"/>
</eliminateUnreachable>
</jpe>
</target>

Optimizations.


Devirtualization.


Idea of method calls devirtualization is substitution of virtual method calls by non-virtual when program analysis shows us, that concrete type of method is statically known. In some cases such transformation can cause significant speedup of program execution. One of simple devirtualization strategies is just mark as final all classes, which have no descendants. Availability of descendants can be checked by global source analysis. Note, that such optimization can not be performed by compiler or VM, because compiler does not do global analysis (i.e. does not know, that other classes, than in classpath will be called and so on)


Devirtualization parameters can be set from the command line:



Or from ant script via nested element devirtualization with


ie. next target:


<target name=”build-dv-sources”>
<jpe input=”src” output=”generated-1”>
<ctconstant name=”A” value=”1” />
<devirtualization enabled=”true”>
<except classnamepattern=”well.known.plagin.base.*”/>
</devirtualization>
</jpe>
</target>

will cause patial evaluation of text from src into

Unreachable code elimination.

Idea of method is walking from some initial set of entry classes and mark all reachable classes. All other classes can be safety deleted from our system, because execution will never reach ones. Note, that this again is a global optimization (ie compiler can't do this) and that some classes, reachable in original version of program can become unreachable after partial evaluation. (For example, if in examples from Introduction chapter mark Main as reachable, that CompileTimeConstant class will be completely eliminated). Also we need to point system on classes, which are reachable only from reflection or some external API.

Also unused private methods and fields inside classes can be eliminated. I. e. let we have next class:

class A {
public void a()
{
if (CompileTimeConstants.X) {
b();
}else{
c();
}
}
private void b() {
System.out.println(“b”);
}
private void c() {
System.out.println(“c”);
}
}

then after passing jpe with X=1 and unreachable code elimination we will have:

class A {
public void a()
{
b();
}
private void b() {
System.out.println(“b”);
}
}


Elimination parameters, as usual, can be set from command line or form ant script.

From command line:

From ant script:

Document attributes.

Organization: Grad-Soft Ltd, Kiev, Ukraine. http://www.gradsoft.ua

Author: Ruslan Shevchenko Ruslan@Shevchenko.Kiev.UA

Version: 1.1.0

History of changes:

24.07.2010 version 1.1.0

05.10.2007 version 1.0.0