| // Copyright (c) 2025, 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. | |
| package com.android.tools.r8.shaking; | |
| import com.android.tools.r8.graph.AppInfoWithClassHierarchy; | |
| import com.android.tools.r8.graph.AppView; | |
| import com.android.tools.r8.graph.DexClass; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.graph.DexType; | |
| import com.android.tools.r8.graph.ProgramMethod; | |
| import com.android.tools.r8.graph.analysis.EnqueuerAnalysisCollection; | |
| import com.android.tools.r8.graph.analysis.IrBasedEnqueuerAnalysis; | |
| import com.android.tools.r8.graph.analysis.TraceInvokeEnqueuerAnalysis; | |
| import com.android.tools.r8.ir.analysis.type.ClassTypeElement; | |
| import com.android.tools.r8.ir.analysis.type.TypeElement; | |
| import com.android.tools.r8.ir.code.ConstClass; | |
| import com.android.tools.r8.ir.code.InvokeMethod; | |
| import com.android.tools.r8.ir.code.Value; | |
| abstract class EnqueuerEnumReflectionAnalysisBase | |
| implements TraceInvokeEnqueuerAnalysis, IrBasedEnqueuerAnalysis { | |
| private final AppView<? extends AppInfoWithClassHierarchy> appView; | |
| protected final Enqueuer enqueuer; | |
| public EnqueuerEnumReflectionAnalysisBase( | |
| AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) { | |
| this.appView = appView; | |
| this.enqueuer = enqueuer; | |
| } | |
| public void register(EnqueuerAnalysisCollection.Builder builder) { | |
| builder.addTraceInvokeAnalysis(this); | |
| builder.addIrBasedEnqueuerAnalysis(this); | |
| } | |
| protected DexProgramClass maybeGetProgramEnumType(DexType type, boolean checkSuper) { | |
| // Arrays can be used for serialization-related methods. | |
| if (type.isArrayType()) { | |
| type = type.getBaseType(); | |
| if (!type.isClassType()) { | |
| return null; | |
| } | |
| } | |
| DexClass dexClass = appView.definitionFor(type); | |
| if (dexClass == null || !dexClass.isProgramClass() || !dexClass.hasSuperType()) { | |
| return null; | |
| } | |
| DexType superType = dexClass.getSuperType(); | |
| if (superType.isIdenticalTo(appView.dexItemFactory().enumType)) { | |
| return dexClass.asProgramClass(); | |
| } | |
| // Cannot have a sub-sub-type of an Enum. | |
| return checkSuper ? maybeGetProgramEnumType(superType, false) : null; | |
| } | |
| protected void handleReflectiveEnumInvoke( | |
| ProgramMethod context, InvokeMethod invoke, int classParamIndex) { | |
| // The use of java.lang.Enum.valueOf(java.lang.Class, java.lang.String) will indirectly | |
| // access the values() method of the enum class passed as the first argument. The method | |
| // SomeEnumClass.valueOf(java.lang.String) which is generated by javac for all enums will | |
| // call this method. | |
| // Likewise, EnumSet and EnumMap call values() on the passed in Class. | |
| Value arg = invoke.getArgumentForParameter(classParamIndex); | |
| DexType type; | |
| if (invoke | |
| .getInvokedMethod() | |
| .getParameter(classParamIndex) | |
| .isIdenticalTo(appView.dexItemFactory().classType)) { | |
| // EnumMap.<init>(), EnumSet.noneOf(), EnumSet.allOf(), Enum.valueOf(), | |
| // Bundle.getSerializable(), Parcel.readSerializable(), Intent.getSerializableExtra(). | |
| // A phi has no single definition, so it cannot be matched to a const-class. | |
| if (arg.isPhi()) { | |
| return; | |
| } | |
| ConstClass constClass = arg.definition.asConstClass(); | |
| if (constClass == null) { | |
| return; | |
| } | |
| type = constClass.getType(); | |
| } else { | |
| // EnumSet.of(), EnumSet.range(), Intent.putExtra(). | |
| // Phis are allowed here since their type is the join of their operands' types, so a phi of | |
| // values of the same enum type (e.g. "cond ? MyEnum.A : MyEnum.B") still has that type. | |
| TypeElement typeElement = arg.getType(); | |
| if (typeElement.isArrayType()) { | |
| typeElement = typeElement.asArrayType().getBaseType(); | |
| } | |
| ClassTypeElement classTypeElement = typeElement.asClassType(); | |
| if (classTypeElement == null) { | |
| return; | |
| } | |
| type = classTypeElement.getClassType(); | |
| } | |
| DexProgramClass clazz = maybeGetProgramEnumType(type, true); | |
| if (clazz != null) { | |
| enqueuer.markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(context)); | |
| } | |
| } | |
| } |