log.finest("Creating super dispatcher for method "+name+descriptor+" in type "+slashedname);
}
// Create a superdispatcher for this method
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, method.getName(), method.getDescriptor(), null, method.getExceptions());
int ps = Utils.getParameterCount(method.getDescriptor());
ReturnType methodReturnType = Utils.getReturnTypeDescriptor(method.getDescriptor());
int lvarIndex = 0;
mv.visitVarInsn(ALOAD, lvarIndex++); // load this
Utils.createLoadsBasedOnDescriptor(mv, descriptor, lvarIndex);
String targetMethod = method.getName().substring(0,method.getName().lastIndexOf("_$"));
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,typeDescriptor.getSupertypeName(),targetMethod,method.getDescriptor());
Utils.addCorrectReturnInstruction(mv, methodReturnType, false);
int maxs = ps + 1;
if (methodReturnType.isDoubleSlot()) {
maxs++;
}
mv.visitMaxs(maxs, maxs);
mv.visitEnd();
}
for (MethodMember method : methods) {
if (!MethodMember.isCatcher(method)) {
continue;
}
String name = method.getName();
String descriptor = method.getDescriptor();
ReturnType returnType = Utils.getReturnTypeDescriptor(descriptor);
// 1. Create the method signature
int flags = method.getModifiers();
if (Modifier.isProtected(flags)) {
flags = flags - Modifier.PROTECTED + Modifier.PUBLIC;
}
MethodVisitor mv = cw.visitMethod(flags, method.getName(), method.getDescriptor(), null, method.getExceptions());
// 2. Ask the type if anything has changed from first load
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitLdcInsn(method.getId());
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "fetchLatestIfExists", "(I)Ljava/lang/Object;");
// If the return value is null, there is no implementation
mv.visitInsn(DUP);
// 3. create the if statement
Label l1 = new Label();
mv.visitJumpInsn(Opcodes.IFNULL, l1);
// 4. if changed then call the interface to run whatever version has been added
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
int lvarIndex = 0;
mv.visitVarInsn(ALOAD, lvarIndex++); // load this
Utils.createLoadsBasedOnDescriptor(mv, descriptor, lvarIndex);
String desc = new StringBuffer("(L").append(slashedname).append(";").append(descriptor.substring(1)).toString();
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), name, desc);
Utils.addCorrectReturnInstruction(mv, returnType, true);
// 5. if unchanged just run the supertype version (could be another catcher...)
mv.visitLabel(l1);
mv.visitInsn(POP);
int ps = Utils.getParameterCount(method.getDescriptor());
ReturnType methodReturnType = Utils.getReturnTypeDescriptor(method.getDescriptor());
// A catcher for an interface method is inserted into abstract classes. These should never be reached unless
// they now provide an implementation (on a reload) and the subtype has deleted the implementation it had.
// This means there is never a need to call 'super' in the logic below and getting here when there isn't
// something to run in the executor is a bug (so we throw AbstractMethodError)
if (MethodMember.isCatcherForInterfaceMethod(method)) {
mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/AbstractMethodError", "<init>", "()V");
mv.visitInsn(ATHROW);
} else {
mv.visitVarInsn(ALOAD, 0); // load this
Utils.createLoadsBasedOnDescriptor(mv, method.getDescriptor(), 1);
mv.visitMethodInsn(INVOKESPECIAL, supertypeName, method.getName(), method.getDescriptor());
Utils.addCorrectReturnInstruction(mv, methodReturnType, false);
}
int maxs = ps + 1;
if (methodReturnType.isDoubleSlot()) {
maxs++;
}
mv.visitMaxs(maxs, maxs);
mv.visitEnd();
}