blob: 237959c9683454a7ba72c86d98ec815743fe5def [file]
// 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));
}
}
}