Merge "Refactor DexItemFactory.createCallSite() to handle altMetafactory()"
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index a681943..f830427 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -46,8 +46,8 @@
DexString methodName;
if (lens.isIdentityLens()) {
methodName = callSite.methodName;
- } else if (callSite.interfaceMethod != null) {
- methodName = lens.lookupName(callSite.interfaceMethod);
+ } else if (!callSite.interfaceMethods.isEmpty()) {
+ methodName = lens.lookupName(callSite.interfaceMethods.get(0));
} else {
throw new Unimplemented("Minification of non-lambda InvokeDynamic not supported");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 9e22713..ac954f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -29,22 +29,27 @@
public final DexMethodHandle bootstrapMethod;
public final List<DexValue> bootstrapArgs;
- public final DexMethod interfaceMethod;
+ public final List<DexMethod> interfaceMethods;
private DexEncodedArray encodedArray = null;
- DexCallSite(DexString methodName, DexProto methodProto,
- DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs, DexMethod interfaceMethod) {
+ DexCallSite(
+ DexString methodName,
+ DexProto methodProto,
+ DexMethodHandle bootstrapMethod,
+ List<DexValue> bootstrapArgs,
+ List<DexMethod> interfaceMethods) {
assert methodName != null;
assert methodProto != null;
assert bootstrapMethod != null;
assert bootstrapArgs != null;
+ assert interfaceMethods != null;
this.methodName = methodName;
this.methodProto = methodProto;
this.bootstrapMethod = bootstrapMethod;
this.bootstrapArgs = bootstrapArgs;
- this.interfaceMethod = interfaceMethod;
+ this.interfaceMethods = interfaceMethods;
}
public static DexCallSite fromAsmInvokeDynamic(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 75f2ee8..5fe9134 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.naming.NamingLens;
import com.google.common.base.Strings;
@@ -26,6 +27,7 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -228,6 +230,33 @@
public final DexType annotationSynthesizedClassMap =
createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
+ private static final String METAFACTORY_METHOD_NAME = "metafactory";
+ private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+
+ public final DexType metafactoryType = createType("Ljava/lang/invoke/LambdaMetafactory;");
+ public final DexType callSiteType = createType("Ljava/lang/invoke/CallSite;");
+ public final DexType lookupType = createType("Ljava/lang/invoke/MethodHandles$Lookup;");
+ public final DexType serializableType = createType("Ljava/io/Serializable;");
+
+ public final DexMethod metafactoryMethod =
+ createMethod(
+ metafactoryType,
+ createProto(
+ callSiteType,
+ lookupType,
+ stringType,
+ methodTypeType,
+ methodTypeType,
+ methodHandleType,
+ methodTypeType),
+ createString(METAFACTORY_METHOD_NAME));
+
+ public final DexMethod metafactoryAltMethod =
+ createMethod(
+ metafactoryType,
+ createProto(callSiteType, lookupType, stringType, methodTypeType, objectArrayType),
+ createString(METAFACTORY_ALT_METHOD_NAME));
+
private boolean skipNameValidationForTesting = false;
public void setSkipNameValidationForTesting(boolean skipNameValidationForTesting) {
@@ -545,23 +574,46 @@
DexString methodName, DexProto methodProto,
DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
assert !sorted;
- String bootstrapClass = bootstrapMethod.asMethod().holder.toDescriptorString();
- DexMethod interfaceMethod = null;
- if (bootstrapClass.equals("Ljava/lang/invoke/LambdaMetafactory;")) {
- if (methodName.toString().equals("metafactory")) {
- DexType interfaceType = methodProto.returnType;
- assert bootstrapArgs.size() == 3;
- // bootstrapArgs contains samMethodType, implMethod and instantiatedMethodType.
- DexValueMethodType samMethodType = (DexValueMethodType) bootstrapArgs.get(0);
- interfaceMethod = createMethod(interfaceType, samMethodType.value, methodName);
- }
- // TODO(mathiasr): Support altMetafactory, possibly using ir.desugar.LambdaDescriptor
- }
+ List<DexMethod> interfaceMethods =
+ getCallSiteInterfaceMethods(methodName, methodProto, bootstrapMethod, bootstrapArgs);
DexCallSite callSite =
- new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethod);
+ new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethods);
return canonicalize(callSites, callSite);
}
+ private List<DexMethod> getCallSiteInterfaceMethods(
+ DexString methodName,
+ DexProto methodProto,
+ DexMethodHandle bootstrapMethodHandle,
+ List<DexValue> bootstrapArgs) {
+ // TODO(mathiasr): Unify this with LambdaDescriptor.infer().
+ if (!bootstrapMethodHandle.type.isInvokeStatic()) {
+ return Collections.emptyList();
+ }
+ DexMethod bootstrapMethod = bootstrapMethodHandle.asMethod();
+ if (bootstrapMethod != metafactoryMethod && bootstrapMethod != metafactoryAltMethod) {
+ return Collections.emptyList();
+ }
+ DexType interfaceType = methodProto.returnType;
+ assert bootstrapMethod == metafactoryAltMethod || bootstrapArgs.size() == 3;
+ // Signature of main functional interface method.
+ // In Java docs, this argument is named 'samMethodType'.
+ DexValueMethodType funcErasedSignature = (DexValueMethodType) bootstrapArgs.get(0);
+ DexMethod mainMethod = createMethod(interfaceType, funcErasedSignature.value, methodName);
+ if (bootstrapMethod == metafactoryAltMethod) {
+ List<DexMethod> result = new ArrayList<>();
+ result.add(mainMethod);
+ LambdaDescriptor.extractAltMetafactory(
+ this,
+ bootstrapArgs,
+ type -> result.add(createMethod(type, funcErasedSignature.value, methodName)),
+ bridge -> {});
+ return result;
+ } else {
+ return Collections.singletonList(mainMethod);
+ }
+ }
+
public DexMethod createMethod(DexString clazzDescriptor, DexString name,
DexString returnTypeDescriptor,
DexString[] parameterDescriptors) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 5d52523..49a797a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -21,9 +21,10 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
// Represents the lambda descriptor inferred from calls site.
-final class LambdaDescriptor {
+public final class LambdaDescriptor {
private static final int LAMBDA_ALT_SERIALIZABLE = 1;
private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2;
private static final int LAMBDA_ALT_HAS_BRIDGES = 4;
@@ -227,8 +228,8 @@
}
DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
- boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod;
- boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod;
+ boolean isMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryMethod;
+ boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryAltMethod;
if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
// It is not a lambda, thus no need to manage this call site.
return LambdaDescriptor.MATCH_FAILED;
@@ -240,18 +241,18 @@
// Signature of main functional interface method.
DexValue.DexValueMethodType funcErasedSignature =
- getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class);
+ getBootstrapArgument(callSite.bootstrapArgs, 0, DexValue.DexValueMethodType.class);
// Method handle of the implementation method.
DexMethodHandle lambdaImplMethodHandle =
- getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value;
+ getBootstrapArgument(callSite.bootstrapArgs, 1, DexValue.DexValueMethodHandle.class).value;
// Even though there are some limitations on which method handle kinds are
// allowed for lambda impl-methods, there is no way to detect unsupported
// handle kinds after they are transformed into DEX method handle.
// Signature to be enforced on main method.
DexValue.DexValueMethodType funcEnforcedSignature =
- getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class);
+ getBootstrapArgument(callSite.bootstrapArgs, 2, DexValue.DexValueMethodType.class);
if (!isEnforcedSignatureValid(
rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) {
throw new Unreachable(
@@ -277,67 +278,70 @@
"Unexpected number of metafactory method arguments in " + callSite.toString());
}
} else {
- extractExtraLambdaInfo(rewriter, callSite, match);
+ extractAltMetafactory(
+ rewriter.factory,
+ callSite.bootstrapArgs,
+ interfaceType -> {
+ if (!match.interfaces.contains(interfaceType)) {
+ match.interfaces.add(interfaceType);
+ }
+ },
+ match.bridges::add);
}
return match;
}
- private static void extractExtraLambdaInfo(
- LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) {
+ public static void extractAltMetafactory(
+ DexItemFactory dexItemFactory,
+ List<DexValue> bootstrapArgs,
+ Consumer<DexType> interfaceConsumer,
+ Consumer<DexProto> bridgeConsumer) {
int argIndex = 3;
- int flagsArg = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueInt.class).value;
+ int flagsArg =
+ getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
assert (flagsArg & ~LAMBDA_ALT_MASK) == 0;
// Load extra interfaces if any.
if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) {
- int count = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueInt.class).value;
+ int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
for (int i = 0; i < count; i++) {
- DexType type = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueType.class).value;
- if (!match.interfaces.contains(type)) {
- match.interfaces.add(type);
- }
+ DexType interfaceType =
+ getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueType.class).value;
+ interfaceConsumer.accept(interfaceType);
}
}
// If the lambda is serializable, add it.
if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) {
- if (!match.interfaces.contains(rewriter.serializableType)) {
- match.interfaces.add(rewriter.serializableType);
- }
+ interfaceConsumer.accept(dexItemFactory.serializableType);
}
// Load bridges if any.
if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) {
- int count = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueInt.class).value;
+ int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
for (int i = 0; i < count; i++) {
- DexProto bridgeProto = getBootstrapArgument(
- callSite, argIndex++, DexValue.DexValueMethodType.class).value;
- match.bridges.add(bridgeProto);
+ DexProto bridgeProto =
+ getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueMethodType.class)
+ .value;
+ bridgeConsumer.accept(bridgeProto);
}
}
- if (callSite.bootstrapArgs.size() != argIndex) {
- throw new Unreachable(
- "Unexpected number of metafactory method arguments in " + callSite.toString());
+ if (bootstrapArgs.size() != argIndex) {
+ throw new Unreachable("Unexpected number of metafactory method arguments in DexCallSite");
}
}
@SuppressWarnings("unchecked")
- private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) {
- List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+ private static <T> T getBootstrapArgument(List<DexValue> bootstrapArgs, int i, Class<T> clazz) {
if (bootstrapArgs.size() < i) {
- throw new Unreachable("Expected to find at least "
- + i + " bootstrap arguments in " + callSite.toString());
+ throw new Unreachable(
+ "Expected to find at least " + i + " bootstrap arguments in DexCallSite");
}
DexValue value = bootstrapArgs.get(i);
if (!clazz.isAssignableFrom(value.getClass())) {
- throw new Unreachable("Unexpected type of "
- + "bootstrap arguments #" + i + " in " + callSite.toString());
+ throw new Unreachable("Unexpected type of bootstrap arguments #" + i + " in DexCallSite");
}
return (T) value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index aad7961..82792df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -41,16 +41,8 @@
* lambda class generation, and instruction patching.
*/
public class LambdaRewriter {
- private static final String METAFACTORY_TYPE_DESCR = "Ljava/lang/invoke/LambdaMetafactory;";
- private static final String CALLSITE_TYPE_DESCR = "Ljava/lang/invoke/CallSite;";
- private static final String LOOKUP_TYPE_DESCR = "Ljava/lang/invoke/MethodHandles$Lookup;";
- private static final String METHODTYPE_TYPE_DESCR = "Ljava/lang/invoke/MethodType;";
- private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
- private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
- private static final String METAFACTORY_METHOD_NAME = "metafactory";
- private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
// Public for testing.
@@ -62,12 +54,8 @@
final AppInfo appInfo;
final DexItemFactory factory;
- final DexMethod metafactoryMethod;
final DexMethod objectInitMethod;
- final DexMethod metafactoryAltMethod;
- final DexType serializableType;
-
final DexString constructorName;
final DexString classConstructorName;
final DexString instanceFieldName;
@@ -97,28 +85,11 @@
this.factory = converter.appInfo.dexItemFactory;
this.appInfo = converter.appInfo;
- DexType metafactoryType = factory.createType(METAFACTORY_TYPE_DESCR);
- DexType callSiteType = factory.createType(CALLSITE_TYPE_DESCR);
- DexType lookupType = factory.createType(LOOKUP_TYPE_DESCR);
- DexType methodTypeType = factory.createType(METHODTYPE_TYPE_DESCR);
- DexType methodHandleType = factory.createType(METHODHANDLE_TYPE_DESCR);
-
- this.metafactoryMethod = factory.createMethod(metafactoryType,
- factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
- methodTypeType, methodHandleType, methodTypeType),
- factory.createString(METAFACTORY_METHOD_NAME));
-
- this.metafactoryAltMethod = factory.createMethod(metafactoryType,
- factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
- factory.objectArrayType),
- factory.createString(METAFACTORY_ALT_METHOD_NAME));
-
this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
DexProto initProto = factory.createProto(factory.voidType);
this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
- this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
this.deserializeLambdaMethodProto = factory.createProto(