diff options
author | 2017-10-06 23:31:07 +0200 | |
---|---|---|
committer | 2017-10-07 11:07:19 +0200 | |
commit | 38da0c2e6e082964e32e8646439cdec7cd50808f (patch) | |
tree | 7360f27c28fcf9710557a81b66d4de0fb4b0e810 /src/tools/android/java/com/google/devtools | |
parent | 674ab860b3f2ebee958196d96ffee1702087201b (diff) |
Do not rewrite static interface method invocations from bootclasspath
RELNOTES: n/a
PiperOrigin-RevId: 171344856
Diffstat (limited to 'src/tools/android/java/com/google/devtools')
3 files changed, 68 insertions, 14 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java index 07702fed06..36a149233d 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java @@ -378,7 +378,7 @@ class Desugar { interfaceLambdaMethodCollector.build(), bridgeMethodReader); - desugarAndWriteGeneratedClasses(outputFileProvider); + desugarAndWriteGeneratedClasses(outputFileProvider, bootclasspathReader); copyThrowableExtensionClass(outputFileProvider); byte[] depsInfo = depsCollector.toByteArray(); @@ -535,7 +535,8 @@ class Desugar { } } - private void desugarAndWriteGeneratedClasses(OutputFileProvider outputFileProvider) + private void desugarAndWriteGeneratedClasses( + OutputFileProvider outputFileProvider, ClassReaderFactory bootclasspathReader) throws IOException { // Write out any classes we generated along the way ImmutableMap<String, ClassNode> generatedClasses = store.drain(); @@ -547,7 +548,8 @@ class Desugar { UnprefixingClassWriter writer = rewriter.writer(ClassWriter.COMPUTE_MAXS); // checkState above implies that we want Java 7 .class files, so send through that visitor. // Don't need a ClassReaderFactory b/c static interface methods should've been moved. - ClassVisitor visitor = new Java7Compatibility(writer, (ClassReaderFactory) null); + ClassVisitor visitor = + new Java7Compatibility(writer, (ClassReaderFactory) null, bootclasspathReader); generated.getValue().accept(visitor); String filename = rewriter.unprefix(generated.getKey()) + ".class"; outputFileProvider.write(filename, writer.toByteArray()); @@ -583,7 +585,7 @@ class Desugar { } if (outputJava7) { // null ClassReaderFactory b/c we don't expect to need it for lambda classes - visitor = new Java7Compatibility(visitor, (ClassReaderFactory) null); + visitor = new Java7Compatibility(visitor, (ClassReaderFactory) null, bootclasspathReader); if (options.desugarInterfaceMethodBodiesIfNeeded) { visitor = new DefaultMethodClassFixer( @@ -637,7 +639,7 @@ class Desugar { } if (!options.onlyDesugarJavac9ForLint) { if (outputJava7) { - visitor = new Java7Compatibility(visitor, classpathReader); + visitor = new Java7Compatibility(visitor, classpathReader, bootclasspathReader); if (options.desugarInterfaceMethodBodiesIfNeeded) { visitor = new DefaultMethodClassFixer( diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java index 0f03236e72..9371d60f51 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java @@ -341,14 +341,13 @@ class InterfaceDesugaring extends ClassVisitor { @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { // Assume that any static interface methods on the classpath are moved - if (itf || owner.equals(interfaceName)) { + if ((itf || owner.equals(interfaceName)) && !bootclasspath.isKnown(owner)) { boolean isLambda = name.startsWith("lambda$"); name = normalizeInterfaceMethodName(name, isLambda, opcode == Opcodes.INVOKESTATIC); if (isLambda) { // Redirect lambda invocations to completely remove all lambda methods from interfaces. checkArgument(!owner.endsWith(DependencyCollector.INTERFACE_COMPANION_SUFFIX), "shouldn't consider %s an interface", owner); - checkArgument(!bootclasspath.isKnown(owner)); // must be in current input if (opcode == Opcodes.INVOKEINTERFACE) { opcode = Opcodes.INVOKESTATIC; desc = companionDefaultMethodDescriptor(owner, desc); @@ -370,8 +369,7 @@ class InterfaceDesugaring extends ClassVisitor { name, expectedLambdaMethodName); itf = false; - } else if ((opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL) - && !bootclasspath.isKnown(owner)) { + } else if ((opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL)) { checkArgument(!owner.endsWith(DependencyCollector.INTERFACE_COMPANION_SUFFIX), "shouldn't consider %s an interface", owner); if (opcode == Opcodes.INVOKESPECIAL) { diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java index fc4c75f261..777dfcb18e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java +++ b/src/tools/android/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.ASM5, 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.ASM5, 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); |