aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-10-06 23:31:07 +0200
committerGravatar Klaus Aehlig <aehlig@google.com>2017-10-07 11:07:19 +0200
commit38da0c2e6e082964e32e8646439cdec7cd50808f (patch)
tree7360f27c28fcf9710557a81b66d4de0fb4b0e810 /src/tools/android/java/com/google/devtools
parent674ab860b3f2ebee958196d96ffee1702087201b (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')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java12
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java6
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java64
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);