diff options
Diffstat (limited to 'java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java')
-rw-r--r-- | java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java | 42 |
1 files changed, 40 insertions, 2 deletions
diff --git a/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java b/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java index 3524fae..f17f114 100644 --- a/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java +++ b/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); + } } /** |