A problem when using registerMethod often is that it is rather slow. The main reason is the heavy use of reflection. Using the asm library one can however create a much faster implementation of the same functionality that only use reflection on each class once and uses the asm library.
Example.pde
void setup() {
CLS instance[]=new CLS[100000];
for (int i=0; i<instance.length; i++) {
instance[i]=new CLS();
registerMethod("pre", instance[i]);
}
println(millis());
noLoop();
}
void draw() {
println(millis());
}
public class CLS {
void pre() {
}
}
Handler.pde
import java.util.*;
import processing.event.KeyEvent;
import processing.event.MouseEvent;
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;
//Overrides the handleMethod,registerMethod and unregisterMethod Methods.
//Here the events like draw,pre and so on are processed. The String notes wich method is to be executed.
@Override
public void handleMethods(String n, Object... ob) {
handleMethods(this, n, ob);
}
//Registers the method
@Override
public void registerMethod(String name, Object o) {
registerMethod(this, name, o);
}
//Registers the method. A feature implemented in this minilibrary is that to register
//e.g. the draw-method the method itself doesn't need to be called draw
//This means one can attach other methods to be run after draw even if they don't carry this name.
//However this remapping may only be set up once per class.
//The are only there to give the option to either give the names as arguments or as an array the contents must look like this
//names[0] -> method to be run after draw registerMethod("draw",...)
//names[1] -> method to be run before draw registerMethod("pre" ,...)
//names[2] -> method to be run post draw registerMethod("post",...)
//names[3] -> method to be run post when exit() is called or the window closes registerMethod("dispose",...)
//names[4] -> method to be run post when a keyEvent is registered registerMethod("keyEvent",...)
//names[5] -> method to be run post when a MouseEvent is registered registerMethod("mouseEvent",...)
public void registerMethod(String name, Object o, String... names) {
registerMethod(this, name, o, names);
}
//Unregisters a method
@Override
public void unregisterMethod(String name, Object o) {
unregisterMethod(this, name, o);
}
//ClassLoader used to load the wrapperclasses
final public class DynCL extends ClassLoader {
public Class<?> defineClass(String name, byte[]b) {
Class<?> ret= defineClass(name.replace("/", "."), b, 0, b.length);
return ret;
}
}
public DynCL globCL=new DynCL();
//All registerable methods are here. This doubles as the standard-entry for the names vargs in registerMethod.
public static String regmethods[]={"draw", "pre", "post", "dispose", "keyEvent", "mouseEvent"};
//Creates a Wrapper. This is a instance of a interface wich does have the draw,pre,post,... methods and can execute them without reflection.
public <T> RegisterableWrapper<T> wrap(Class<T> cls, String... names) {
//Edits the classname
String clName=cls.getName().replace(".", "/");
//Creates a classname for the Wrapperclass
String nclnm ="wrappers/Wrapper"+cls.getSimpleName();
ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_FRAMES);
//Creates the class. It most notably implements the RegisterableWrapper interface
cw.visit(V1_7, ACC_PUBLIC, nclnm, null, "java/lang/Object", new String[]{"RegisterableWrapper"});
//Writes empty constructor
MethodVisitor con=cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
con.visitCode();
con.visitVarInsn(ALOAD, 0);
con.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
con.visitInsn(Opcodes.RETURN);
con.visitMaxs(1, 1);
for (int i=0; i<regmethods.length; i++) {
boolean present=methodPresent(cls, names[i]);
//Implements the abstract Method
/*void methodName(Object o){
* ((Class) o).method();
* }
* */
MethodVisitor mvclPrim=cw.visitMethod(ACC_PUBLIC, regmethods[i], "(Ljava/lang/Object;)V", null, null);
mvclPrim.visitCode();
if (present) {
mvclPrim.visitVarInsn(ALOAD, 1);
mvclPrim.visitTypeInsn(CHECKCAST, clName);
mvclPrim.visitMethodInsn(INVOKEVIRTUAL, clName, names[i], "()V", false);
}
mvclPrim.visitInsn(Opcodes.RETURN);
mvclPrim.visitMaxs(2, 2);
}
/*Implements the KeyEvent and mouseEvent Method the same way.*/
do {
boolean present=methodPresent(cls, names[4]);
MethodVisitor mvclPrim=cw.visitMethod(ACC_PUBLIC, "keyEvent", "(Ljava/lang/Object;Lprocessing/event/KeyEvent;)V", null, null);
mvclPrim.visitCode();
if (present) {
mvclPrim.visitVarInsn(ALOAD, 1);
mvclPrim.visitTypeInsn(CHECKCAST, clName);
mvclPrim.visitVarInsn(ALOAD, 2);
mvclPrim.visitMethodInsn(INVOKEVIRTUAL, clName, names[4], "(Lprocessing/event/KeyEvent;)V", false);
}
mvclPrim.visitInsn(Opcodes.RETURN);
mvclPrim.visitMaxs(3, 3);
} while (false);
do {
boolean present=methodPresent(cls, names[5]);
MethodVisitor mvclPrim=cw.visitMethod(ACC_PUBLIC, "mouseEvent", "(Ljava/lang/Object;Lprocessing/event/MouseEvent;)V", null, null);
mvclPrim.visitCode();
if (present) {
mvclPrim.visitVarInsn(ALOAD, 1);
mvclPrim.visitTypeInsn(CHECKCAST, clName);
mvclPrim.visitVarInsn(ALOAD, 2);
mvclPrim.visitMethodInsn(INVOKEVIRTUAL, clName, names[4], "(Lprocessing/event/MouseEvent;)V", false);
}
mvclPrim.visitInsn(Opcodes.RETURN);
mvclPrim.visitMaxs(3, 3);
} while (false);
cw.visitEnd();
try {
//Loads the class and returns a instance
return ((Class<RegisterableWrapper<T>>)globCL.defineClass(nclnm, cw.toByteArray()))
.getConstructor(new Class[]{}).newInstance();
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
//Creates a wrapper without needing to input the names
public <T> RegisterableWrapper<T> wrap(Class<T> cls) {
return wrap(cls, regmethods);
}
//Checks if of a certain name exists in a class
public static boolean methodPresent(Class<?> cls, String name) {
if(name.length()==0) return false;
try {
while (true) {
java.lang.reflect.Method[] mts=cls.getDeclaredMethods();
for (java.lang.reflect.Method m:mts) {
if (m.getName().equals(name)) return true;
}
if (cls==Object.class) return false;
cls=cls.getSuperclass();
}
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
//Tracks wrappers used so only one instance needs to be created
Map<Class<?>, RegisterableWrapper<Object>> wrapperMap=new HashMap<Class<?>, RegisterableWrapper<Object>>();
//Tracks registered methods. This contains e.g. all registered draw methods.
public class RegisterMethodHandler {
//Tracks objects
List<Object> objs=new ArrayList<Object>();
//Tracks wrappers
List<RegisterableWrapper<Object>> wrps=new ArrayList<RegisterableWrapper<Object>>();
//Adds a object to the list
public void register(Object obj, String... names) {
Class<?> cls=obj.getClass();
//If there is no wrapper present one will be created
if (!wrapperMap.containsKey(cls)) declareMap(cls, names);
objs.add(obj);
wrps.add(wrapperMap.get(cls));
}
public void register(Object obj) {
register(obj, regmethods);
}
//Removes an object from the handler
public void unregister(Object obj) {
for (int i=0; i<objs.size(); i++) {
if (objs.get(i)==obj) {
objs.remove(i);
wrps.remove(i);
return;
}
}
}
//Executes the method by checking the name and executing the according method
public void executeMethod(String name, Object... args) {
switch(name) {
case "draw":
for (int i=0; i<objs.size(); i++) wrps.get(i).draw(objs.get(i));
break;
case "pre":
for (int i=0; i<objs.size(); i++) wrps.get(i).pre(objs.get(i));
break;
case "post":
for (int i=0; i<objs.size(); i++) wrps.get(i).post(objs.get(i));
break;
case "dispose":
for (int i=0; i<objs.size(); i++) wrps.get(i).dispose(objs.get(i));
break;
case "keyEvent":
for (int i=0; i<objs.size(); i++) wrps.get(i).keyEvent(objs.get(i), (processing.event.KeyEvent)args[0]);
break;
case "mouseEvent":
for (int i=0; i<objs.size(); i++) wrps.get(i).mouseEvent(objs.get(i), (processing.event.MouseEvent)args[0]);
break;
default:
throw new RuntimeException("Unrecognized Method: "+name);
}
}
}
//Contains the data about all PApplets, and wich methods are registere (The PApplets are tracked because this code is part of a small library)
public static Map<PApplet, Map<String, RegisterMethodHandler>> collected=new HashMap<PApplet, Map<String, RegisterMethodHandler>>();
//Handles the Registering by putting the method and object into the correct data-structure
public void registerMethod(PApplet pa, String name, Object o, String... names) {
if (!collected.containsKey(pa)) collected.put(pa, new HashMap<String, RegisterMethodHandler>());
if (!collected.get(pa).containsKey(name)) collected.get(pa).put(name, new RegisterMethodHandler());
RegisterMethodHandler rmh=collected.get(pa).get(name);
rmh.register(o, names);
}
public void registerMethod(PApplet pa, String name, Object o) {
registerMethod(pa, name, o, regmethods);
}
public void unregisterMethod(PApplet pa, String name, Object o) {
collected.get(pa).get(name).unregister(o);
}
//executes handled Methods
public void handleMethods(PApplet pa, String name, Object... args) {
//Checks if the method is even registered
if (!collected.containsKey(pa)) return;
if (!collected.get(pa).containsKey(name)) return;
collected.get(pa).get(name).executeMethod(name, args);
}
//Creates a wrapper and saves it
void declareMap(Class<?> cls, String... names) {
wrapperMap.put(cls, (RegisterableWrapper<Object>)wrap(cls, names));
}
RegisterableWrapper.java
import processing.event.*;
public interface RegisterableWrapper<T> {
public void draw(T a);
public void pre(T a);
public void post(T a);
public void dispose(T a);
public void keyEvent(T a, KeyEvent ke);
public void mouseEvent(T a, MouseEvent me);
}
This has a much less overhead (300ms instead of 25s) in the example and less time executing (20ms between setup and draw instead of 80-120ms)