Section 1 gives an overview of the RIT Classfile Library. Section 2 discusses why I chose to develop yet another Java library for manipulating class files. Section 3 describes how to synthesize a class. Section 4 describes how to synthesize an interface. Section 5 describes how to load synthesized class files into the Java Virtual Machine (JVM).
With the RIT Classfile Library you can analyze an existing class file to discover which class, fields, methods, and so on it contains. While you can get the same information using the Java Reflection API (package java.lang.reflect), the Java Reflection API first requires you to load the class into the JVM. The RIT Classfile Library can analyze a class file without actually loading the class. Also, since it doesnt use reflection, the RIT Classfile Library can be used in Java environments that lack reflection, such as the Java 2 Micro Edition Connected Limited Device Configuration (J2ME CLDC).
Note: The RIT Classfile Librarys analysis capabilities are not yet implemented and will appear in a future version of the Library.
With the RIT Classfile Library you can also synthesize a new class file and load it directly into the JVM. In this way, a running program can create a new class without having to generate a source file and run the Java compiler. Thus, the RIT Classfile Library can be used to create new classes in Java environments that lack a Java compiler and/or a file system, such as the J2ME CLDC.
The original motivation for writing the RIT Classfile Library was to enable the development of remote method invocation systems that generate their own stubs automatically rather than relying on a human to run an offline stub compiler. Javas dynamic proxies (class java.lang.reflect.Proxy) provide a similar capability. However, custom-synthesized stub classes generally execute more quickly than dynamic proxies that use reflection, and hence are more attractive in applications where performance is a concern. Also, the RIT Classfile Library is fully general and can synthesize any class, not just a proxy for an interface.
apology 1 a :
a formal justification :
DEFENSE
Websters New Collegiate Dictionary |
Im aware of half a dozen or so programs and APIs with capabilities similar to those of the RIT Classfile Library. Why write another one? Does the world need yet another library for manipulating Java class files?
I wrote the RIT Classfile Library because I wasnt satisfied with the existing programs and libraries for my purpose, synthesizing remote method invocation stubs in small devices with limited (J2ME CLDC) Java environments. Some programs are class file assemblers that take their input from a source file; this wont work in a small device with no file system, and the code for parsing source text, generating error messages, and so on is unnecessary overhead for my purpose. Some libraries are over-general, with beautifully object-oriented designs but quite large and complex class hierarchies; the code footprint of such libraries is problematic for small devices with limited memories. Some libraries tangle analysis and synthesis capabilities together in the same classes, so that its difficult to subset the library to do just one or the other; again, this leads to a larger than necessary code footprint. Some libraries expose the low-level details of the Java class file data structures like the constant pool and the code attributes; I find a high-level API that hides these details easier to work with.
My goals in writing the RIT Classfile Library were
I assume youre familiar with JVM internals. See the References for further information.
To synthesize a class, first create an instance of class SynthesizedClassDescription. A class description is an object that holds information about everything thats in a class. A synthesized class description is a class description where you fill out (synthesize) the class information yourself. (The other kind of class description, an analyzed class description, is a class description where the class information comes from a pre-existing class file. Analysis capabilities are not yet implemented in the RIT Classfile Library.)
Once you have the synthesized class description object, populate it with fields, methods, and whatever other information the class needs. Next, tell the synthesized class description object to emit its binary class file into an output stream, such as a FileOutputStream or a ByteArrayOutputStream. Finally, to use your new class, load it into the JVM as described later.
When synthesizing your class, youll need to refer to other classes and types for example, to specify the superclass of your class, or to specify the data type for a field in your class. A type reference is an object that holds just enough information to refer to some data type. You can use any of several kinds of type reference:
To add a field to your class, create a field description object, which holds all information about the field. You can use any of several kinds of field description:
To add a subroutine to your class, create a subroutine description object, which holds all information about the subroutine. The term subroutine refers collectively to constructors, methods, and class initializers. You can use any of several kinds of subroutine description:
Each subroutine (except an abstract method) needs a sequence of bytecode instructions. You use class Op to obtain each instruction, then you add each instruction to the subroutine description. You can also add instances of class Location to mark the target locations for branch instructions.
Some bytecode instructions refer to fields or methods that exist elsewhere. To refer to a field, use a field reference object. Class NamedFieldReference lets you refer to a field by name. A field description can also be used as a field reference.
To refer to a subroutine, use a subroutine reference object. You can use any of several kinds of subroutine reference:
There are two very important pieces of information you must specify for each subroutine (except an abstract method): max_stack, which gives the maximum number of words the subroutine ever pushes on the operand stack, and max_locals, which gives the number of words needed to hold the subroutines this pointer, arguments, and local variables. If you dont specify the correct max_stack and max_locals, your class may fail verification when loaded, or your subroutine may behave incorrectly when invoked.
To synthesize an interface, first create an instance of class SynthesizedInterfaceDescription. An interface description is an object that holds information about everything thats in an interface. A synthesized interface description is an interface description where you fill out (synthesize) the interface information yourself. (The other kind of interface description, an analyzed interface description, is an interface description where the interface information comes from a pre-existing class file. Analysis capabilities are not yet implemented in the RIT class file Library.)
Once you have the synthesized interface description object, you work with it in exactly the same way as a synthesized class description object. Populate it with fields, methods, and whatever other information the interface needs. Next, tell the synthesized interface description object to emit its binary class file into an output stream, such as a FileOutputStream or a ByteArrayOutputStream. Finally, to use your new interface, load it into the JVM as described later.
The only fields you can have in an interface are public static final fields. To add such a field to your interface, create a field description object that is an instance of class SynthesizedInterfaceFieldDescription.
The only methods you can have in an interface are public abstract methods. To add such a method to your interface, create a subroutine description object that is an instance of class SynthesizedInterfaceMethodDescription. An interface can also have a class initializer (class SynthesizedClassInitializerDescription) to initialize the interfaces fields if necessary.
After youve filled everything out, a synthesized class description object lets you emit the binary class file into an output stream. (Everything in this section applies to synthesized interfaces too. For brevity, Ill omit mentioning interfaces.) To create actual instances of the synthesized class, you have to load this class file into the JVM. Two strategies are possible.
You can store the binary class file in a file (using a FileOutputStream), placing the file where a class loader can find it. For example, you can place the file in the proper directory in the system class loaders CLASSPATH. Or you can place the file in any old directory, and use a URLClassLoader to get the file from the proper "file://" URL. Or you can place the file where an HTTP server can retrieve it, and use a URLClassLoader to get the file from the proper "http://" URL.
Alternatively, you can feed the binary class file directly into a class loader, bypassing the file system altogether. The RIT Classfile Library provides a special class loader implementation for this purpose, class DirectClassLoader. To feed a class file into a direct class loader, you obtain an output stream from the direct class loader and emit the synthesized class file into this output stream. When later told to load that class, the direct class loader gets the class file from the bytes that were written.
The documentation and Java source files in the RIT Classfile Library ("The Library") are copyright © 2001, 2002 by the Rochester Institute of Technology. All rights reserved. For further information, contact the author, Alan Kaminsky, at ark@it.rit.edu.
The Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
The Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
A copy of the GNU General Public License is provided in the file gpl.txt. You may also obtain a copy of the GNU General Public License on the World Wide Web at http://www.gnu.org/licenses/gpl.html or by writing to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.