Skip navigation links

Package gnu.bytecode

Contains classes to generate, read, write, and print Java bytecode in the form of .class files.

See: Description

Package gnu.bytecode Description

Contains classes to generate, read, write, and print Java bytecode in the form of .class files.

It is used by Kawa to compile Scheme into bytecodes; it should be useful for other languages that need to be compiled into Java bytecodes. (An interesting exercise would be an interactive Java expression evaluator.) The classes here are relatively low-level. If you want to use them to generate bytecode from a high-level language, it would be easier to use the gnu.expr package, which works at the expression level, and handles all the code-generation for you.

The most important class is ClassType. This contains information about a single class. Note that the difference between ClassType and java.lang.Class is that the latter can only represent existing classes that have been loaded into the Java VM; in contrast, ClassType can be used to build new classes incrementally and on the fly. A ClassType can also wrap a java.lang.Class, or data read in from a .class file. Use ClassType.make to refer to existing classes and new ClassType to refer to classes you're generating.

A ClassType has a list of Field objects; new ones can be added using the various addField methods. A ClassType manages a ConstantPool. A ClassType also has a list of Method objects; new ones can be created by the various addMethod objects.

Calling Method.startCode gives you a CodeAttr object you can use to emit bytecodes for that method.

Once you have finished generating a ClassType, you can write it to a .class file with the writeToFile method. You can also make a byte array suitable for ClassLoader.defineClass using the writeToArray method. This is useful if you want to compile and immediately load a class, without going via disk.

You can print out the contents of a ClassType in human-readable form using the class ClassTypeWriter. This prints a fair bit of information of the generated class, including dis-assembling the code of the methods.

You can also build a ClassType by reading it from an existing .class file by using a ClassFileInput class. This reads the constant pool, the fields, methods, superclass, and interfaces. The gnu.bytecode.dump class has a main method that prints out the information in a named class file, which you can use as a replacement for javap(1).

A Simple Example

Here's a complete example showing the basics. If this was really all you wanted to do, the code could be shorter, but you get to see more of what's available this way.

import gnu.bytecode.*;

public class MetaHelloWorld {
        public static void main(String[] args) throws Exception {
            // "public class HelloWorld extends java.lang.Object".
                ClassType c = new ClassType("HelloWorld");
                c.setSuper("java.lang.Object");
                c.setModifiers(Access.PUBLIC);

            // "public static int add(int, int)".
                Method m = c.addMethod("add", "(II)I", Access.PUBLIC | Access.STATIC);
                CodeAttr code = m.startCode();
                code.pushScope();
                code.emitLoad(code.getArg(0));
                code.emitLoad(code.getArg(1));
                code.emitAdd(Type.intType);
                Variable resultVar = code.addLocal(Type.intType, "result");
                code.emitDup();
                code.emitStore(resultVar);
                code.emitReturn();
                code.popScope();

            // Get a byte[] representing the class file.
                // We could write this to disk if we wanted.
                byte[] classFile = c.writeToArray();

            // Disassemble this class.
                // The output is similar to javap(1).
                ClassTypeWriter.print(c, System.out, 0);

            // Load the generated class into this JVM.
                // gnu.bytecode provides ArrayClassLoader, or you can use your own.
                ArrayClassLoader cl = new ArrayClassLoader();
                cl.addClass("HelloWorld", classFile);
                
            // Actual invocation is just the usual reflection code.
                Class<?> helloWorldClass = cl.loadClass("HelloWorld", true);
                Class[] argTypes = new Class[] { int.class, int.class };
                int result = (Integer) helloWorldClass.getMethod("add", argTypes).invoke(null, 1, 2);
                System.err.println(result);
        }
}

License

See the file COPYING.

Author

Per Bothner <per@bothner.com>

How to get it

The gnu.bytecode package is currently distributed as part of Kawa, though it can be used independently of the rest of Kawa.

Bugs and patches

Send them to per@bothner.com, or to the Kawa mailing list.
Skip navigation links