Synthesize bridge methods from core library interfaces in user-written classes
PiperOrigin-RevId: 223865971
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 4b195b2..6906f9c 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
@@ -20,6 +20,7 @@
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.desugar.io.BitFlags;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
@@ -544,9 +545,17 @@
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
+ Type[] neededArgTypes = neededType.getArgumentTypes();
+ Type[] parameterTypes = Type.getArgumentTypes(desc);
+ for (int i = 0; i < neededArgTypes.length; ++i) {
+ Type arg = neededArgTypes[i];
stubMethod.visitVarInsn(arg.getOpcode(Opcodes.ILOAD), slot);
+ if (!arg.equals(parameterTypes[i])) {
+ checkState(arg.getSort() == Type.ARRAY || arg.getSort() == Type.OBJECT,
+ "Can't cast parameter %s from in bridge for %s.%s%s to %s",
+ i, stubbedInterfaceName, name, desc, arg.getClassName());
+ stubMethod.visitTypeInsn(Opcodes.CHECKCAST, arg.getInternalName());
+ }
slot += arg.getSize();
}
// Just call the bridged method directly on the visited class using invokevirtual
@@ -585,22 +594,52 @@
@Nullable
private Method findBridgedMethod(String name, String desc) {
Type[] paramTypes = Type.getArgumentTypes(desc);
+ Type returnType = Type.getReturnType(desc);
Class<?> itf = loadFromInternal(stubbedInterfaceName);
checkArgument(itf.isInterface(), "Should be an interface: %s", stubbedInterfaceName);
- Method result = null;
+
+ // 1. Find the bridge method we're trying to implement
+ Method bridge = null;
for (Method m : itf.getDeclaredMethods()) {
- if (m.isBridge()) {
+ if (m.isBridge()
+ && m.getName().equals(name)
+ && Arrays.equals(paramTypes, Type.getArgumentTypes(m))
+ && returnType.equals(Type.getReturnType(m))) {
+ bridge = m;
+ break;
+ }
+ }
+ checkState(bridge != null, "Couldn't find bridge %s.%s %s", stubbedInterfaceName, name, desc);
+ checkState(bridge.getParameterCount() == paramTypes.length);
+
+ // 2. Try to find the method being bridged
+ Method result = null;
+ next_method:
+ for (Method m : itf.getDeclaredMethods()) {
+ if (m.isBridge() || Modifier.isStatic(m.getModifiers())) {
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;
+ // All argument types match, only return type will differ: this is the method we want
+ return m;
+ } else if (m.getParameterCount() == bridge.getParameterCount()) {
+ for (int i = 0; i < m.getParameterCount(); ++i) {
+ if (!bridge.getParameterTypes()[i].isAssignableFrom(m.getParameterTypes()[i])) {
+ continue next_method;
+ }
+ }
+
+ // All of m's parameter types are subtypes of the bridge's parameter types (or primitives)
+ if (result == null) {
+ result = m;
+ } else {
+ // Bail if we find multiple methods that could be bridged
+ return null;
+ }
}
}
return result;