ClassBuilder
com.jsmacrosce.jsmacros.core.library.impl.classes.ClassBuilder
Builds a Java class at runtime from source declarations or member builders. Scripts obtain one through Reflection.createClassBuilder, add members, and define it with finishBuildAndFreeze(). The generated class is defined in JsMacros' proxy package;Reflection.getClassFromClassBuilderResult looks it up by its generated name.
Source declarations provide Java code directly. The typed member builders additionally support configuring modifiers, exceptions, and annotations; their guest-body forms can call back into the running script, unlike source method bodies. The callback body uses a key derived from the generated class name, method name, and JVM descriptor.
Use MethodBuilder.buildBody() to mix Java statements with guest calls;
BodyBuilder shows how to capture a guest call's result in a Java variable.
The returned Class is typed as its superclass, so script code may need a cast to access generated members. Defining the class freezes it; it cannot then be modified or built again.
Compilation and resolution failures are reported by the operation that encounters them.
// Build and define a generated subclass with one Java-source method and
// one method that calls back into this script.
const builder = Reflection.createClassBuilder("Counting",
Reflection.getClass("java.util.ArrayList"));
// members can be written as java source, one declaration at a time
builder.addField("private int total;");
builder.addMethod("public int getTotal() { return this.total; }");
// Typed member builders accept Class objects and guestBody calls back into
// this script; its handler receives the instance and boxed arguments.
const check = builder.addMethod(Reflection.getClass("boolean"), "hasItems");
check.makePublic();
check.guestBody(JavaWrapper.methodToJava(function (self, args) {
return self.size() > 0;
}));
// this is what freezes it, and it can only be called once
const CountingClass = builder.finishBuildAndFreeze();
// The returned Class is exposed as its superclass type; JavaScript can still
// invoke generated members on the constructed instance.
const counting = Reflection.newInstance(CountingClass);
print(counting.getTotal());
print(counting.hasItems());Type Parameters
T: superclass type, used as the generated class's exposed type
Nested types
Constructors
new ClassBuilder
parent and implementing the listed interfaces. Dots in name become dollar signs; the generated class is placed in JsMacros' proxy package, and this name is used by Reflection.getClassFromClassBuilderResult to look it up. The parent and interfaces must resolve in the bytecode library's class pool or this constructor throws NotFoundException. // extending a concrete class, with the shipped typings typing the result
// as that class rather than as the one being built
const builder = Reflection.createClassBuilder("Counting",
Reflection.getClass("java.util.ArrayList"));
builder.addField("private int total;");Parameters:
name: the name to generate the class underparent: the class to extendinterfaces: any interfaces to implement
Fields
ctClass
methodWrappers
BodyBuilder adds a call-specific suffix when a method contains multiple guest calls. Handlers are registered as guestBody or appendGuestCode builds the guest-call code.Methods
addAnnotation
addAnnotation(Class<?> type): ClassBuilder.AnnotationBuilder<ClassBuilder<T>>#// an annotation with nothing on it. the cast is for the editor's
// benefit: the shipped typings do not describe the annotation types
builder.addAnnotation(Reflection.getClass("java.lang.FunctionalInterface") as any)
.finish();
// one with a value, which takes two finishes: the first closes the
// array, the second puts the annotation on the class
builder.addAnnotation(Reflection.getClass("java.lang.SuppressWarnings") as any)
.putArray("value")
.putString("unchecked")
.finish()
.finish();Parameters:
type: the annotation type to put on the class
addClinit
addClinit(): ClassBuilder.ConstructorBuilder#addConstructor
addConstructor(Class<?>... params): ClassBuilder.ConstructorBuilder#const init = builder.addConstructor(Reflection.getClass("java.lang.String"));
init.makePublic();
init.body('super($1); this.label = $1;');Parameters:
params: the parameter types, in order
java.lang may be unqualified, as in Java. public MyClass() { }
public MyClass(String text) { System.out.println(text); }
protected MyClass(String text, int number) { super(text, number, ""); }
public MyClass(String text, int number, String other) {
this(text, number);
this.other = other;
}Parameters:
code: the code for the constructor
addField
addField(Class<?> fieldType, String name): ClassBuilder.FieldBuilder#builder.addField(Reflection.getClass("java.lang.String"), "label")
.makePrivate()
.toggleFinal()
.initializer().setString("unnamed")
.end();Parameters:
fieldType: the type of the fieldname: the name of the field
java.lang may be unqualified, as in Java. private String name;
private java.lang.String name;
public java.util.List list = new java.util.ArrayList();
static int value = 10;Parameters:
code: the code for the field
addMethod
addMethod(Class<?> returnType, String name, Class<?>... params): ClassBuilder.MethodBuilder#const build = builder.addMethod(Reflection.getClass("java.lang.String"), "describe",
Reflection.getClass("java.lang.Object"));
build.makePublic();
build.body("return "an object: " + $1;");
// the same method, but abstract, which is what an interface needs
const required = builder.addMethod(Reflection.getClass("void"), "reset");
required.makePublic();
required.endAbstract();Parameters:
returnType: the type the method returnsname: the name of the methodparams: the parameter types, in order
java.lang may be unqualified, as in Java. public Object id(Object obj) { return obj; }
private void print(String text) { System.out.println(text); }
private static java.util.Map toMap(Object[] keys, Object[] values) {
java.util.Map map = new java.util.HashMap();
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
return map;
}
public String toString() {
System.out.println(super.toString());
return "Hello World!";
}Parameters:
code: the code for the method
finishBuildAndFreeze
const CountingClass = builder.finishBuildAndFreeze();
// typed as what it extends, so an added method needs a cast
const counting = Reflection.newInstance(CountingClass) as any;
print(counting.getTotal());