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()); + } + } }