Package org.biojava.utils.bytecode
Description
The ByteCode package is able to generate Java Bytecode that can be used to define Java classes. It is not restricted to things that can be expressed as Java code (as in .java files), and is more flexible than basic Java bytecode.
This package supports generating classes, interfaces, methods, fields, and the like. It follows the macro-assembler pattern, where users can effectively extend the bytecode instruction set with their own instructions that at compile time expand to real bytecode instructions. Many useful pseudo-instructions are provided, such as IfExpression. This package also supports some advanced functionality such as templating. Templating supports both object and primitive types and types can be resolved at the level of complete methods or single expressions.
The ByteCode package has no support whatsoever for editing Java Bytecode. In particular, there is no support for search-and-replace or optimization.
The main classes for generating bytecode are
CodeUtils
,
ByteCode
,
InstructionVector
and
GeneratedCodeClass
. You will probably also
use GeneratedClassLoader
, as it does the
actual work of building and loading classes.
It is probably a requirement of using this package effectively that you have memorised or have handy a copy of the Java VM specification. This is available from the Sun web site.
Class Reflection
The ByteCode package represents classes, methods and fields using its own APIs.
These, for obvious reasons, closely resemble those provided by the core Java
APIs. The three interfaces
CodeClass
,
CodeMethod
and
CodeField
represent these basic concepts.
Classes from the Java VM are reflected into this model using
IntrospectedCodeClass
.
To get an instance, use the factory method
IntrospectedCodeClass.forClass(Class)
or
IntrospectedCodeClass.forClass(String)
.
Class Generation
Classes that you generate yourself will be represented by instances of
GeneratedCodeClass
.
Methods are created by calling
GeneratedCodeClass.createMethod(java.lang.String, org.biojava.utils.bytecode.CodeClass, org.biojava.utils.bytecode.CodeClass[], java.lang.String[], int)
,
which will return a
GeneratedCodeMethod
. The method can then be
implemented by associating a
CodeGenerator
with the method on that particular class using
GeneratedCodeClass.setCodeGenerator(org.biojava.utils.bytecode.CodeMethod, org.biojava.utils.bytecode.CodeGenerator)
.
Fields are generated by calling
GeneratedCodeClass.createField(java.lang.String, org.biojava.utils.bytecode.CodeClass, int)
.
The classes themselves are instantiated into the VM using
GeneratedClassLoader
.
This implements
ClassLoader.defineClass(byte[], int, int)
and
over-rides the other necessary book-keeping methods. You may wish to sub-class
or wrap this class in an application to generate the desired behavior.
An example
We are going to implement HelloWorld in bytecode. Here is the (annotated) code.
Firstly, lets define our application structure...
We will fill in the createHelloWorld method to actualy do the work of printing
out hello world by implementing a Runnable with a run() method. Firstly, we
need a load of types, methods and a field or two.
public class BCHelloWorld {
public static void main(String[] args)
throws Throwable {
createHelloWorld().run();
}
public static Runnable createHelloWorld() {
...
}
}
Now we are ready to create our code class. It will be called HelloWorldRunnable,
inherit directly from Object, implement Runnable, and be a public class.
CodeClass cl_Object = IntrospectedCodeClass.forClass(Object.class);
CodeClass cl_String = IntrospectedCodeClass.forClass(String.class);
CodeClass cl_System = IntrospectedCodeClass.forClass(System.class);
CodeClass cl_PrintStream = IntrospectedCodeClass.forClass(PrintStream.class);
CodeClass cl_Void = IntrospectedCodeClass.forClass(Void.TYPE);
CodeClass cl_Runnable = IntrospectedCodeClass.forClass(Runnable.class);
CodeField f_System_out = cl_System.getFieldByName("out");
CodeMethod m_PrintStream_printLn = cl_PrintStream.getMethod(
"printLn",
new CodeClass[] { cl_String } );
CodeMethod m_Object_init = cl_Object.getConstructor(CodeUtils.EMPTY_LIST);
This will need a constructor. Remember, constructors have the name >init<:,
and you must be sure to call the super-constructor explicitly. Normaly the javac
compiler does this for you.
GeneratedCodeClass ourClass = new GeneratedCodeClass(
"HelloWorldRunnable",
cl_Object,
new CodeClass[] { cl_Runnable },
CodeUtils.ACC_PUBLIC | CodeUtils.ACC_SUPER);
To be a Runnable implementation, we must also provide a run() method. This will
take no arguments, return void and be publically accessible. Also, the body of
this method will print out "Hello World" and then we can all feel pleased with
ourselves.
GeneratedCodeMethod init = ourClass.createMethod(
"<init>",
cl_Void,
CodeUtils.EMPTY_LIST,
CodeUtils.ACC_PUBLIC);
InstructionVector initIV = new InstructionVector();
initIV.add(ByteCode.make_aload(init.getThis()));
initIV.add(ByteCode.make_invokespecial(m_Object_init));
initIV.add(ByteCode.make_return());
ourClass.setCodeGenerator(init, initIV);
Now we want to load in a class with this deffinition and instantiate it.
GeneratedCodeMethod run = ourClass.createMethod(
"run",
cl_Void,
CodeUtils.EMPTY_LIST,
CodeUtils.ACC_PUBLIC);
InstructionVector runIV = new InstructionVector();
runIV.add(ByteCode.make_getstatic(f_System_out));
runIV.add(ByteCode.make_sconst("Hello World");
runIV.add(ByteCode.make_invokevirtual(m_PrintStream_printLn));
runIV.add(ByteCode.make_return());
ourClass.setCodeGenerator(run, runIV);
And there you are. With any luck, if you type all of this in, and the fates
smile on you, and biojava.jar is arround for linking against, you should have
generated your very own unuque HelloWorld class.
GeneratedClassLoader gcl = new GeneratedClassLoader(class.getClassLoader());
Class newClass = gcl.defineClass(ourClass);
return (Runnable) newClass.newInstance();
Good luck.
-
Interface Summary Interface Description CodeClass Interface for Java classes within the bytecode generation framework.CodeContext Interface which encapsulates the stream to which Java bytecode can be written.CodeGenerator Interface for an object which can produce Java bytecode.CodeMethod Wrap up details about a method in a Java class fileInstruction Base class for java bytecode instructions.ParametricCodeGenerator PParametricCodeGenerator -
Class Summary Class Description ByteCode Factory for objects which encapsulate individual Java bytecode instructions.CodeField Wrap up details about a field in a Java class file.CodeUtils Utility code for things you will frequently need.ConstantPool Build a Java class file constant pool.GeneratedClassLoader A class loader that actually produces real Java classes from GeneratedCodeClass instances.GeneratedCodeClass A CodeClass implementation that is used to generate new classes.GeneratedCodeMethod A method that will be generated.IfExpression A CodeGenerator that provides something semanticaly identical to if.InstructionVector A list of Instructions and/or other CodeGenerator objects.IntrospectedCodeClass CodeClass instances that represent normal Java Class objects.Label A Label used to mark a position in byte code.LocalVariable A local variable.MarkLabel A CodeGenerator that just marks a label that can be used for jumps.ParametricType A template type. -
Exception Summary Exception Description CodeException An exception indicating that something went wrong generating byte code.