summaryrefslogtreecommitdiff
path: root/java/com/google/devtools/build/android/desugar/Java7Compatibility.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/devtools/build/android/desugar/Java7Compatibility.java')
-rw-r--r--java/com/google/devtools/build/android/desugar/Java7Compatibility.java64
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);