Treat non-lambda metafactory argument method handles specially.
They can reach MethodHandle.invokeExact calls which match exact
method signatures. Therefore, we cannot perform member rebinding
on the methods in method handles because that could (and likely
would) change the receiver type making the matching fail.
Therefore, we keep the original receiver type for all such
method handles. That also means that we need to keep those
receiver types in the output and cannot perform merging or
other operations that would make those types disappear.
R=christofferqa@google.com, sgjesse@google.com, tamaskenez@google.com
Change-Id: Ic31373f574493b6941acca176f39decf0ef12182
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index c762182..42fed08 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -67,6 +68,10 @@
class UseCollector extends UseRegistry {
+ UseCollector(DexItemFactory factory) {
+ super(factory);
+ }
+
@Override
public boolean registerInvokeVirtual(DexMethod method) {
DexEncodedMethod target = appInfo.lookupVirtualTarget(method.holder, method);
@@ -243,7 +248,7 @@
}
private void analyze() {
- UseCollector useCollector = new UseCollector();
+ UseCollector useCollector = new UseCollector(appInfo.dexItemFactory);
for (DexProgramClass dexProgramClass : application.classes()) {
useCollector.registerSuperType(dexProgramClass, dexProgramClass.superType);
for (DexType implementsType : dexProgramClass.interfaces.values) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 3122404..c36bd57 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -37,7 +38,7 @@
@Override
public void registerUse(UseRegistry registry, DexType clazz) {
- registry.registerMethodHandle(handle);
+ registry.registerMethodHandle(handle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
index bad4e47..fd17fef 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.ShortBuffer;
@@ -57,7 +58,8 @@
@Override
public void registerUse(UseRegistry registry) {
- registry.registerMethodHandle(getMethodHandle());
+ registry.registerMethodHandle(
+ getMethodHandle(), MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java b/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java
index 7f31098..bb209b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java
@@ -6,6 +6,10 @@
public class DefaultUseRegistry extends UseRegistry {
+ public DefaultUseRegistry(DexItemFactory factory) {
+ super(factory);
+ }
+
@Override
public boolean registerInvokeVirtual(DexMethod method) {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java b/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java
index 0e28d3d..1375e9d 100644
--- a/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java
@@ -6,7 +6,8 @@
public class DelegatingUseRegistry extends UseRegistry {
private final UseRegistry delegate;
- public DelegatingUseRegistry(UseRegistry delegate) {
+ public DelegatingUseRegistry(DexItemFactory factory, UseRegistry delegate) {
+ super(factory);
this.delegate = delegate;
}
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 3e833ad..403790c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -309,6 +309,10 @@
return skipNameValidationForTesting;
}
+ public boolean isLambdaMetafactoryMethod(DexMethod dexMethod) {
+ return dexMethod == metafactoryMethod || dexMethod == metafactoryAltMethod;
+ }
+
public synchronized void clearSubtypeInformation() {
types.values().forEach(DexType::clearSubtypeInformation);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 2a49835..3aba90c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -42,12 +42,24 @@
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
+ if (collectIndexedItemsExceptName(indexedItems, method, instructionOffset)) {
+ collectIndexedItemsName(indexedItems, method, instructionOffset);
+ }
+ }
+
+ public boolean collectIndexedItemsExceptName(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
if (indexedItems.addMethod(this)) {
holder.collectIndexedItems(indexedItems, method, instructionOffset);
proto.collectIndexedItems(indexedItems, method, instructionOffset);
- indexedItems.getRenamedName(this).collectIndexedItems(
- indexedItems, method, instructionOffset);
+ return true;
}
+ return false;
+ }
+
+ public void collectIndexedItemsName(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
+ indexedItems.getRenamedName(this).collectIndexedItems(indexedItems, method, instructionOffset);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index bfdc1b7..3af0a41 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.naming.NamingLens;
+import java.util.Objects;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
@@ -183,14 +184,37 @@
}
}
- public MethodHandleType type;
- public Descriptor<? extends DexItem, ? extends Descriptor<?,?>> fieldOrMethod;
+ public final MethodHandleType type;
+
+ // Field or method that the method handle is targeting.
+ public final Descriptor<? extends DexItem, ? extends Descriptor<?,?>> fieldOrMethod;
+
+ // If the method handle is of method type and is not an argument to a lambda metafactory
+ // the method handle could flow to an invokeExact instruction which does equality checking
+ // on method descriptors including the receiver. Therefore, for such method handles we
+ // cannot perform rewriting of the receiver as that will make the invokeExact invocation
+ // fail due to type mismatch. Therefore, fieldOrMethod will contain the method handle
+ // as we want it in the output with the original receiver. That means that member rebinding
+ // has not been applied to fieldOrMethod. Since renaming happens on member rebound dex methods
+ // we need to record the member rebound target as well for naming. That is what rewrittenTarget
+ // is for.
+ public final DexMethod rewrittenTarget;
public DexMethodHandle(
MethodHandleType type,
Descriptor<? extends DexItem, ? extends Descriptor<?,?>> fieldOrMethod) {
this.type = type;
this.fieldOrMethod = fieldOrMethod;
+ this.rewrittenTarget = null;
+ }
+
+ public DexMethodHandle(
+ MethodHandleType type,
+ Descriptor<? extends DexItem, ? extends Descriptor<?, ?>> fieldOrMethod,
+ DexMethod rewrittenTarget) {
+ this.type = type;
+ this.fieldOrMethod = fieldOrMethod;
+ this.rewrittenTarget = rewrittenTarget;
}
public static DexMethodHandle fromAsmHandle(
@@ -205,14 +229,18 @@
@Override
public int computeHashCode() {
- return type.hashCode() + fieldOrMethod.computeHashCode() * 7;
+ return type.hashCode()
+ + fieldOrMethod.computeHashCode() * 7
+ + Objects.hashCode(rewrittenTarget) * 13;
}
@Override
public boolean computeEquals(Object other) {
if (other instanceof DexMethodHandle) {
DexMethodHandle o = (DexMethodHandle) other;
- return type.equals(o.type) && fieldOrMethod.equals(o.fieldOrMethod);
+ return type.equals(o.type)
+ && fieldOrMethod.equals(o.fieldOrMethod)
+ && Objects.equals(rewrittenTarget, o.rewrittenTarget);
}
return false;
}
@@ -231,7 +259,17 @@
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
if (indexedItems.addMethodHandle(this)) {
- fieldOrMethod.collectIndexedItems(indexedItems, method, instructionOffset);
+ if (fieldOrMethod.isDexMethod() && rewrittenTarget != null) {
+ // If there is a rewritten target we need to use that to get the right name of the
+ // targeted method (only member rebound methods take part in naming). The rest of the
+ // indexed items are collected from fieldOrMethod.
+ if (fieldOrMethod.asDexMethod().collectIndexedItemsExceptName(
+ indexedItems, method, instructionOffset)) {
+ rewrittenTarget.collectIndexedItemsName(indexedItems, method, instructionOffset);
+ }
+ } else {
+ fieldOrMethod.collectIndexedItems(indexedItems, method, instructionOffset);
+ }
}
}
@@ -323,7 +361,10 @@
if (isMethodHandle()) {
DexMethod method = asMethod();
owner = lens.lookupInternalName(method.holder);
- name = lens.lookupName(method).toString();
+ name =
+ rewrittenTarget != null
+ ? lens.lookupName(rewrittenTarget).toString()
+ : lens.lookupName(method).toString();
desc = method.proto.toDescriptorString(lens);
if (method.holder.toDescriptorString().equals("Ljava/lang/invoke/LambdaMetafactory;")) {
itf = false;
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index f8608d6..ccf790b 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -14,6 +14,17 @@
public abstract class UseRegistry {
+ private DexItemFactory factory;
+
+ public enum MethodHandleUse {
+ ARGUMENT_TO_LAMBDA_METAFACTORY,
+ NOT_ARGUMENT_TO_LAMBDA_METAFACTORY
+ }
+
+ public UseRegistry(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
public abstract boolean registerInvokeVirtual(DexMethod method);
public abstract boolean registerInvokeDirect(DexMethod method);
@@ -44,7 +55,8 @@
return registerTypeReference(type);
}
- public void registerMethodHandle(DexMethodHandle methodHandle) {
+ public void registerMethodHandle(
+ DexMethodHandle methodHandle, MethodHandleUse use) {
switch (methodHandle.type) {
case INSTANCE_GET:
registerInstanceFieldRead(methodHandle.asField());
@@ -84,7 +96,11 @@
}
public void registerCallSite(DexCallSite callSite) {
- registerMethodHandle(callSite.bootstrapMethod);
+ boolean isLambdaMetaFactory =
+ factory.isLambdaMetafactoryMethod(callSite.bootstrapMethod.asMethod());
+
+ registerMethodHandle(
+ callSite.bootstrapMethod, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
// Lambda metafactory will use this type as the main SAM
// interface for the dynamically created lambda class.
@@ -96,7 +112,11 @@
if (arg instanceof DexValueType) {
registerTypeReference(((DexValueType) arg).value);
} else if (arg instanceof DexValueMethodHandle) {
- registerMethodHandle(((DexValueMethodHandle) arg).value);
+ DexMethodHandle handle = ((DexValueMethodHandle) arg).value;
+ MethodHandleUse use = isLambdaMetaFactory
+ ? MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY
+ : MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+ registerMethodHandle(handle, use);
} else if (arg instanceof DexValueMethodType) {
registerProto(((DexValueMethodType) arg).value);
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 6a302c2..1ee2f6b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -485,6 +485,7 @@
InvokeExtractor(AppInfoWithLiveness appInfo, GraphLense graphLense, Node caller,
CallGraph graph) {
+ super(appInfo.dexItemFactory);
this.appInfo = appInfo;
this.graphLense = graphLense;
this.caller = caller;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 52ac6ce..ef490f7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -3,9 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY;
+import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
@@ -19,9 +21,11 @@
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstMethodHandle;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstanceOf;
@@ -29,7 +33,6 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMultiNewArray;
@@ -81,9 +84,15 @@
DexProto newMethodProto =
appInfo.dexItemFactory.applyClassMappingToProto(
callSite.methodProto, graphLense::lookupType, protoFixupCache);
- DexMethodHandle newBootstrapMethod =
- rewriteDexMethodHandle(callSite.bootstrapMethod, method);
- List<DexValue> newArgs = rewriteBootstrapArgs(callSite.bootstrapArgs, method);
+ DexMethodHandle newBootstrapMethod = rewriteDexMethodHandle(
+ callSite.bootstrapMethod, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+ boolean isLambdaMetaFactory =
+ appInfo.dexItemFactory.isLambdaMetafactoryMethod(callSite.bootstrapMethod.asMethod());
+ MethodHandleUse methodHandleUse = isLambdaMetaFactory
+ ? ARGUMENT_TO_LAMBDA_METAFACTORY
+ : NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+ List<DexValue> newArgs =
+ rewriteBootstrapArgs(callSite.bootstrapArgs, method, methodHandleUse);
if (!newMethodProto.equals(callSite.methodProto)
|| newBootstrapMethod != callSite.bootstrapMethod
|| !newArgs.equals(callSite.bootstrapArgs)) {
@@ -94,6 +103,17 @@
invokeCustom.inValues());
iterator.replaceCurrentInstruction(newInvokeCustom);
}
+ } else if (current.isConstMethodHandle()) {
+ DexMethodHandle handle = current.asConstMethodHandle().getValue();
+ DexMethodHandle newHandle = rewriteDexMethodHandle(
+ handle, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+ if (newHandle != handle) {
+ ConstMethodHandle newInstruction =
+ new ConstMethodHandle(
+ code.createValue(current.outType(), current.getLocalInfo()),
+ newHandle);
+ iterator.replaceCurrentInstruction(newInstruction);
+ }
} else if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
@@ -227,7 +247,7 @@
}
private List<DexValue> rewriteBootstrapArgs(
- List<DexValue> bootstrapArgs, DexEncodedMethod method) {
+ List<DexValue> bootstrapArgs, DexEncodedMethod method, MethodHandleUse use) {
List<DexValue> newBoostrapArgs = null;
boolean changed = false;
for (int i = 0; i < bootstrapArgs.size(); i++) {
@@ -235,7 +255,8 @@
DexValue newArgument = null;
if (argument instanceof DexValueMethodHandle) {
DexMethodHandle oldHandle = ((DexValueMethodHandle) argument).value;
- DexMethodHandle newHandle = rewriteDexMethodHandle(oldHandle, method);
+ DexMethodHandle newHandle =
+ rewriteDexMethodHandle(oldHandle, method, use);
if (newHandle != oldHandle) {
newArgument = new DexValueMethodHandle(newHandle);
}
@@ -268,26 +289,45 @@
}
private DexMethodHandle rewriteDexMethodHandle(
- DexMethodHandle methodHandle, DexEncodedMethod context) {
+ DexMethodHandle methodHandle, DexEncodedMethod context, MethodHandleUse use) {
if (methodHandle.isMethodHandle()) {
DexMethod invokedMethod = methodHandle.asMethod();
MethodHandleType oldType = methodHandle.type;
GraphLenseLookupResult lenseLookup =
graphLense.lookupMethod(invokedMethod, context, oldType.toInvokeType());
- DexMethod actualTarget = lenseLookup.getMethod();
- MethodHandleType newType = lenseLookup.getType().toMethodHandle(actualTarget);
- if (actualTarget != invokedMethod) {
- DexClass clazz = appInfo.definitionFor(actualTarget.holder);
- if (clazz != null && (oldType.isInvokeInterface() || oldType.isInvokeInstance())) {
- newType =
- lenseLookup.getType() == Type.INTERFACE
- ? MethodHandleType.INVOKE_INTERFACE
- : MethodHandleType.INVOKE_INSTANCE;
+ DexMethod rewrittenTarget = lenseLookup.getMethod();
+ DexMethod actualTarget;
+ MethodHandleType newType;
+ if (use == ARGUMENT_TO_LAMBDA_METAFACTORY) {
+ // Lambda metafactory arguments will be lambda desugared away and therefore cannot flow
+ // to a MethodHandle.invokeExact call. We can therefore member-rebind with no issues.
+ actualTarget = rewrittenTarget;
+ newType = lenseLookup.getType().toMethodHandle(actualTarget);
+ } else {
+ assert use == NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+ // MethodHandles that are not arguments to a lambda metafactory will not be desugared
+ // away. Therefore they could flow to a MethodHandle.invokeExact call which means that
+ // we cannot member rebind. We therefore keep the receiver and also pin the receiver
+ // with a keep rule (see Enqueuer.registerMethodHandle).
+ actualTarget =
+ appInfo.dexItemFactory.createMethod(
+ invokedMethod.holder, rewrittenTarget.proto, rewrittenTarget.name);
+ newType = oldType;
+ if (oldType.isInvokeDirect()) {
+ // For an invoke direct, the rewritten target must have the same holder as the original.
+ // If the method has changed from private to public we need to use virtual instead of
+ // direct.
+ assert rewrittenTarget.holder == actualTarget.holder;
+ newType = lenseLookup.getType().toMethodHandle(actualTarget);
+ assert newType == MethodHandleType.INVOKE_DIRECT
+ || newType == MethodHandleType.INVOKE_INSTANCE;
}
- return new DexMethodHandle(newType, actualTarget);
}
- if (oldType != newType) {
- return new DexMethodHandle(newType, actualTarget);
+ if (newType != oldType || actualTarget != invokedMethod || rewrittenTarget != actualTarget) {
+ return new DexMethodHandle(
+ newType,
+ actualTarget,
+ rewrittenTarget != actualTarget ? rewrittenTarget : null);
}
} else {
DexField field = methodHandle.asField();
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 9d16497..e16e2d5 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
@@ -239,9 +239,7 @@
DexItemFactory factory = appInfo.dexItemFactory;
DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
- boolean isMetafactoryMethod = bootstrapMethod == factory.metafactoryMethod;
- boolean isAltMetafactoryMethod = bootstrapMethod == factory.metafactoryAltMethod;
- if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
+ if (!factory.isLambdaMetafactoryMethod(bootstrapMethod)) {
// It is not a lambda, thus no need to manage this call site.
return LambdaDescriptor.MATCH_FAILED;
}
@@ -283,7 +281,7 @@
funcMethodName, funcErasedSignature.value, funcEnforcedSignature.value,
lambdaImplMethodHandle, mainFuncInterface, captures);
- if (isMetafactoryMethod) {
+ if (bootstrapMethod == factory.metafactoryMethod) {
if (callSite.bootstrapArgs.size() != 3) {
throw new Unreachable(
"Unexpected number of metafactory method arguments in " + callSite.toString());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 9b5af03..eaa5037 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -299,7 +299,7 @@
if (instruction.isInvokeCustom()) {
// Just invalidate any candidates referenced from non-static context.
- CallSiteReferencesInvalidator invalidator = new CallSiteReferencesInvalidator();
+ CallSiteReferencesInvalidator invalidator = new CallSiteReferencesInvalidator(factory);
invalidator.registerCallSite(instruction.asInvokeCustom().getCallSite());
continue;
}
@@ -553,6 +553,11 @@
}
private class CallSiteReferencesInvalidator extends UseRegistry {
+
+ private CallSiteReferencesInvalidator(DexItemFactory factory) {
+ super(factory);
+ }
+
private boolean registerMethod(DexMethod method) {
registerTypeReference(method.holder);
registerProto(method.proto);
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index 0b67945..3395753 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -60,8 +61,9 @@
registry.registerConstClass(application.getType((Type) cst));
}
} else if (cst instanceof Handle) {
+ DexMethodHandle handle = DexMethodHandle.fromAsmHandle((Handle) cst, application, clazz);
registry.registerMethodHandle(
- DexMethodHandle.fromAsmHandle((Handle) cst, application, clazz));
+ handle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
index 46fdc82..2fa7f41 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -39,7 +39,8 @@
// The tree pruner can mark bridge methods abstract if they are not reachable but cannot
// be removed.
if (method.accessFlags.isBridge() && !method.accessFlags.isAbstract()) {
- InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+ InvokeSingleTargetExtractor targetExtractor =
+ new InvokeSingleTargetExtractor(appInfo.dexItemFactory);
method.getCode().registerCodeReferences(targetExtractor);
DexMethod target = targetExtractor.getTarget();
InvokeKind kind = targetExtractor.getKind();
diff --git a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
index 8dc8a73..8a65562 100644
--- a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
+++ b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.optimize;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
@@ -12,7 +13,8 @@
private InvokeKind kind = InvokeKind.NONE;
private DexMethod target;
- public InvokeSingleTargetExtractor() {
+ public InvokeSingleTargetExtractor(DexItemFactory factory) {
+ super(factory);
}
private boolean setTarget(DexMethod target, InvokeKind kind) {
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index ebf110e..debba04 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -31,7 +31,8 @@
private void identifyBridgeMethod(DexEncodedMethod method) {
MethodAccessFlags accessFlags = method.accessFlags;
if (accessFlags.isBridge() && !accessFlags.isAbstract()) {
- InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+ InvokeSingleTargetExtractor targetExtractor =
+ new InvokeSingleTargetExtractor(appInfo.dexItemFactory);
method.getCode().registerCodeReferences(targetExtractor);
DexMethod target = targetExtractor.getTarget();
InvokeKind kind = targetExtractor.getKind();
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 249eb74..41551e7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -308,7 +308,8 @@
private final DexEncodedMethod currentMethod;
- private UseRegistry(DexEncodedMethod currentMethod) {
+ private UseRegistry(DexItemFactory factory, DexEncodedMethod currentMethod) {
+ super(factory);
this.currentMethod = currentMethod;
}
@@ -485,6 +486,23 @@
}
@Override
+ public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
+ super.registerMethodHandle(methodHandle, use);
+ // If a method handle is not an argument to a lambda metafactory it could flow to a
+ // MethodHandle.invokeExact invocation. For that to work, the receiver type cannot have
+ // changed and therefore we cannot perform member rebinding. For these handles, we maintain
+ // the receiver for the method handle. Therefore, we have to make sure that the receiver
+ // stays in the output (and is not class merged). To ensure that we treat the receiver
+ // as instantiated.
+ if (methodHandle.isMethodHandle() && use != MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY) {
+ DexClass holder = appInfo.definitionFor(methodHandle.asMethod().holder);
+ if (holder != null) {
+ markClassAsInstantiatedWithMethodHandleRule(holder);
+ }
+ }
+ }
+
+ @Override
public void registerCallSite(DexCallSite callSite) {
callSites.add(callSite);
super.registerCallSite(callSite);
@@ -1393,7 +1411,7 @@
markParameterAndReturnTypesAsLive(method);
processAnnotations(method.annotations.annotations);
method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
- method.registerCodeReferences(new UseRegistry(method));
+ method.registerCodeReferences(new UseRegistry(options.itemFactory, method));
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(method));
}
@@ -1478,6 +1496,13 @@
collectReachedFields(staticFields, this::tryLookupStaticField)));
}
+ private void markClassAsInstantiatedWithMethodHandleRule(DexClass clazz) {
+ ProguardKeepRule rule =
+ ProguardConfigurationUtils.buildMethodHandleKeepRule(clazz);
+ proguardCompatibilityWorkList.add(
+ Action.markInstantiated(clazz, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
+ }
+
private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
ProguardKeepRule rule =
ProguardConfigurationUtils.buildDefaultInitializerKeepRule(clazz);
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index 8e5e12a..7d8b604 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
@@ -38,8 +39,7 @@
private final Set<DexType> baseClasses;
private final AppInfoWithSubtyping appInfo;
private final Set<DexType> mainDexTypes = new HashSet<>();
- private final DirectReferencesCollector codeDirectReferenceCollector =
- new DirectReferencesCollector();
+ private final DirectReferencesCollector codeDirectReferenceCollector;
private final AnnotationDirectReferenceCollector annotationDirectReferenceCollector =
new AnnotationDirectReferenceCollector();
private final Map<DexType, Boolean> annotationTypeContainEnum;
@@ -52,6 +52,7 @@
public MainDexListBuilder(Set<DexType> baseClasses, DexApplication application) {
this.dexApplication = application;
this.appInfo = new AppInfoWithSubtyping(dexApplication);
+ this.codeDirectReferenceCollector = new DirectReferencesCollector(appInfo.dexItemFactory);
this.baseClasses =
baseClasses.stream().filter(this::isProgramClass).collect(Collectors.toSet());
DexClass enumType = appInfo.definitionFor(appInfo.dexItemFactory.enumType);
@@ -198,8 +199,8 @@
private class DirectReferencesCollector extends UseRegistry {
-
- private DirectReferencesCollector() {
+ private DirectReferencesCollector(DexItemFactory factory) {
+ super(factory);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index f993328..2ffdd3a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -18,6 +18,19 @@
import java.util.stream.Collectors;
public class ProguardConfigurationUtils {
+
+ public static ProguardKeepRule buildMethodHandleKeepRule(DexClass clazz) {
+ ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
+ builder.setType(ProguardKeepRuleType.KEEP);
+ builder.getModifiersBuilder().setAllowsObfuscation(true);
+ builder.getModifiersBuilder().setAllowsOptimization(true);
+ builder.setClassType(
+ clazz.isInterface() ? ProguardClassType.INTERFACE : ProguardClassType.CLASS);
+ builder.setClassNames(
+ ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
+ return builder.build();
+ }
+
public static ProguardKeepRule buildDefaultInitializerKeepRule(DexClass clazz) {
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setType(ProguardKeepRuleType.KEEP);
@@ -25,9 +38,8 @@
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
builder.setClassType(ProguardClassType.CLASS);
- ProguardClassNameList.Builder classNameListBuilder = ProguardClassNameList.builder();
- classNameListBuilder.addClassName(false, ProguardTypeMatcher.create(clazz.type));
- builder.setClassNames(classNameListBuilder.build());
+ builder.setClassNames(
+ ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
if (clazz.hasDefaultInitializer()) {
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.INIT);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 84ccf84..783e133 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
@@ -442,7 +443,7 @@
// Check that all accesses from [source] to classes or members from the current package of
// [source] will continue to work. This is guaranteed if the methods of [source] do not access
// any private or protected classes or members from the current package of [source].
- IllegalAccessDetector registry = new IllegalAccessDetector(source);
+ IllegalAccessDetector registry = new IllegalAccessDetector(options.itemFactory, source);
for (DexEncodedMethod method : source.methods()) {
method.registerCodeReferences(registry);
if (registry.foundIllegalAccess()) {
@@ -1603,7 +1604,8 @@
private boolean foundIllegalAccess = false;
private DexClass source;
- public IllegalAccessDetector(DexClass source) {
+ public IllegalAccessDetector(DexItemFactory factory, DexClass source) {
+ super(factory);
this.source = source;
}
diff --git a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
index 449d426..64405bb 100644
--- a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
+++ b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
@@ -9,6 +9,20 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+class ClassWithLongName {
+ @Override
+ public String toString() {
+ return getClass().getName();
+ }
+}
+
+class AnotherClassWithALongName {
+ @Override
+ public String toString() {
+ return getClass().getName();
+ }
+}
+
interface J {
default void targetMethodTest8() {
@@ -38,6 +52,11 @@
default void targetMethodTest10() {
System.out.println("targetMethodTest10 from I");
}
+
+ default AnotherClassWithALongName targetMethodTest11(ClassWithLongName a) {
+ System.out.println("targetMethodTest11 from I");
+ return new AnotherClassWithALongName();
+ }
}
abstract class Super {
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
index e42b9c9..05a6f28 100644
--- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -57,6 +57,7 @@
generateMethodTest11(cw);
generateMethodTest12(cw);
generateMethodTest13(cw);
+ generateMethodTest14(cw);
generateMethodMain(cw);
super.visitEnd();
}
@@ -97,6 +98,8 @@
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test12", "()V", false);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test13", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test14", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
@@ -424,4 +427,43 @@
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke instance taking an argument and returning a result. This should
+ * work through renaming.
+ */
+ private void generateMethodTest14(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test14", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(ClassWithLongName.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL,
+ Type.getInternalName(ClassWithLongName.class),
+ "<init>",
+ "()V",
+ false);
+ mv.visitInvokeDynamicInsn(
+ "targetMethodTest11",
+ "(Linvokecustom/InvokeCustom;Linvokecustom/ClassWithLongName;)"
+ + "Linvokecustom/AnotherClassWithALongName;",
+ bootstrap,
+ new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(InvokeCustom.class),
+ "targetMethodTest11",
+ "(Linvokecustom/ClassWithLongName;)Linvokecustom/AnotherClassWithALongName;",
+ false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
}
diff --git a/src/test/examplesAndroidO/invokecustom/keep-rules.txt b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
index bcefc4b..54a5682 100644
--- a/src/test/examplesAndroidO/invokecustom/keep-rules.txt
+++ b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
@@ -8,7 +8,7 @@
public static void main(...);
}
--keepclasseswithmembers class * {
+-keepclassmembers class * {
*** targetMethodTest*(...);
}
diff --git a/src/test/examplesAndroidO/invokecustom2/keep-rules.txt b/src/test/examplesAndroidO/invokecustom2/keep-rules.txt
new file mode 100644
index 0000000..629ec41
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom2/keep-rules.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point and the methods that are targets for invoke custom invocations.
+# Get rid of everything that is not reachable from there.
+-keep public class invokecustom2.InvokeCustom {
+ public static void main(...);
+}
+
+-keepclassmembers class * {
+ *** targetMethodTest*(...);
+}
+
+-allowaccessmodification
diff --git a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
index e8447a3..c3cb86f 100644
--- a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
+++ b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
@@ -9,6 +9,41 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+class ArgumentType {
+}
+
+class ReturnType {
+}
+
+interface I {
+ default ReturnType targetMethodTest4(ArgumentType arg) {
+ System.out.println("I.targetMethodTest4");
+ return new ReturnType();
+ }
+}
+
+class Middle implements I {
+}
+
+class Sub extends Middle {
+}
+
+interface I2 {
+ default ReturnType targetMethodTest5(ArgumentType arg) {
+ System.out.println("I2.targetMethodTest5");
+ return new ReturnType();
+ }
+}
+
+// TODO(116283747): Add the same test where the interface method is not overriden but inherited
+// from the interface. Currently, that works on the reference implementation but fails on Art.
+class Impl implements I2 {
+ @Override
+ public ReturnType targetMethodTest5(ArgumentType arg) {
+ System.out.println("Impl.targetMethodTest5");
+ return new ReturnType();
+ }
+}
public class InvokeCustom {
@@ -35,4 +70,12 @@
final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
return new ConstantCallSite(targetMH.asType(type));
}
+
+ public static void doInvokeSubWithArg(MethodHandle handle) throws Throwable {
+ handle.invoke(new Sub(), new ArgumentType());
+ }
+
+ public static void doInvokeExactImplWithArg(MethodHandle handle) throws Throwable {
+ ReturnType result = (ReturnType) handle.invokeExact(new Impl(), new ArgumentType());
+ }
}
diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
index 7ee4bdc..495f080 100644
--- a/src/test/examplesAndroidP/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
@@ -46,6 +46,8 @@
public void visitEnd() {
generateMethodTest1(cw);
generateMethodTest2(cw);
+ generateMethodTest3(cw);
+ generateMethodTest4(cw);
generateMethodMain(cw);
super.visitEnd();
}
@@ -64,20 +66,24 @@
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test3", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test4", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
/**
- * Generate test with an invokedynamic, a static bootstrap method without extra args and
- * args to the target method.
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and
+ * args to the target method.
*/
private void generateMethodTest1(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
null, null);
MethodType mt = MethodType.methodType(
CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
- Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
"bsmLookupStatic", mt.toMethodDescriptorString(), false);
mv.visitLdcInsn(new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
"targetMethodTest1", "()V", false));
@@ -91,15 +97,15 @@
}
/**
- * Generate test with an invokedynamic, a static bootstrap method without extra args and
- * args to the target method.
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and
+ * args to the target method.
*/
private void generateMethodTest2(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
null, null);
MethodType mt = MethodType.methodType(
CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
- Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
"bsmLookupStatic", mt.toMethodDescriptorString(), false);
mv.visitLdcInsn(Type.getMethodType("(ZBSCIFJDLjava/lang/String;)Ljava/lang/Object;"));
mv.visitInvokeDynamicInsn("targetMethodTest3", "(Ljava/lang/invoke/MethodType;)V",
@@ -107,4 +113,59 @@
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
+
+ /**
+ * Generate test with a const method handle pointing to the middle of a class hierarchy.
+ * Call a static method with the method handle which will do a MethodHandle.invoke call on
+ * a sub class instance.
+ *
+ * Tests that the const method handle is rewritten when renaming. Also tests that the
+ * middle class does not disappear (for instance via class merging).
+ */
+ private void generateMethodTest3(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(ReturnType.class, ArgumentType.class);
+ Handle invokeWithArg = new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(Middle.class),
+ "targetMethodTest4",
+ mt.toMethodDescriptorString(),
+ false);
+ mv.visitLdcInsn(invokeWithArg);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ Type.getInternalName(InvokeCustom.class),
+ "doInvokeSubWithArg",
+ "(Ljava/lang/invoke/MethodHandle;)V",
+ false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with a const method handle pointing to a class which inherits the method from
+ * the super class. Call a static method with the method handle which will do a
+ * MethodHandle.invokeExact.
+ */
+ private void generateMethodTest4(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(ReturnType.class, ArgumentType.class);
+ Handle invokeExactWithArg = new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(Impl.class),
+ "targetMethodTest5",
+ mt.toMethodDescriptorString(),
+ false);
+ mv.visitLdcInsn(invokeExactWithArg);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ Type.getInternalName(InvokeCustom.class),
+ "doInvokeExactImplWithArg",
+ "(Ljava/lang/invoke/MethodHandle;)V",
+ false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
}
diff --git a/src/test/examplesAndroidP/invokecustom/keep-rules.txt b/src/test/examplesAndroidP/invokecustom/keep-rules.txt
index dbc21fc..b4998ec 100644
--- a/src/test/examplesAndroidP/invokecustom/keep-rules.txt
+++ b/src/test/examplesAndroidP/invokecustom/keep-rules.txt
@@ -8,7 +8,7 @@
public static void main(...);
}
--keepclasseswithmembers class * {
+-keepclassmembers class * {
*** targetMethodTest*(...);
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 71f4559..e47a430 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -90,6 +90,16 @@
.run();
}
+ @Test
+ public void invokeCustom2WithShrinking() throws Throwable {
+ test("invokecustom2-with-shrinking", "invokecustom2", "InvokeCustom")
+ .withMinApiLevel(AndroidApiLevel.O)
+ .withBuilderTransformation(builder ->
+ builder.addProguardConfigurationFiles(
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_O_DIR, "invokecustom2/keep-rules.txt")))
+ .run();
+ }
+
@Override
@Test
public void lambdaDesugaring() throws Throwable {