This repository was archived by the owner on Aug 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Example
Brian S. O'Neill edited this page Feb 7, 2021
·
5 revisions
An example class is provided (cojen.example.HelloWorld) which demonstrates dynamic class generation and loading. It creates a class equivalent to the following, loads it, and then invokes the main method. Based on HelloWorldBuilder from the Apache BCEL project.
public class HelloWorld {
public static void main(String[] argv) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String name = null;
try {
System.out.print("Please enter your name> ");
name = in.readLine();
} catch(IOException e) { return; }
System.out.println("Hello, " + name);
}
}
Here is the full source to the example, sans header comments for brevity:
package org.cojen.example;
// Used to generate the class
import org.cojen.classfile.CodeBuilder;
import org.cojen.classfile.Label;
import org.cojen.classfile.LocalVariable;
import org.cojen.classfile.MethodInfo;
import org.cojen.classfile.Modifiers;
import org.cojen.classfile.RuntimeClassFile;
import org.cojen.classfile.TypeDesc;
// Used to execute the generated class
import java.lang.reflect.Method;
public class HelloWorld {
public static void main(String[] args) throws Exception {
// Create the RuntimeClassFile using an automatically selected name.
RuntimeClassFile cf = createClassFile();
Class clazz = cf.defineClass();
// Find the generated main method and invoke it.
Method m = clazz.getMethod("main", new Class[] {String[].class});
m.invoke(null, new Object[] {args});
}
/**
* Creates a ClassFile which defines a simple interactive HelloWorld class.
*
* @param className name given to class
*/
private static RuntimeClassFile createClassFile() {
// Create a ClassFile with the super class of Object.
RuntimeClassFile cf = new RuntimeClassFile();
// Default constructor works only if super class has an accessible
// no-arg constructor.
cf.addDefaultConstructor();
// Add the main method, and construct a CodeBuilder for defining the
// bytecode.
TypeDesc[] params = new TypeDesc[] {TypeDesc.STRING.toArrayType()};
MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "main", null, params);
CodeBuilder b = new CodeBuilder(mi);
// Create some types which will be needed later.
TypeDesc bufferedReader = TypeDesc.forClass("java.io.BufferedReader");
TypeDesc inputStreamReader = TypeDesc.forClass("java.io.InputStreamReader");
TypeDesc inputStream = TypeDesc.forClass("java.io.InputStream");
TypeDesc reader = TypeDesc.forClass("java.io.Reader");
TypeDesc stringBuffer = TypeDesc.forClass("java.lang.StringBuffer");
TypeDesc printStream = TypeDesc.forClass("java.io.PrintStream");
// Declare local variables to be used.
LocalVariable in = b.createLocalVariable("in", bufferedReader);
LocalVariable name = b.createLocalVariable("name", TypeDesc.STRING);
// Create the first line of code, corresponding to
// in = new BufferedReader(new InputStreamReader(System.in));
b.newObject(bufferedReader);
b.dup();
b.newObject(inputStreamReader);
b.dup();
b.loadStaticField("java.lang.System", "in", inputStream);
params = new TypeDesc[] {inputStream};
b.invokeConstructor(inputStreamReader.getRootName(), params);
params = new TypeDesc[] {reader};
b.invokeConstructor(bufferedReader.getRootName(), params);
b.storeLocal(in);
// Create and locate a label for the start of the "try" block.
Label tryStart = b.createLabel().setLocation();
// Create input prompt.
b.loadStaticField("java.lang.System", "out", printStream);
b.loadConstant("Please enter your name> ");
params = new TypeDesc[] {TypeDesc.STRING};
b.invokeVirtual(printStream, "print", null, params);
// Read a line from the reader, and store it in the "name" variable.
b.loadLocal(in);
b.invokeVirtual(bufferedReader, "readLine", TypeDesc.STRING, null);
b.storeLocal(name);
// If no exception is thrown, branch to a label to print the
// response. The location of the label has not yet been set.
Label printResponse = b.createLabel();
b.branch(printResponse);
// Create and locate a label for the end of the "try" block.
Label tryEnd = b.createLabel().setLocation();
// Create the "catch" block.
b.exceptionHandler(tryStart, tryEnd, "java.io.IOException");
b.returnVoid();
// If no exception, then branch to this location to print the response.
printResponse.setLocation();
// Create the line of code, corresponding to
// System.out.println("Hello, " + name);
b.loadStaticField("java.lang.System", "out", printStream);
b.newObject(stringBuffer);
b.dup();
b.loadConstant("Hello, ");
params = new TypeDesc[] {TypeDesc.STRING};
b.invokeConstructor(stringBuffer, params);
b.loadLocal(name);
b.invokeVirtual(stringBuffer, "append", stringBuffer, params);
b.invokeVirtual(stringBuffer, "toString", TypeDesc.STRING, null);
params = new TypeDesc[] {TypeDesc.STRING};
b.invokeVirtual(printStream, "println", null, params);
// The last instruction reached must be a return or else the class
// verifier will complain.
b.returnVoid();
return cf;
}
}