| // 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.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.toBaseType(appView.dexItemFactory()); |
| 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 classArg = invoke.getArgumentForParameter(classParamIndex); |
| if (classArg.isPhi()) { |
| return; |
| } |
| DexType type; |
| if (invoke |
| .getInvokedMethod() |
| .getParameter(classParamIndex) |
| .isIdenticalTo(appView.dexItemFactory().classType)) { |
| // EnumMap.<init>(), EnumSet.noneOf(), EnumSet.allOf(), Enum.valueOf(). |
| ConstClass constClass = classArg.definition.asConstClass(); |
| if (constClass == null) { |
| return; |
| } |
| type = constClass.getType(); |
| } else { |
| // EnumSet.of(), EnumSet.range() |
| ClassTypeElement typeElement = classArg.getType().asClassType(); |
| if (typeElement == null) { |
| return; |
| } |
| type = typeElement.getClassType(); |
| } |
| DexProgramClass clazz = maybeGetProgramEnumType(type, true); |
| if (clazz != null) { |
| enqueuer.markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(context)); |
| } |
| } |
| } |