Javarifier - Reference Immutability Inference Tool for Javari

Developed by the Javari team (javari@csail.mit.edu).

Contents:

Motivation: Inferring reference immutability in Java programs

Javarifier infers the immutability (according to the definition of the Javari language) of every reference in a Java program. In other words, it converts Java programs and libraries to Javari. It annotates source or .class format with backward-compatible comments or attributes.

Annotating programs aids developers in reasoning about the code and modifying it without introducing subtle mutation errors. Annotating libraries is important because Javari programs use libraries without Javari annotations often do not typecheck. Manually determining the mutability for each (public) parameter and return type is tedious and error-prone. Javarifier automatically performs this analysis.

Javarifier

Javarifier reads a set of Java classes to analyze (in .class format) and infers the (im)mutability of every reference. It outputs the inferred mutabilities in an annotation file, a textual file format for describing annotations of Java programs; the input class files are not modified. These annotations can be used directly, or inserted into either the .class files or the corresponding .java files using the annotation file utilities. If Javari annotations are inserted into the source code, they can be checked by the Javari type checker.

In earlier versions of Javari, methods must be invariant in the mutability of their parameters and return types. The newer definition of Javari used by both Javarifier and the Javari type checker permits covariant subtyping on parameters and contra-variant subtyping on return types.

Inference using existing Javari classes and stubs

Javarifier respects all existing Javari annotations in the program classes it analyzes. If any type is already annotated as @ReadOnly, Javarifier uses this annotation to determine the mutability of other types and does not change the existing @ReadOnly annotation. This mechanism is how Javarifier infers annotations on a program that uses libraries that have already been annotated using stub classes (also known as skeleton classes).

Installation

The following instructions assume either a Linux or Windows command-line environment.

The current distribution is Javarifier version 0.1.3, released April 27, 2008.

  1. Download javarifier.zip.
  2. Unpack the distribution zipfile by running:
      unzip javarifier.zip

    This command creates a directory named javarifier containing:

  3. Add the javarifier directory to your path.

Using Javarifier

The following instructions are for running javarifier on a Linux machine. The instructions are the same for Windows, except the tool is javarifier.bat, and you must use the Windows path names instead of the Linux path names.

The javarifier tool takes as arguments any number of fully-qualified names of the classes to analyze. All of these classes must be in your $CLASSPATH, although this behavior can be overriden with the -programCPEntries option. Here are examples:

javarifier myPackage.myClassOne myPackage.myClassTwo
javarifier myPackage.myClassOne myPackage.myClassTwo
javarifier `find myPackage -type f -name '*.java' | perl -p -e 's/\.java//g; s/\//./g'`

The last command above, if run in the parent directory of the myPackage source code directory, analyzes all classes in the package.

(Just using the default $CLASSPATH may not work, because the JVM may also load classes from other locations, such as the boot class path. An example is the classes in jdk/jre/lib/jce.jar. Therefore, you may need to add such jar files to CLASSPATH, or explicitly mention them on the javarifier command line via the -world option.)

Specifying a classpath

To specify a classpath other than $CLASSPATH for Javarifier to use to lookup classes being analyzed, you can use the -programCPEntries argument and pass in a full classpath. The first two of the following examples are identical.


javarifier myPackage.myClassTwo
javarifier myPackage.myClassTwo -programCPEntries $CLASSPATH
javarifier myPackage.myClassTwo -programCPEntries /path/to/classes1:/path/to/classes2

Outputting to a file

By default, all output, including the results, is printed to standard out. To output the results to an annotation file, use the -output option, followed by the name of the output file. Note that Javarifier outputs the results for all classes into a single file. For example:

javarifier myPackage.myClassOne myPackage.myClassTwo -output myPackage.jaif

Specifying JDK jarfiles

By default, Javarifier assumes that the JDK classes required by the program classes on which you are running Javarifier are in your CLASSPATH. For example, it assumes rt.jar is in your CLASSPATH. If this is not the case, or if you wish to reference a different JDK, you may use the -world option, followed by the classpath of your JDK jarfiles. For example:

javarifier myPackage.myClassOne myPackage.myClassTwo -world /jdk1.7.0/jre/lib/rt.jar:/jdk1.7.0/jre/lib/resources.jar:/jdk1.7.0/jre/lib/all-other-jars.jar

Using stub classes

A stub class (or skeleton class) gives Javari types for an external library. Javarifier comes with annotated versions of part of the JDK, in directory annotated-jdk.

If no stub class is available for a particular class, then Javarifier assumes that every reference in that class has @Mutable type. The -printStubs flag causes Javarifier not to perform inference, but only to output a list of classes for which stubs are needed.

There are two ways to inform Javari of additional stub classes (or fully-annotated Javari classes).

If you create stub versions of additional classes (whether in the JDK or other libraries), please contribute them back to the Javari project (email javari@csail.mit.edu) so that we can include them in future releases.

* Determining which classes need to be stubbed out: In order to determine which classes need to be stubbed out for Javarifier to work correctly,

Understanding and debugging Javarifier output

Javarifier may infer some references to be @Mutable that you believe should be @ReadOnly (or vice versa). This usually indicates a problem with your code such as a bug, a missing @Assignable keyword, or simply that you have misunderstood what your code does. Sometimes, it can indicate a bug in Javarifier.

To understand why Javarifier inferred a particular type qualifier, you can examine the constraints generated by Javarifier during its inference process. Use the -dumpConstraints flag, as in:

javarifier myPackage.myClass -dumpConstraints

The Javarifier constraints are of three types:

An example of a constraint is

  U: <Graph.createGraph.this: UNKNOWN LGraph;[] MUTABLE>

You may find it helpful to cross-reference between Javarifier constraints and the Java source code of the classes being analyzed (from the constraints were generated).

For more details about the constraints, see the paper "Inference of reference immutability". Contact us if you need more help understanding the constraints.

Running Javarifier test suite

A test suite is included with the release. To run the test suite, go into the tests/ directory and run make. The output will indicate whether the tests passed or failed.

Feedback

Please send questions, comments, bug reports and feature requests to javari@csail.mit.edu. When reporting bugs, please help us to resolve the issue quickly by including the complete output of Javarifier, and exact instructions of how to reproduce a bug (including all necessary input files).

Update Log


Last revised: March 10, 2008