Back to Project Pages Home

 

Writing an extension for JAPT (Java optimizer)

 

1 Implement the Extension Interface

2 Using the Message and Logger classes

2.1 Logger Streams

2.2 Message Objects

3 The Japt Repository

3.1 Do not Alter External Classes

3.2 Do not Alter Identifying Characteristics of the Application Interface

4 The Code Attribute

4.1 Inserting, Replacing and Removing Instructions

4.2 Creating Instructions

4.3 Copying Instructions

4.4 Visiting Instructions

5 Running your Extension

4.1 Running from the Command Line

4.2 Running in Java

 

This document covers the basic concepts of Japt from a programmer’s perspective (adding an extension to Japt), rather than a user’s perspective (running existing extensions).

 

Japt is composed of the underlying library JikesBT (com.ibm.jikesbt) and the japt API in the following packages:

com.ibm.ive.tools.japt

com.ibm.ive.tools.japt.commandLine

com.ibm.ive.tools.japt.commandLine.options

com.ibm.ive.tools.commandLine

 

 

Overview of the Extension Architecture

 

Japt is built with an extension architecture.  It is much like a plugin architecture except that extensions are defined on the command line. 

 

The idea is that you should simply worry about writing your bytecode or class manipulation code and you need to do nothing to integrate your code into Japt.  You can use the existing LoadException to load your classes, you can then run your own custom, then you can use the ClassGenerationExtension or JarGenerationExtension to save the altered classes, all of this controlled by the command line.  The existing Japt architecture does not need to know about the existence of your extension, and you can easily pick and choose which existing extensions you need to use with your own.

 

 

1. Implement com.ibm.ive.tools.japt.Extension

 

Extensions if Japt must implement the interface com.ibm.ive.tools.japt.Extension.

 

Additionally, if the extension is to be integrated with the command line version of Japt, the user must implement the interface com.ibm.ive.tools.japt.commandLine.CommandLineExtension.

 

The interface CommandLineExtension has the method getOptions, which provides an array of command line options.  The extension must provide these objects which the command line parser will update as it parses the command line.

 

The key method in the Option interface is the method

protected abstract void handle(String args[], CommandLineParser fromParser) throws CommandLineException;

which is called whenever the given option appears on the command line, along with any arguments present and the parser which found the option.  There is a single parser for the command line as well as a new parser for every options file or rules file.

 

The key method in the Extension class is the method

void execute(JaptRepository repository, Logger logger) throws ExtensionException;

This is the method that will be called by Japt to run your extension.  The JaptRepository object and the Japt Logger object will be passed to your extension so that it can alter the classes in the repository and pass any user information to the logger.

 

 

2. Use the Message and Logger classes

 

Information is directed to the user by calling the appropriate method in com.ibm.ive.tools.japt.Logger.

 

2.1 Logger streams

 

A logger object has five streams:

1. error: an error has occurred

2. warning: something unusual or possibly undesirable has occurred that might not be an error

3. information: something about what Japt or an extension has done

4. status: information that should be presented immediately

4. progress: something regarding the progress of Japt as it runs

 

The logger object decides where each stream will write its information.  One type of logger, the StandardLogger, will write the error stream to the standard error stream and the other three streams to the standard output stream. 

 

The command line japt will generate a CommandLineLogger for use by the extensions.

On the command line, the user may specify a log file for japt for use by this logger.  This logger writes the error stream to the standard error stream.  It writes the warning and progress streams to the standard output stream.  Additionally, it will also write the error, warning and information streams to the log file, if such a file was specified.

 

2.2 Message Objects

 

Information to be passed to the user is generally stored in an object of type com.ibm.ive.tools.japt.Message.  The subclass com.ibm.ive.tools.japt.LogMessage has additional methods named log which cause the message to be written to the given logger. 

 

The message object itself will decide which logger stream(s) will receive the message.  Japt defines a subclass for each Logger stream, the ErrorMessage, InfoMessage, StatusMessage, ProgressMessage and WarningMessage classes, to direct their contents to their corresponding logger streams.  These are all subclasses of JaptMessage, which adds a prefix to each message as it is written to identify the type of message and the component from which it originated.

 

Extension writers are encouraged to use one of the existing JaptMessage subclasses, or to subclass JaptMessage itself, when they are creating their own messages for the user.

Messages with Arguments

 

When a JaptMessage object is logged, it may be passed arguments that are inserted into the message.  JaptMessage objects that take arguments must be constructed with the JaptMessage constructor that takes a FormattedString as an argument.  The FormattedString object will break a message up into components.  Between each component is where each argument will be inserted.

 

 

 3. The Japt Repository

 

The japt repository stores all classes and resources that are loaded by japt.  It also holds the class path entry objects from which more classes and resources may be loaded.  The resources are stored in the resources vector.  The classes are stored in the classes vector.  Additionally, those classes which are internal classes are also stored in the internalClasses vector.

 

3.1 Do Not Alter External Classes

 

One of the rules for writing a japt extension is that only internal classes may be modified.  There are methods in the JaptRepository object that will get all the internal classes or will determine whether a given class is internal or external.

 

