diff options
author | kmb <kmb@google.com> | 2018-03-26 18:38:07 -0700 |
---|---|---|
committer | Ivan Gavrilovic <gavra@google.com> | 2018-05-04 10:40:23 +0100 |
commit | 6227f0cb24cbe58be9847a356e97205a5ba17f61 (patch) | |
tree | 65d1bc6be8865f7410780c5155463e594394ab00 | |
parent | faa0690d2152c090607a8db136ba9104bc22bb4e (diff) | |
download | desugar-6227f0cb24cbe58be9847a356e97205a5ba17f61.tar.gz |
stub simple core library bridge methods that only differ in return type
RELNOTES: None.
PiperOrigin-RevId: 190559240
GitOrigin-RevId: 327c74df7c3b4820a0620bf9696c3f88bffebda3
Change-Id: I0f9a4718ff0e8714e3133ecf0ef528bb7a039bba
-rw-r--r-- | java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java | 85 |
1 files changed, 72 insertions, 13 deletions
diff --git a/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java b/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java index f0fc6d1..1de48bf 100644 --- a/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java +++ b/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java @@ -19,6 +19,8 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.collect.ImmutableList; import com.google.devtools.build.android.desugar.io.BitFlags; +import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; @@ -513,27 +515,84 @@ public class DefaultMethodClassFixer extends ClassVisitor { stubMethod.visitMaxs(0, 0); // rely on class writer to compute these stubMethod.visitEnd(); - return null; + return null; // don't visit the visited interface's default method } else if (shouldStubAsBridgeDefaultMethod(access, name, desc)) { recordIfInstanceMethod(access, name, desc); + MethodVisitor stubMethod = + DefaultMethodClassFixer.this.visitMethod(access, name, desc, (String) null, exceptions); // If we're visiting a bootclasspath interface then we most likely don't have the code. // That means we can't just copy the method bodies as we're trying to do below. - checkState(!isBootclasspathInterface, - "TODO stub core interface %s bridge methods in %s", stubbedInterfaceName, internalName); - // For bridges we just copy their bodies instead of going through the companion class. - // Meanwhile, we also need to desugar the copied method bodies, so that any calls to - // interface methods are correctly handled. - return new InterfaceDesugaring.InterfaceInvocationRewriter( - DefaultMethodClassFixer.this.visitMethod(access, name, desc, (String) null, exceptions), - stubbedInterfaceName, - bootclasspath, - targetLoader, - depsCollector, - internalName); + if (isBootclasspathInterface) { + // Synthesize a "bridge" method that calls the true implementation + Method bridged = findBridgedMethod(name, desc); + checkState(bridged != null, + "TODO: Can't stub core interface bridge method %s.%s %s in %s", + stubbedInterfaceName, name, desc, internalName); + + int slot = 0; + stubMethod.visitVarInsn(Opcodes.ALOAD, slot++); // load the receiver + Type neededType = Type.getType(bridged); + for (Type arg : neededType.getArgumentTypes()) { + // TODO(b/73586397): insert downcasts if necessary + stubMethod.visitVarInsn(arg.getOpcode(Opcodes.ILOAD), slot); + slot += arg.getSize(); + } + // Just call the bridged method directly on the visited class using invokevirtual + stubMethod.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + internalName, + name, + neededType.getDescriptor(), + /*itf=*/ false); + stubMethod.visitInsn(neededType.getReturnType().getOpcode(Opcodes.IRETURN)); + + stubMethod.visitMaxs(0, 0); // rely on class writer to compute these + stubMethod.visitEnd(); + return null; // don't visit the visited interface's bridge method + } else { + // For bridges we just copy their bodies instead of going through the companion class. + // Meanwhile, we also need to desugar the copied method bodies, so that any calls to + // interface methods are correctly handled. + return new InterfaceDesugaring.InterfaceInvocationRewriter( + stubMethod, + stubbedInterfaceName, + bootclasspath, + targetLoader, + depsCollector, + internalName); + } } else { return null; // not a default or bridge method or the class already defines this method. } } + + /** + * Returns a non-bridge interface method with given name that a method with the given descriptor + * can bridge to, if any such method can be found. + */ + @Nullable + private Method findBridgedMethod(String name, String desc) { + Type[] paramTypes = Type.getArgumentTypes(desc); + Class<?> itf = loadFromInternal(stubbedInterfaceName); + checkArgument(itf.isInterface(), "Should be an interface: %s", stubbedInterfaceName); + Method result = null; + for (Method m : itf.getDeclaredMethods()) { + if (m.isBridge()) { + continue; + } + if (!m.getName().equals(name)) { + continue; + } + // For now, only support specialized return types (which don't require casts) + // TODO(b/73586397): Make this work for other kinds of bridges in core library interfaces + if (Arrays.equals(paramTypes, Type.getArgumentTypes(m))) { + checkState(result == null, + "Found multiple bridge target %s and %s for descriptor %s", result, m, desc); + return result = m; + } + } + return result; + } } /** |