diff options
Diffstat (limited to 'java/com/google/devtools/build/android/desugar/Java7Compatibility.java')
-rw-r--r-- | java/com/google/devtools/build/android/desugar/Java7Compatibility.java | 64 |
1 files changed, 59 insertions, 5 deletions
diff --git a/java/com/google/devtools/build/android/desugar/Java7Compatibility.java b/java/com/google/devtools/build/android/desugar/Java7Compatibility.java index 752227e..30de63d 100644 --- a/java/com/google/devtools/build/android/desugar/Java7Compatibility.java +++ b/java/com/google/devtools/build/android/desugar/Java7Compatibility.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import javax.annotation.Nullable; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; @@ -27,19 +28,28 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.TypePath; /** - * Visitor that ensures bytecode version <= 51 (Java 7) and that throws if it sees default or static - * interface methods (i.e., non-abstract interface methods), which don't exist in Java 7. + * Visitor that tries to ensures bytecode version <= 51 (Java 7) and that throws if it sees default + * or static interface methods (i.e., non-abstract interface methods), which don't exist in Java 7. + * <p>The class version will 52 iff static interface method from the bootclasspath is invoked. + * This is mostly ensure that the generated bytecode is valid. */ public class Java7Compatibility extends ClassVisitor { - private final ClassReaderFactory factory; + @Nullable private final ClassReaderFactory factory; + @Nullable private final ClassReaderFactory bootclasspathReader; private boolean isInterface; private String internalName; + private int access; + private String signature; + private String superName; + private String[] interfaces; - public Java7Compatibility(ClassVisitor cv, ClassReaderFactory factory) { + public Java7Compatibility( + ClassVisitor cv, ClassReaderFactory factory, ClassReaderFactory bootclasspathReader) { super(Opcodes.ASM6, cv); this.factory = factory; + this.bootclasspathReader = bootclasspathReader; } @Override @@ -51,6 +61,10 @@ public class Java7Compatibility extends ClassVisitor { String superName, String[] interfaces) { internalName = name; + this.access = access; + this.signature = signature; + this.superName = superName; + this.interfaces = interfaces; isInterface = BitFlags.isSet(access, Opcodes.ACC_INTERFACE); super.visit( Math.min(version, Opcodes.V1_7), @@ -83,7 +97,10 @@ public class Java7Compatibility extends ClassVisitor { || "<clinit>".equals(name), "Interface %s defines non-abstract method %s%s, which is not supported", internalName, name, desc); - MethodVisitor result = super.visitMethod(access, name, desc, signature, exceptions); + MethodVisitor result = + new UpdateBytecodeVersionIfNecessary( + super.visitMethod(access, name, desc, signature, exceptions)); + return (isInterface && "<clinit>".equals(name)) ? new InlineJacocoInit(result) : result; } @@ -96,6 +113,42 @@ public class Java7Compatibility extends ClassVisitor { } } + /** This will rewrite class version to 52 if it sees invokestatic on an interface. */ + private class UpdateBytecodeVersionIfNecessary extends MethodVisitor { + + boolean updated = false; + + public UpdateBytecodeVersionIfNecessary(MethodVisitor methodVisitor) { + super(Opcodes.ASM5, methodVisitor); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (itf && opcode == Opcodes.INVOKESTATIC) { + checkNotNull(bootclasspathReader); + checkState( + bootclasspathReader.isKnown(owner), + "%s contains invocation of static interface method that is " + + "not in the bootclasspath. Owner: %s, name: %s, desc: %s.", + Java7Compatibility.this.internalName, + owner, + name, + desc); + if (!updated) { + Java7Compatibility.this.cv.visit( + Opcodes.V1_8, + Java7Compatibility.this.access, + Java7Compatibility.this.internalName, + Java7Compatibility.this.signature, + Java7Compatibility.this.superName, + Java7Compatibility.this.interfaces); + updated = true; + } + } + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + private class InlineJacocoInit extends MethodVisitor { public InlineJacocoInit(MethodVisitor dest) { super(Opcodes.ASM6, dest); @@ -106,6 +159,7 @@ public class Java7Compatibility extends ClassVisitor { if (opcode == Opcodes.INVOKESTATIC && "$jacocoInit".equals(name) && internalName.equals(owner)) { + checkNotNull(factory); ClassReader bytecode = checkNotNull(factory.readIfKnown(internalName), "Couldn't load interface %s to inline $jacocoInit()", internalName); InlineOneMethod copier = new InlineOneMethod("$jacocoInit", this); |