Merge "Allow synthetic methods in the root set"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8ebc02f..cb038b3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -376,7 +376,7 @@
// We can now remove visibility bridges. Note that we do not need to update the
// invoke-targets here, as the existing invokes will simply dispatch to the now
// visible super-method. MemberRebinding, if run, will then dispatch it correctly.
- application = new VisibilityBridgeRemover(appView.appInfo(), application).run();
+ new VisibilityBridgeRemover(appView.withLiveness()).run();
}
}
@@ -481,12 +481,15 @@
new EnumOrdinalMapCollector(appViewWithLiveness, options).run());
}
+ assert appView.appInfo().hasLiveness();
+
timing.begin("Create IR");
Set<DexCallSite> desugaredCallSites;
CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
try {
IRConverter converter =
- new IRConverter(appView, options, timing, printer, mainDexClasses, rootSet);
+ new IRConverter(
+ appView.withLiveness(), options, timing, printer, mainDexClasses, rootSet);
application = converter.optimize(application, executorService);
desugaredCallSites = converter.getDesugaredCallSites();
} finally {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index f9a9dde..e232cb9 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -106,6 +106,11 @@
}
@Override
+ public VerticallyMergedClasses verticallyMergedClasses() {
+ return AppView.this.verticallyMergedClasses();
+ }
+
+ @Override
public AppView<AppInfoWithLiveness> withLiveness() {
return this;
}
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 6b9257b..54895ac 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -267,6 +267,10 @@
public final DexProto twrCloseResourceMethodProto =
createProto(voidType, throwableType, autoCloseableType);
+ public final DexString deserializeLambdaMethodName = createString("$deserializeLambda$");
+ public final DexProto deserializeLambdaMethodProto =
+ createProto(objectType, createType("Ljava/lang/invoke/SerializedLambda;"));
+
// Dex system annotations.
// See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
public final DexType annotationDefault = createType("Ldalvik/annotation/AnnotationDefault;");
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 3aba90c..bd86c12 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -185,6 +185,11 @@
return builder.toString();
}
+ public boolean isLambdaDeserializeMethod(DexItemFactory dexItemFactory) {
+ return name == dexItemFactory.deserializeLambdaMethodName
+ && proto == dexItemFactory.deserializeLambdaMethodProto;
+ }
+
synchronized public void setSingleVirtualMethodCache(
DexType receiverType, DexEncodedMethod method) {
if (singleTargetCache == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 7065297..8571409 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -160,7 +160,7 @@
InternalOptions options,
Timing timing,
CfgPrinter printer,
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithLiveness> appView,
MainDexClasses mainDexClasses,
RootSet rootSet) {
assert appInfo != null;
@@ -181,7 +181,8 @@
this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(this) : null;
this.interfaceMethodRewriter =
options.isInterfaceMethodDesugaringEnabled()
- ? new InterfaceMethodRewriter(this, options) : null;
+ ? new InterfaceMethodRewriter(appView, this, options)
+ : null;
this.twrCloseResourceRewriter =
(options.enableDesugaring && enableTwrCloseResourceDesugaring())
? new TwrCloseResourceRewriter(this) : null;
@@ -276,7 +277,7 @@
* Create an IR converter for processing methods with full program optimization enabled.
*/
public IRConverter(
- AppView<AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithLiveness> appView,
InternalOptions options,
Timing timing,
CfgPrinter printer,
@@ -317,10 +318,11 @@
);
}
- private void removeLambdaDeserializationMethods() {
+ private boolean removeLambdaDeserializationMethods() {
if (lambdaRewriter != null) {
- lambdaRewriter.removeLambdaDeserializationMethods(appInfo.classes());
+ return lambdaRewriter.removeLambdaDeserializationMethods(appInfo.classes());
}
+ return false;
}
private void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
@@ -528,8 +530,13 @@
public DexApplication optimize(DexApplication application, ExecutorService executorService)
throws ExecutionException {
+ if (options.enableTreeShaking) {
+ assert !removeLambdaDeserializationMethods();
+ } else {
+ removeLambdaDeserializationMethods();
+ }
+
computeReachabilitySensitivity(application);
- removeLambdaDeserializationMethods();
collectLambdaMergingCandidates(application);
collectStaticizerCandidates(application);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index a9a571f..e79017d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
@@ -78,6 +79,7 @@
public static final String DEFAULT_METHOD_PREFIX = "$default$";
public static final String PRIVATE_METHOD_PREFIX = "$private$";
+ private final AppView<? extends AppInfoWithLiveness> appView;
private final IRConverter converter;
private final InternalOptions options;
final DexItemFactory factory;
@@ -112,8 +114,12 @@
ExcludeDexResources
}
- public InterfaceMethodRewriter(IRConverter converter, InternalOptions options) {
+ public InterfaceMethodRewriter(
+ AppView<? extends AppInfoWithLiveness> appView,
+ IRConverter converter,
+ InternalOptions options) {
assert converter != null;
+ this.appView = appView;
this.converter = converter;
this.options = options;
this.factory = options.itemFactory;
@@ -429,7 +435,7 @@
private Map<DexType, DexProgramClass> processInterfaces(Builder<?> builder, Flavor flavour) {
NestedGraphLense.Builder graphLensBuilder = GraphLense.builder();
- InterfaceProcessor processor = new InterfaceProcessor(this);
+ InterfaceProcessor processor = new InterfaceProcessor(appView, this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, true)) {
processor.process(clazz.asProgramClass(), graphLensBuilder);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index fb40b0a..01b647b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -30,6 +31,7 @@
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@@ -47,11 +49,16 @@
//
// Also moves static interface methods into a companion class.
final class InterfaceProcessor {
+
+ private final AppView<? extends AppInfoWithLiveness> appView;
private final InterfaceMethodRewriter rewriter;
+
// All created companion and dispatch classes indexed by interface type.
final Map<DexType, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
- InterfaceProcessor(InterfaceMethodRewriter rewriter) {
+ InterfaceProcessor(
+ AppView<? extends AppInfoWithLiveness> appView, InterfaceMethodRewriter rewriter) {
+ this.appView = appView;
this.rewriter = rewriter;
}
@@ -106,7 +113,7 @@
}
}
- // If at least one bridge methods was removed update the table.
+ // If at least one bridge method was removed then update the table.
if (remainingMethods.size() < iface.virtualMethods().length) {
iface.setVirtualMethods(remainingMethods.toArray(
new DexEncodedMethod[remainingMethods.size()]));
@@ -303,6 +310,9 @@
// methods. Bridge methods that does not override an implementation in a super-interface must
// also be kept (such a situation can happen if the vertical class merger merges two interfaces).
private boolean interfaceMethodRemovalChangesApi(DexEncodedMethod method, DexClass iface) {
+ if (appView != null && appView.appInfo().isPinned(method.method)) {
+ return true;
+ }
if (method.accessFlags.isBridge()) {
Deque<DexType> worklist = new ArrayDeque<>();
Set<DexType> seenBefore = new HashSet<>();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index bf36c20..b78f6df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -517,11 +517,6 @@
for (int i = 0; i < directMethods.length; i++) {
DexEncodedMethod encodedMethod = directMethods[i];
if (implMethod.match(encodedMethod)) {
- // Check that this method is synthetic, since this implies that the method cannot be kept
- // by an explicit -keep rule. This is necessary because we could otherwise be changing
- // the signature of a kept method here, which is not allowed (see b/120971047).
- assert encodedMethod.accessFlags.isSynthetic();
-
// We need to create a new static method with the same code to be able to safely
// relax its accessibility without making it virtual.
MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
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 6836ad3..235c96d 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
@@ -152,7 +152,7 @@
}
/** If the lambda delegates to lambda$ method. */
- boolean delegatesToLambdaImplMethod() {
+ public boolean delegatesToLambdaImplMethod() {
DexString methodName = implHandle.asMethod().name;
return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX);
}
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 707078f..df52ef1 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
@@ -46,9 +46,6 @@
* lambda class generation, and instruction patching.
*/
public class LambdaRewriter {
- private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
-
- private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
// Public for testing.
public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
@@ -66,9 +63,6 @@
final DexString classConstructorName;
final DexString instanceFieldName;
- final DexString deserializeLambdaMethodName;
- final DexProto deserializeLambdaMethodProto;
-
final BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
// Maps call sites seen so far to inferred lambda descriptor. It is intended
@@ -98,10 +92,6 @@
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.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
- this.deserializeLambdaMethodProto = factory.createProto(
- factory.objectType, factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR));
}
/**
@@ -138,7 +128,8 @@
}
/** Remove lambda deserialization methods. */
- public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+ public boolean removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+ boolean anyRemoved = false;
for (DexProgramClass clazz : classes) {
// Search for a lambda deserialization method and remove it if found.
DexEncodedMethod[] directMethods = clazz.directMethods();
@@ -147,8 +138,7 @@
for (int i = 0; i < methodCount; i++) {
DexEncodedMethod encoded = directMethods[i];
DexMethod method = encoded.method;
- if (method.name == deserializeLambdaMethodName &&
- method.proto == deserializeLambdaMethodProto) {
+ if (method.isLambdaDeserializeMethod(appInfo.dexItemFactory)) {
assert encoded.accessFlags.isStatic();
assert encoded.accessFlags.isSynthetic();
@@ -157,12 +147,15 @@
System.arraycopy(directMethods, i + 1, newMethods, i, methodCount - i - 1);
clazz.setDirectMethods(newMethods);
+ anyRemoved = true;
+
// We assume there is only one such method in the class.
break;
}
}
}
}
+ return anyRemoved;
}
/**
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 debba04..59caf25 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -3,87 +3,89 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.Arrays;
-import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
public class VisibilityBridgeRemover {
- private final AppInfoWithSubtyping appInfo;
- private final DexApplication application;
- private final Set<DexEncodedMethod> unneededVisibilityBridges = Sets.newIdentityHashSet();
- public VisibilityBridgeRemover(AppInfoWithSubtyping appInfo, DexApplication application) {
- this.appInfo = appInfo;
- this.application = application;
+ private final AppView<? extends AppInfoWithLiveness> appView;
+
+ public VisibilityBridgeRemover(AppView<? extends AppInfoWithLiveness> appView) {
+ this.appView = appView;
}
- private void identifyBridgeMethod(DexEncodedMethod method) {
+ private void removeUnneededVisibilityBridgesFromClass(DexProgramClass clazz) {
+ clazz.setDirectMethods(removeUnneededVisibilityBridges(clazz.directMethods()));
+ clazz.setVirtualMethods(removeUnneededVisibilityBridges(clazz.virtualMethods()));
+ }
+
+ private DexEncodedMethod[] removeUnneededVisibilityBridges(DexEncodedMethod[] methods) {
+ Set<DexEncodedMethod> methodsToBeRemoved = null;
+ for (DexEncodedMethod method : methods) {
+ if (isUnneededVisibilityBridge(method)) {
+ if (methodsToBeRemoved == null) {
+ methodsToBeRemoved = Sets.newIdentityHashSet();
+ }
+ methodsToBeRemoved.add(method);
+ }
+ }
+ if (methodsToBeRemoved != null) {
+ Set<DexEncodedMethod> finalMethodsToBeRemoved = methodsToBeRemoved;
+ return Arrays.stream(methods)
+ .filter(method -> !finalMethodsToBeRemoved.contains(method))
+ .toArray(DexEncodedMethod[]::new);
+ }
+ return methods;
+ }
+
+ private boolean isUnneededVisibilityBridge(DexEncodedMethod method) {
+ if (appView.appInfo().isPinned(method.method)) {
+ return false;
+ }
MethodAccessFlags accessFlags = method.accessFlags;
- if (accessFlags.isBridge() && !accessFlags.isAbstract()) {
- InvokeSingleTargetExtractor targetExtractor =
- new InvokeSingleTargetExtractor(appInfo.dexItemFactory);
- method.getCode().registerCodeReferences(targetExtractor);
- DexMethod target = targetExtractor.getTarget();
- InvokeKind kind = targetExtractor.getKind();
- // javac-generated visibility forward bridge method has same descriptor (name, signature and
- // return type).
- if (target != null && target.hasSameProtoAndName(method.method)) {
- assert !accessFlags.isPrivate() && !accessFlags.isConstructor();
- if (kind == InvokeKind.SUPER) {
- // This is a visibility forward, so check for the direct target.
- DexEncodedMethod targetMethod =
- appInfo.resolveMethod(target.getHolder(), target).asSingleTarget();
- if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
- if (Log.ENABLED) {
- Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method,
- targetMethod.method);
- }
- unneededVisibilityBridges.add(method);
+ if (!accessFlags.isBridge() || accessFlags.isAbstract()) {
+ return false;
+ }
+ InvokeSingleTargetExtractor targetExtractor =
+ new InvokeSingleTargetExtractor(appView.dexItemFactory());
+ method.getCode().registerCodeReferences(targetExtractor);
+ DexMethod target = targetExtractor.getTarget();
+ InvokeKind kind = targetExtractor.getKind();
+ // javac-generated visibility forward bridge method has same descriptor (name, signature and
+ // return type).
+ if (target != null && target.hasSameProtoAndName(method.method)) {
+ assert !accessFlags.isPrivate() && !accessFlags.isConstructor();
+ if (kind == InvokeKind.SUPER) {
+ // This is a visibility forward, so check for the direct target.
+ DexEncodedMethod targetMethod =
+ appView.appInfo().resolveMethod(target.getHolder(), target).asSingleTarget();
+ if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
+ if (Log.ENABLED) {
+ Log.info(
+ getClass(),
+ "Removing visibility forwarding %s -> %s",
+ method.method,
+ targetMethod.method);
}
+ return true;
}
}
}
+ return false;
}
- private void removeUnneededVisibilityBridges() {
- Set<DexType> classes = unneededVisibilityBridges.stream()
- .map(method -> method.method.getHolder())
- .collect(Collectors.toSet());
- for (DexType type : classes) {
- DexClass clazz = appInfo.definitionFor(type);
- clazz.setVirtualMethods(removeMethods(clazz.virtualMethods(), unneededVisibilityBridges));
+ public void run() {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ removeUnneededVisibilityBridgesFromClass(clazz);
}
}
-
- private DexEncodedMethod[] removeMethods(DexEncodedMethod[] methods,
- Set<DexEncodedMethod> removals) {
- assert methods != null;
- List<DexEncodedMethod> newMethods = Arrays.stream(methods)
- .filter(method -> !removals.contains(method))
- .collect(Collectors.toList());
- assert newMethods.size() < methods.length;
- return newMethods.toArray(new DexEncodedMethod[newMethods.size()]);
- }
-
- public DexApplication run() {
- for (DexClass clazz : appInfo.classes()) {
- clazz.forEachMethod(this::identifyBridgeMethod);
- }
- if (!unneededVisibilityBridges.isEmpty()) {
- removeUnneededVisibilityBridges();
- }
- return application;
- }
-
}
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 13dd3ea..dc6dabc 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -193,6 +193,10 @@
*/
private final Set<DexMethod> methodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
/**
+ * Set of direct lambda methods that are the immediate target of an invoke-dynamic.
+ */
+ private final Set<DexMethod> lambdaMethodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
+ /**
* Set of virtual methods that are the immediate target of an invoke-direct.
* */
private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect = Sets.newIdentityHashSet();
@@ -237,15 +241,15 @@
private final Set<DexMethod> virtualTargetsMarkedAsReachable = Sets.newIdentityHashSet();
/**
- * A set of dexitems we have reported missing to dedupe warnings.
+ * A set of references we have reported missing to dedupe warnings.
*/
private final Set<DexReference> reportedMissing = Sets.newIdentityHashSet();
/**
- * A set of items that we are keeping due to keep rules. This may differ from the rootSet due to
- * dependent keep rules.
+ * A set of references that we are keeping due to keep rules. This may differ from the root set
+ * due to dependent keep rules.
*/
- private final Set<DexDefinition> pinnedItems = Sets.newIdentityHashSet();
+ private final Set<DexReference> pinnedItems = Sets.newIdentityHashSet();
/**
* A map from classes to annotations that need to be processed should the classes ever become
@@ -305,6 +309,8 @@
DexDefinition item = appInfo.definitionFor(root.getKey());
if (item != null) {
enqueueRootItem(item, root.getValue());
+ } else {
+ assert false : "Expected root item `" + root.getKey().toSourceString() + "` to be present";
}
}
@@ -351,7 +357,7 @@
} else {
throw new IllegalArgumentException(item.toString());
}
- pinnedItems.add(item);
+ pinnedItems.add(item.toReference());
}
private void enqueueFirstNonSerializableClassInitializer(DexClass clazz, KeepReason reason) {
@@ -648,6 +654,10 @@
assert implHandle != null;
DexMethod method = implHandle.asMethod();
+ if (descriptor.delegatesToLambdaImplMethod()) {
+ lambdaMethodsTargetedByInvokeDynamic.add(method);
+ }
+
if (!methodsTargetedByInvokeDynamic.add(method)) {
return;
}
@@ -1517,9 +1527,18 @@
} finally {
timing.end();
}
+ unpinLambdaMethods();
return new AppInfoWithLiveness(appInfo, this);
}
+ private void unpinLambdaMethods() {
+ for (DexMethod method : lambdaMethodsTargetedByInvokeDynamic) {
+ pinnedItems.remove(method);
+ rootSet.prune(method);
+ }
+ lambdaMethodsTargetedByInvokeDynamic.clear();
+ }
+
private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
DexClass holder = appInfo.definitionFor(target.method.holder);
// If this method no longer has a corresponding class then we have shaken it away before.
@@ -2029,8 +2048,7 @@
instanceFieldReads.keySet(), staticFieldReads.keySet());
this.fieldsWritten = enqueuer.mergeFieldAccesses(
instanceFieldWrites.keySet(), staticFieldWrites.keySet());
- this.pinnedItems =
- DexDefinition.mapToReference(enqueuer.pinnedItems.stream()).collect(Collectors.toSet());
+ this.pinnedItems = enqueuer.pinnedItems;
this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
this.superInvokes = joinInvokedMethods(enqueuer.superInvokes, TargetWithContext::getTarget);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 9e6be06..f94954b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -897,10 +897,8 @@
if (context instanceof ProguardKeepRule) {
if (item.isDexEncodedMethod()) {
DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
- if (encodedMethod.isSyntheticMethod()) {
- // Don't keep synthetic methods (see b/120971047 for additional details).
- // TODO(b/122819537): need to distinguish lambda desugared synthetic methods v.s. kotlinc
- // synthetic methods?
+ if (encodedMethod.method.isLambdaDeserializeMethod(appView.dexItemFactory())) {
+ // Don't keep lambda deserialization methods.
return;
}
// If desugaring is enabled, private and static interface methods will be moved to a
@@ -1232,7 +1230,8 @@
Map<DexType, Set<DexReference>> requiredReferencesPerType = new IdentityHashMap<>();
for (DexReference reference : noShrinking.keySet()) {
// Check that `pinnedItems` is a super set of the root set.
- assert pinnedItems == null || pinnedItems.contains(reference);
+ assert pinnedItems == null || pinnedItems.contains(reference)
+ : "Expected reference `" + reference.toSourceString() + "` to be pinned";
if (reference.isDexType()) {
DexType type = reference.asDexType();
requiredReferencesPerType.putIfAbsent(type, Sets.newIdentityHashSet());
@@ -1263,7 +1262,10 @@
.map(DexEncodedField::getKey)
.collect(Collectors.toSet());
}
- assert fields.contains(requiredField);
+ assert fields.contains(requiredField)
+ : "Expected field `"
+ + requiredField.toSourceString()
+ + "` from the root set to be present";
} else if (requiredReference.isDexMethod()) {
DexMethod requiredMethod = requiredReference.asDexMethod();
if (methods == null) {
@@ -1273,7 +1275,10 @@
.map(DexEncodedMethod::getKey)
.collect(Collectors.toSet());
}
- assert methods.contains(requiredMethod);
+ assert methods.contains(requiredMethod)
+ : "Expected method `"
+ + requiredMethod.toSourceString()
+ + "` from the root set to be present";
} else {
assert false;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index f19b089..f796046 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -39,10 +39,6 @@
@Test
public void testDontShrinkAndDontOptimize() throws Exception {
- // TODO(b/122819537): need to trace kotlinc-generated synthetic methods.
- if (backend == Backend.DEX) {
- return;
- }
test("-dontshrink", "-dontoptimize");
}
@@ -53,10 +49,6 @@
@Test
public void testDontShrink() throws Exception {
- // TODO(b/122819537): need to trace kotlinc-generated synthetic methods.
- if (backend == Backend.DEX) {
- return;
- }
test("-dontshrink");
}
@@ -69,5 +61,4 @@
public void testDontObfuscate() throws Exception {
test("-dontobfuscate");
}
-
}
diff --git a/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java b/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java
index cad2410..b08be31 100644
--- a/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java
@@ -26,17 +26,8 @@
// static synthetic void bar() { Sub.foo(); }
// }
// class Sub extends Base {}
- //
- // As per b/120971047, we do not add synthetic methods to the root set.
- // When running the above example with -dontshrink, the static call in the synthetic method is not
- // traced, so no chance to rebind that call to Base#foo. Then, at the end of dex writing, it hits
- // assertion error in the naming lense that checks if call targets are eligible for renaming.
@Test
public void test() throws Exception {
- // TODO(b/122819537): need to trace kotlinc-generated synthetic methods.
- if (backend == Backend.DEX) {
- return;
- }
testForR8(backend)
.addProgramClassFileData(Base.dump(), Sub.dump())
.addKeepRules("-dontshrink")