package com.concurnas.runtime; import java.lang.reflect.Modifier; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import com.concurnas.runtime.cps.analysis.ANFTransform; import com.concurnas.runtime.cps.analysis.ConcClassWriter; import com.concurnas.runtime.cps.analysis.Fiberizer; /** * If a native method invokes an interface type on a java object which has not been implemented (as the fiberized version has in its place), we need the 'normal' * version to map to the fiberized version with a fake fiber. We do this here by converting the interface method into a defualt method * @author jason * */ public class AddDefaultMethodsToInterfaceForFiber extends ClassVisitor implements Opcodes{ public AddDefaultMethodsToInterfaceForFiber(ClassVisitor classVisitor) { super(ASM7, classVisitor); } private String name; public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { this.name = name; cv.visit(version<V1_8?V1_8:version, access, name, signature, superName, interfaces);//upgrade to min java spec supporting default methods } public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { if (!Modifier.isStatic(access) && ((access & ACC_ABSTRACT ) != 0 && !descriptor.contains(Fiberizer.FIBER_SUFFIX)) ) { // replace with default method pointing to fiberized version { MethodVisitor methodVisitor = cv.visitMethod(access & ~ACC_ABSTRACT, name, descriptor, signature, null); //annotations indicating that this method is normally obligatory to implement! (i.e. normally not default) //@DefaultMethodRequiresImplementation { AnnotationVisitor annotationVisitor0 = methodVisitor.visitAnnotation("Lcom/concurnas/bootstrap/runtime/DefaultMethodRequiresImplementation;", true); annotationVisitor0.visitEnd(); } methodVisitor.visitCode(); methodVisitor.visitLabel(new Label()); methodVisitor.visitVarInsn(ALOAD, 0); //load others int locVar=1; for(char c : ANFTransform.getPrimOrObj(descriptor)){//reload the stack from vars methodVisitor.visitVarInsn(ANFTransform.getLoadOp(c), locVar++); if(c == 'D' || c=='J'){ locVar++; } } methodVisitor.visitMethodInsn(INVOKESTATIC, "com/concurnas/bootstrap/runtime/cps/Fiber", "getCurrentFiberWithCreate", "()Lcom/concurnas/bootstrap/runtime/cps/Fiber;", false); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/concurnas/bootstrap/runtime/cps/Fiber", "begin", "()Lcom/concurnas/bootstrap/runtime/cps/Fiber;", false); methodVisitor.visitMethodInsn(INVOKEINTERFACE, this.name, name, descriptor.replace(")", Fiberizer.FIBER_SUFFIX), true); String dd = Type.getMethodType(descriptor).getReturnType().getDescriptor(); methodVisitor.visitInsn(dd.startsWith("[")?ARETURN:ANFTransform.getReturnOp(descriptor.charAt(descriptor.length()-1)));//return methodVisitor.visitMaxs(2, 1); methodVisitor.visitEnd(); return null; } } return cv.visitMethod(access, name, descriptor, signature, exceptions); } private static class IsInterfaceChecker extends ClassVisitor{ public boolean isInterface; public IsInterfaceChecker() { super(ASM7); } public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { isInterface = Modifier.isInterface(access) && Modifier.isAbstract(access) && ((access & ACC_ANNOTATION)==0); } } public static byte[] transform(byte[] code, ConcClassUtil loader) { IsInterfaceChecker checkler = new IsInterfaceChecker(); new ClassReader(code).accept(checkler, 0); if(checkler.isInterface) { ClassWriter cw = new ConcClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS, loader.getDetector()); new ClassReader(code).accept(new AddDefaultMethodsToInterfaceForFiber(cw), 0); return cw.toByteArray(); }else { return code; } } }