3.2 Do not Alter Identifying Characteristics of the Application Interface

 

The application interface consists of a set of classes, methods and fields that are identified as possible entry points into the set of internal classes, see the Japt documentation for details. 

 

The identifying characteristics of these items are the properties that allow them to be identified and accessed.

 

Their names cannot be changed.  Their access permissions can be changed to make them more accessible, but not less accessible.  They cannot be removed or relocated.  They cannot be changed from static to non-static and vice-versa.  Method signatures and field types cannot be changed.  Any other identifying characteristics cannot be changed. 

 

Characteristics that do not identify these items or affect how thet are accessed can be changed, such as whether they are synchronized, fp-strict, or abstract.  Method bodies can be changed.  Any non-identifying characteristic can be changed. 

 

 

4. The Code Attribute

 

The BT_CodeAttribute object for a method stores the bytecode instruction vector and any associated attributes, such as the debug attributes used to debug that method. 

 

4.1 Inserting, Replacing and Removing Instructions

 

When inserting, removing, or replacing instructions in a code attribute, it is recommended that the programmer use the associated code attribute methods that being with “insertInstruction”, “removeInstruction” or “replaceInstruction”.  This ensures that the instructions are linked and unlinked properly, and additionally the associated attributes such as the debug attributes will be updated as well. 

 

If the programmer chooses not to use the methods provided by the code attribute, then whenever an instruction is inserted into a code attribute’s vector, it should have its “link” method called.  Also, whenever an instruction is removed from a code attribute’s instruction vector, it should have its “unlink” method called.   This ensures that any relationships that have been created by the presence of that instruction are updated accordingly.  The unlink and link methods may be called more than once on the same instruction with no adverse effects.  This means that you can add a few instructions to a method and then call link on all instructions the entire method to ensure all instructions have been linked at least once. 

 

Linking and unlinking code, methods, fields and classes in Japt/JikesBT ensures that any data structures containing those artifacts are updated.  For instance, for a method invocation instruction, there is a data structure in the code attribute indicating the called methods from that code, as well as a data structure in the method object listing callers.

 

 

4.2 Creating Instructions

 

When creating instructions, it is recommended that the programmer use the associated “make” methods in BT_Ins.  This ensures that the correct instruction object is created for each opcode.  The instruction object hierarchy is documented in the javadoc comments for the BT_Ins class.

 

4.3 Copying Instructions

 

The ideal way to copy instructions is to use the associated methods in BT_Inliner.  This ensures that the code attributes which reference the instructions are updated properly, and the instructions appropriate rules for creating the new instructions are followed.

 

4.4 Visiting Instructions

 

You may visit the instructions in the same order in which they are executed by creating a BT_CodeVisitor object and passing this object to the associated BT_CodeAttribute.visitReachableCode method.  The code visitor object will generate information regarding which instructions were visited and a list of subroutines in the method.

 

If you subclass BT_CodeVisitor, then there are key methods that you may override in order to analyze the instructions while they are being visited.  There are existing subclasses of BT_CodeVisitor that verify instructions, analyze the operation stack, visit subroutines, and find method invocations on specific objects.

 

 

5 Running your Extension

 

5.1 Running from the Command Line

 

To run your extension from the command line, it must be a subclass of CommandLineExtension.  Running it is easy.  Just use:

 

java –jar japt.jar –extension theExtensionClassName <your extension options>

 

If your extension does not load classes, this means that there will be nothing in the Japt repository for your extension to use.  However, japt automatically runs the built-in load extension first, so you can use this to load classes.  You can then use one of japt’s output extensions to write to the file system:

 

java –jar japt.jar –loadFile inputClasses.jar –extension theExtensionClassName <your extension options> –jarOutput –output outputClasses.jar

 

5.2 Running in Java

 

You may call on Japt from within your java application.  To do so, you need to create a factory, a repository, and an instance of japt to run your extensions.

 

Here is an example:

 

Logger logger = new StandardLogger();

JaptFactory factory = new JaptFactory(logger);

JaptRepository rep = new JaptRepository(factory);

Japt japt = new Japt(rep);

MyExtension extension = new MyExtension();

japt.executeExtensions(new Extension[] {loadExtension, extension,

 outExtension}, logger);

 

Once again, if your extension does not load classes, you may wish to make use of the Japt load extension, and you can use one of the japt output extensions to write the class files to the file system:

 

Logger logger = new StandardLogger();

JaptFactory factory = new JaptFactory(logger);

JaptRepository rep = new JaptRepository(factory);

Japt japt = new Japt(rep);

LoadExtension loadExtension = new LoadExtension();

loadExtension.options.load.add("inputClasses.jar");

MyExtension extension = new MyExtension();

JarGenerationExtension outExtension = new JarGenerationExtension();

outExtension.target.setValue("outputClasses.jar");

japt.executeExtensions(new Extension[] {loadExtension, extension,

 outExtension}, logger);

rep.resetClassPath(); //close open jars in classpath

 

All of the built-in japt extensions have their various options exposed for use in the same way that they are used from the command line.