Move some reflective identification handling from Enqueuer
This also refactors the reflective identification into smaller methods.
Change-Id: I7f24b4870b46cb50d72daf9868fce785a052f306
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 6fab82f..4d8c7c5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -358,6 +358,14 @@
createString("Ljava/util/concurrent/atomic/AtomicLongFieldUpdater;");
public final DexString referenceFieldUpdaterDescriptor =
createString("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
+
+ public final DexType javaUtilConcurrentAtomicAtomicIntegerFieldUpdater =
+ createType(intFieldUpdaterDescriptor);
+ public final DexType javaUtilConcurrentAtomicAtomicLongFieldUpdater =
+ createType(longFieldUpdaterDescriptor);
+ public final DexType javaUtilConcurrentAtomicAtomicReferenceFieldUpdater =
+ createType(referenceFieldUpdaterDescriptor);
+
public final DexString newUpdaterName = createString("newUpdater");
public final DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME);
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 6e60a93..4dc8598 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -51,7 +51,6 @@
import com.android.tools.r8.graph.DexEncodedMember;
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.DexItemFactory.ClassMethods;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMember;
@@ -262,8 +261,6 @@
private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
private final Map<DexCallSite, ProgramMethodSet> callSites = new IdentityHashMap<>();
- private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
-
private List<KeepDeclaration> keepDeclarations = Collections.emptyList();
private ApplicableRulesEvaluator applicableRules = ApplicableRulesEvaluator.empty();
@@ -1602,19 +1599,7 @@
if (registry != null && !registry.markInvokeStaticAsSeen(invokedMethod)) {
return;
}
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
- || dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
- // Implicitly add -identifiernamestring rule for the Java reflection in use.
- identifierNameStrings.add(invokedMethod);
- // Revisit the current method to implicitly add -keep rule for items with reflective access.
- reflectiveIdentificationAnalysis.enqueue(context);
- } else if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) {
- reflectiveIdentificationAnalysis.enqueue(context);
- } else if (dexItemFactory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
- // Handling of application services.
- reflectiveIdentificationAnalysis.enqueue(context);
- }
+ reflectiveIdentificationAnalysis.scanInvoke(invokedMethod, context);
markTypeAsLive(invokedMethod.getHolderType(), context);
MethodResolutionResult resolutionResult =
handleInvokeOfStaticTarget(invokedMethod, context, reason);
@@ -1651,16 +1636,7 @@
if (registry != null && !registry.markInvokeVirtualAsSeen(invokedMethod)) {
return;
}
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- if (invokedMethod.isIdenticalTo(dexItemFactory.classMethods.newInstance)
- || invokedMethod.isIdenticalTo(dexItemFactory.constructorMethods.newInstance)) {
- reflectiveIdentificationAnalysis.enqueue(context);
- } else if (dexItemFactory.classMethods.isReflectiveMemberLookup(invokedMethod)) {
- // Implicitly add -identifiernamestring rule for the Java reflection in use.
- identifierNameStrings.add(invokedMethod);
- // Revisit the current method to implicitly add -keep rule for items with reflective access.
- reflectiveIdentificationAnalysis.enqueue(context);
- }
+ reflectiveIdentificationAnalysis.scanInvoke(invokedMethod, context);
markTypeAsLive(invokedMethod.getHolderType(), context);
MethodResolutionResult resolutionResult =
markVirtualMethodAsReachable(invokedMethod, false, context, reason);
@@ -4607,7 +4583,9 @@
amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
amendWithCompanionMethods(rootSet.reprocess),
rootSet.alwaysClassInline,
- joinIdentifierNameStrings(rootSet.identifierNameStrings, identifierNameStrings),
+ joinIdentifierNameStrings(
+ rootSet.identifierNameStrings,
+ reflectiveIdentificationAnalysis.getIdentifierNameStrings()),
emptySet(),
Collections.emptyMap(),
lockCandidates,
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java
index f52fba7..0d4e6b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java
@@ -12,6 +12,8 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexItemFactory.ClassMethods;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
@@ -40,9 +42,11 @@
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.timing.Timing;
+import com.google.common.collect.Sets;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
public class EnqueuerReflectiveIdentificationAnalysis {
@@ -51,6 +55,7 @@
private final Enqueuer enqueuer;
private final InternalOptions options;
+ private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
private final ProgramMethodSet worklist = ProgramMethodSet.create();
public EnqueuerReflectiveIdentificationAnalysis(
@@ -61,6 +66,51 @@
this.options = appView.options();
}
+ public Set<DexMember<?, ?>> getIdentifierNameStrings() {
+ return identifierNameStrings;
+ }
+
+ public void scanInvoke(DexMethod invokedMethod, ProgramMethod method) {
+ ClassMethods classMethods = factory.classMethods;
+ DexType holder = invokedMethod.getHolderType();
+ if (holder.isIdenticalTo(factory.classType)) {
+ // java.lang.Class
+ if (invokedMethod.isIdenticalTo(classMethods.newInstance)) {
+ enqueue(method);
+ } else if (classMethods.isReflectiveClassLookup(invokedMethod)
+ || classMethods.isReflectiveMemberLookup(invokedMethod)) {
+ // Implicitly add -identifiernamestring rule for the Java reflection in use.
+ identifierNameStrings.add(invokedMethod);
+ enqueue(method);
+ }
+ } else if (holder.isIdenticalTo(factory.constructorType)) {
+ // java.lang.reflect.Constructor
+ if (invokedMethod.isIdenticalTo(factory.constructorMethods.newInstance)) {
+ enqueue(method);
+ }
+ } else if (holder.isIdenticalTo(factory.proxyType)) {
+ // java.lang.reflect.Proxy
+ if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
+ enqueue(method);
+ }
+ } else if (holder.isIdenticalTo(factory.serviceLoaderType)) {
+ // java.util.ServiceLoader
+ if (factory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
+ enqueue(method);
+ }
+ } else if (holder.isIdenticalTo(factory.javaUtilConcurrentAtomicAtomicIntegerFieldUpdater)
+ || holder.isIdenticalTo(factory.javaUtilConcurrentAtomicAtomicLongFieldUpdater)
+ || holder.isIdenticalTo(factory.javaUtilConcurrentAtomicAtomicReferenceFieldUpdater)) {
+ // java.util.concurrent.atomic.AtomicIntegerFieldUpdater
+ // java.util.concurrent.atomic.AtomicLongFieldUpdater
+ // java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+ if (factory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
+ identifierNameStrings.add(invokedMethod);
+ enqueue(method);
+ }
+ }
+ }
+
public void enqueue(ProgramMethod method) {
worklist.add(method);
}
@@ -89,117 +139,16 @@
DexMethod invokedMethod = invoke.getInvokedMethod();
if (invokedMethod.isIdenticalTo(factory.classMethods.newInstance)) {
handleJavaLangClassNewInstance(method, invoke);
- return;
- }
- if (invokedMethod.isIdenticalTo(factory.constructorMethods.newInstance)) {
+ } else if (invokedMethod.isIdenticalTo(factory.constructorMethods.newInstance)) {
handleJavaLangReflectConstructorNewInstance(method, invoke);
- return;
- }
- if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
+ } else if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
handleJavaLangReflectProxyNewProxyInstance(method, invoke);
- return;
- }
- if (factory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
+ } else if (factory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
handleServiceLoaderInvocation(method, invoke);
- return;
- }
- if (enqueuer.getAnalyses().handleReflectiveInvoke(method, invoke)) {
- return;
- }
- if (!isReflectionMethod(factory, invokedMethod)) {
- return;
- }
- IdentifierNameStringLookupResult<?> identifierLookupResult =
- identifyIdentifier(invoke, appView, method);
- if (identifierLookupResult == null) {
- return;
- }
- DexReference referencedItem = identifierLookupResult.getReference();
- if (referencedItem.isDexType()) {
- DexType referencedType = referencedItem.asDexType();
- if (!referencedType.isClassType()
- || appView.allMergedClasses().isMergeSource(referencedType)) {
- return;
- }
- assert identifierLookupResult.isTypeResult();
- IdentifierNameStringTypeLookupResult identifierTypeLookupResult =
- identifierLookupResult.asTypeResult();
- DexProgramClass clazz =
- enqueuer.getProgramClassOrNullFromReflectiveAccess(referencedType, method);
- if (clazz == null) {
- return;
- }
- enqueuer.markTypeAsLive(clazz, KeepReason.reflectiveUseIn(method));
- if (clazz.canBeInstantiatedByNewInstance()
- && identifierTypeLookupResult.isTypeCompatInstantiatedFromUse(options)) {
- enqueuer.markClassAsInstantiatedWithCompatRule(
- clazz, () -> KeepReason.reflectiveUseIn(method));
- } else if (identifierTypeLookupResult.isTypeInitializedFromUse()) {
- enqueuer.markDirectAndIndirectClassInitializersAsLive(clazz);
- }
- // To ensure we are not moving the class because we cannot prune it when there is a reflective
- // use of it.
- if (enqueuer.getKeepInfo().getClassInfo(clazz).isShrinkingAllowed(options)) {
- enqueuer
- .getKeepInfo()
- .joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
- }
- } else if (referencedItem.isDexField()) {
- DexField field = referencedItem.asDexField();
- DexProgramClass clazz =
- enqueuer.getProgramClassOrNullFromReflectiveAccess(field.holder, method);
- if (clazz == null) {
- return;
- }
- DexEncodedField encodedField = clazz.lookupField(field);
- if (encodedField == null) {
- return;
- }
- // Normally, we generate a -keepclassmembers rule for the field, such that the field is only
- // kept if it is a static field, or if the holder or one of its subtypes are instantiated.
- // However, if the invoked method is a field updater, then we always need to keep instance
- // fields since the creation of a field updater throws a NoSuchFieldException if the field
- // is not present.
- boolean keepClass =
- !encodedField.isStatic()
- && factory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod);
- if (keepClass) {
- enqueuer
- .getWorklist()
- .enqueueMarkInstantiatedAction(
- clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
- }
- if (enqueuer.getKeepInfo().getFieldInfo(encodedField, clazz).isShrinkingAllowed(options)) {
- ProgramField programField = new ProgramField(clazz, encodedField);
- enqueuer.applyMinimumKeepInfoWhenLive(
- programField,
- KeepFieldInfo.newEmptyJoiner()
- .disallowOptimization()
- .disallowShrinking()
- .addReason(KeepReason.reflectiveUseIn(method)));
- }
- } else {
- assert referencedItem.isDexMethod();
- DexMethod targetedMethodReference = referencedItem.asDexMethod();
- DexProgramClass clazz =
- enqueuer.getProgramClassOrNullFromReflectiveAccess(
- targetedMethodReference.holder, method);
- if (clazz == null) {
- return;
- }
- ProgramMethod targetedMethod = clazz.lookupProgramMethod(targetedMethodReference);
- if (targetedMethod == null) {
- return;
- }
- KeepReason reason = KeepReason.reflectiveUseIn(method);
- if (targetedMethod.getDefinition().belongsToDirectPool()) {
- enqueuer.markMethodAsTargeted(targetedMethod, reason);
- enqueuer.markDirectStaticOrConstructorMethodAsLive(targetedMethod, reason);
- } else {
- enqueuer.markVirtualMethodAsLive(targetedMethod, reason);
- }
- enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
- targetedMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
+ } else if (enqueuer.getAnalyses().handleReflectiveInvoke(method, invoke)) {
+ // Intentionally empty.
+ } else if (isReflectionMethod(factory, invokedMethod)) {
+ handleReflectiveLookup(method, invoke);
}
}
@@ -493,4 +442,107 @@
}
}
}
+
+ private void handleReflectiveLookup(ProgramMethod method, InvokeMethod invoke) {
+ IdentifierNameStringLookupResult<?> identifierLookupResult =
+ identifyIdentifier(invoke, appView, method);
+ if (identifierLookupResult != null) {
+ DexReference referencedItem = identifierLookupResult.getReference();
+ referencedItem.accept(
+ referencedType ->
+ handleReflectiveTypeLookup(method, referencedType, identifierLookupResult),
+ referencedField -> handleReflectiveFieldLookup(method, invoke, referencedField),
+ referencedMethod -> handleReflectiveMethodLookup(method, referencedMethod));
+ }
+ }
+
+ private void handleReflectiveFieldLookup(
+ ProgramMethod method, InvokeMethod invoke, DexField field) {
+ DexProgramClass clazz =
+ enqueuer.getProgramClassOrNullFromReflectiveAccess(field.holder, method);
+ if (clazz == null) {
+ return;
+ }
+ DexEncodedField encodedField = clazz.lookupField(field);
+ if (encodedField == null) {
+ return;
+ }
+ // Normally, we generate a -keepclassmembers rule for the field, such that the field is only
+ // kept if it is a static field, or if the holder or one of its subtypes are instantiated.
+ // However, if the invoked method is a field updater, then we always need to keep instance
+ // fields since the creation of a field updater throws a NoSuchFieldException if the field
+ // is not present.
+ boolean keepClass =
+ !encodedField.isStatic()
+ && factory.atomicFieldUpdaterMethods.isFieldUpdater(invoke.getInvokedMethod());
+ if (keepClass) {
+ enqueuer
+ .getWorklist()
+ .enqueueMarkInstantiatedAction(
+ clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
+ }
+ if (enqueuer.getKeepInfo().getFieldInfo(encodedField, clazz).isShrinkingAllowed(options)) {
+ ProgramField programField = new ProgramField(clazz, encodedField);
+ enqueuer.applyMinimumKeepInfoWhenLive(
+ programField,
+ KeepFieldInfo.newEmptyJoiner()
+ .disallowOptimization()
+ .disallowShrinking()
+ .addReason(KeepReason.reflectiveUseIn(method)));
+ }
+ }
+
+ private void handleReflectiveMethodLookup(
+ ProgramMethod method, DexMethod targetedMethodReference) {
+ DexProgramClass clazz =
+ enqueuer.getProgramClassOrNullFromReflectiveAccess(targetedMethodReference.holder, method);
+ if (clazz == null) {
+ return;
+ }
+ ProgramMethod targetedMethod = clazz.lookupProgramMethod(targetedMethodReference);
+ if (targetedMethod == null) {
+ return;
+ }
+ KeepReason reason = KeepReason.reflectiveUseIn(method);
+ if (targetedMethod.getDefinition().belongsToDirectPool()) {
+ enqueuer.markMethodAsTargeted(targetedMethod, reason);
+ enqueuer.markDirectStaticOrConstructorMethodAsLive(targetedMethod, reason);
+ } else {
+ enqueuer.markVirtualMethodAsLive(targetedMethod, reason);
+ }
+ enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
+ targetedMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
+ }
+
+ private void handleReflectiveTypeLookup(
+ ProgramMethod method,
+ DexType referencedType,
+ IdentifierNameStringLookupResult<?> identifierLookupResult) {
+ if (!referencedType.isClassType()) {
+ return;
+ }
+ assert identifierLookupResult.isTypeResult();
+ IdentifierNameStringTypeLookupResult identifierTypeLookupResult =
+ identifierLookupResult.asTypeResult();
+ DexProgramClass clazz =
+ enqueuer.getProgramClassOrNullFromReflectiveAccess(referencedType, method);
+ if (clazz == null) {
+ return;
+ }
+ enqueuer.markTypeAsLive(clazz, KeepReason.reflectiveUseIn(method));
+ if (clazz.canBeInstantiatedByNewInstance()
+ && identifierTypeLookupResult.isTypeCompatInstantiatedFromUse(options)) {
+ enqueuer.markClassAsInstantiatedWithCompatRule(
+ clazz, () -> KeepReason.reflectiveUseIn(method));
+ } else if (identifierTypeLookupResult.isTypeInitializedFromUse()) {
+ enqueuer.markDirectAndIndirectClassInitializersAsLive(clazz);
+ }
+ // To ensure we are not moving the class because we cannot prune it when there is a reflective
+ // use of it.
+ if (enqueuer.getKeepInfo().getClassInfo(clazz).isShrinkingAllowed(options)) {
+ enqueuer
+ .getKeepInfo()
+ .joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
+ }
+ }
}