try {
ArrayList<Method> annotatedMethods = new ArrayList();
Method[] methods = type.getDeclaredMethods();
for (Method method : methods) {
JRubyMethod jrubyMethod = method.getAnnotation(JRubyMethod.class);
if (jrubyMethod == null) continue;
annotatedMethods.add(method);
}
// To ensure the method cases are generated the same way every time, we make a second sorted list
ArrayList<Method> sortedMethods = new ArrayList(annotatedMethods);
Collections.sort(sortedMethods, new Comparator<Method>() {
public int compare(Method a, Method b) {
return a.getName().compareTo(b.getName());
}
});
// But when binding the methods, we want to use the order from the original class, so we save the indices
HashMap<Method,Integer> indexMap = new HashMap();
for (int index = 0; index < sortedMethods.size(); index++) {
indexMap.put(sortedMethods.get(index), index);
}
if (c == null) {
ClassWriter cw = createIndexedJavaMethodCtor(generatedClassPath, superClass);
SkinnyMethodAdapter mv = null;
mv = new SkinnyMethodAdapter(cw.visitMethod(ACC_PUBLIC, "call", COMPILED_CALL_SIG_BLOCK, null, null));
mv.visitCode();
Label line = new Label();
mv.visitLineNumber(0, line);
Label defaultCase = new Label();
Label[] cases = new Label[sortedMethods.size()];
for (int i = 0; i < cases.length; i++) cases[i] = new Label();
// load method index
mv.aload(THIS_INDEX);
mv.getfield(generatedClassPath, "methodIndex", ci(int.class));
mv.tableswitch(0, cases.length - 1, defaultCase, cases);
for (int i = 0; i < sortedMethods.size(); i++) {
mv.label(cases[i]);
String callName = getAnnotatedMethodForIndex(cw, sortedMethods.get(i), i, superClass);
// invoke call#_method for method
mv.aload(THIS_INDEX);
mv.aload(THREADCONTEXT_INDEX);
mv.aload(RECEIVER_INDEX);
mv.aload(CLASS_INDEX);
mv.aload(NAME_INDEX);
mv.aload(ARGS_INDEX);
mv.aload(BLOCK_INDEX);
mv.invokevirtual(generatedClassPath, callName, COMPILED_CALL_SIG_BLOCK);
mv.areturn();
}
// if we fall off the switch, error.
mv.label(defaultCase);
mv.aload(THREADCONTEXT_INDEX);
mv.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
mv.ldc("Error: fell off switched invoker for class: " + implementationClass.getBaseName());
mv.invokevirtual(p(Ruby.class), "newRuntimeError", sig(RaiseException.class, String.class));
mv.athrow();
c = endCall(cw, mv, generatedClassName);
}
for (int i = 0; i < annotatedMethods.size(); i++) {
Method method = annotatedMethods.get(i);
JRubyMethod jrubyMethod = method.getAnnotation(JRubyMethod.class);
if (jrubyMethod.frame()) {
for (String name : jrubyMethod.name()) {
ASTInspector.FRAME_AWARE_METHODS.add(name);
}
}
int index = indexMap.get(method);
JavaMethod ic = (JavaMethod)c.getConstructor(new Class[]{RubyModule.class, Visibility.class, int.class}).newInstance(new Object[]{implementationClass, jrubyMethod.visibility(), index});
ic.setArity(Arity.fromAnnotation(jrubyMethod));
ic.setJavaName(method.getName());
ic.setSingleton(Modifier.isStatic(method.getModifiers()));
ic.setCallConfig(CallConfiguration.getCallConfigByAnno(jrubyMethod));