From c8e8749adc7b98c272b2421569dc97a88d487771 Mon Sep 17 00:00:00 2001 From: kmb Date: Thu, 15 Feb 2018 10:43:41 -0800 Subject: Resolve the owner of interface.super calls to inherited default methods for android desugaring RELNOTES: None. PiperOrigin-RevId: 185863194 --- .../android/desugar/DefaultMethodClassFixer.java | 1 + .../devtools/build/android/desugar/Desugar.java | 2 ++ .../build/android/desugar/InterfaceDesugaring.java | 42 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) (limited to 'src/tools/android/java/com') diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java b/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java index 2eda141fb9..52964b7f70 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java @@ -502,6 +502,7 @@ public class DefaultMethodClassFixer extends ClassVisitor { DefaultMethodClassFixer.this.visitMethod(access, name, desc, (String) null, exceptions), stubbedInterfaceName, bootclasspath, + targetLoader, depsCollector, internalName); } else { 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 053e55e760..58b9b885cf 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 @@ -697,6 +697,7 @@ class Desugar { interfaceCache, depsCollector, bootclasspathReader, + loader, store, options.legacyJacocoFix); } @@ -776,6 +777,7 @@ class Desugar { interfaceCache, depsCollector, bootclasspathReader, + loader, store, options.legacyJacocoFix); } 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 3524fae710..f17f114884 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 @@ -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 java.lang.reflect.Method; import javax.annotation.Nullable; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; @@ -47,6 +48,7 @@ class InterfaceDesugaring extends ClassVisitor { private final ClassVsInterface interfaceCache; private final DependencyCollector depsCollector; private final ClassReaderFactory bootclasspath; + private final ClassLoader targetLoader; private final GeneratedClassStore store; private final boolean legacyJaCoCo; @@ -62,12 +64,14 @@ class InterfaceDesugaring extends ClassVisitor { ClassVsInterface interfaceCache, DependencyCollector depsCollector, ClassReaderFactory bootclasspath, + ClassLoader targetLoader, GeneratedClassStore store, boolean legacyJaCoCo) { super(Opcodes.ASM6, dest); this.interfaceCache = interfaceCache; this.depsCollector = depsCollector; this.bootclasspath = bootclasspath; + this.targetLoader = targetLoader; this.store = store; this.legacyJaCoCo = legacyJaCoCo; } @@ -234,7 +238,12 @@ class InterfaceDesugaring extends ClassVisitor { } return result != null ? new InterfaceInvocationRewriter( - result, isInterface() ? internalName : null, bootclasspath, depsCollector, codeOwner) + result, + isInterface() ? internalName : null, + bootclasspath, + targetLoader, + depsCollector, + codeOwner) : null; } @@ -354,6 +363,7 @@ class InterfaceDesugaring extends ClassVisitor { @Nullable private final String interfaceName; private final ClassReaderFactory bootclasspath; + private final ClassLoader targetLoader; private final DependencyCollector depsCollector; /** Internal name that'll be used to record any dependencies on interface methods. */ private final String declaringClass; @@ -362,11 +372,13 @@ class InterfaceDesugaring extends ClassVisitor { MethodVisitor dest, @Nullable String knownInterfaceName, ClassReaderFactory bootclasspath, + ClassLoader targetLoader, DependencyCollector depsCollector, String declaringClass) { super(Opcodes.ASM6, dest); this.interfaceName = knownInterfaceName; this.bootclasspath = bootclasspath; + this.targetLoader = targetLoader; this.depsCollector = depsCollector; this.declaringClass = declaringClass; } @@ -409,7 +421,16 @@ class InterfaceDesugaring extends ClassVisitor { checkArgument(!owner.endsWith(DependencyCollector.INTERFACE_COMPANION_SUFFIX), "shouldn't consider %s an interface", owner); if (opcode == Opcodes.INVOKESPECIAL) { - // Turn Interface.super.m() into Interface$$CC.m(receiver) + // Turn Interface.super.m() into DefiningInterface$$CC.m(receiver). Note that owner + // always refers to the current type's immediate super-interface, but the default method + // may be inherited by that interface, so we have to figure out where the method is + // defined and invoke it in the corresponding companion class (b/73355452). Note that + // we're always dealing with interfaces here, and all interface methods are public, + // so using Class.getMethods should suffice to find inherited methods. Also note this + // can only be a default method invocation, no abstract method invocation. + owner = + findDefaultMethod(owner, name, desc) + .getDeclaringClass().getName().replace('.', '/'); opcode = Opcodes.INVOKESTATIC; desc = companionDefaultMethodDescriptor(owner, desc); } @@ -421,6 +442,23 @@ class InterfaceDesugaring extends ClassVisitor { } super.visitMethodInsn(opcode, owner, name, desc, itf); } + + private Method findDefaultMethod(String owner, String name, String desc) { + try { + Class clazz = targetLoader.loadClass(owner.replace('/', '.')); + // otherwise getting public methods with getMethods() below isn't enough + checkArgument(clazz.isInterface(), "Not an interface: %s", owner); + for (Method m : clazz.getMethods()) { + if (m.getName().equals(name) && Type.getMethodDescriptor(m).equals(desc)) { + checkState(m.isDefault(), "Found non-default method: %s", m); + return m; + } + } + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Couldn't load " + owner, e); + } + throw new IllegalArgumentException("Method not found: " + owner + "." + name + desc); + } } /** -- cgit v1.2.3