blob: 99573caec081562cf21e773795248a387cde307d [file] [log] [blame]
// Copyright (c) 2017, 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.ir.optimize;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.Map;
public class SwitchUtils {
public static final class EnumSwitchInfo {
public final DexType enumClass;
public final Instruction ordinalInvoke;
public final Instruction arrayGet;
public final Instruction staticGet;
public final Int2ReferenceMap<DexField> indexMap;
public final Map<DexField, EnumValueInfo> valueInfoMap;
private EnumSwitchInfo(DexType enumClass,
Instruction ordinalInvoke,
Instruction arrayGet, Instruction staticGet,
Int2ReferenceMap<DexField> indexMap,
Map<DexField, EnumValueInfo> valueInfoMap) {
this.enumClass = enumClass;
this.ordinalInvoke = ordinalInvoke;
this.arrayGet = arrayGet;
this.staticGet = staticGet;
this.indexMap = indexMap;
this.valueInfoMap = valueInfoMap;
}
}
/**
* Looks for a switch statement over the enum companion class of the form
*
* <blockquote>
*
* <pre>
* switch(CompanionClass.$switchmap$field[enumValue.ordinal()]) {
* ...
* }
* </pre>
*
* </blockquote>
*
* and extracts the components and the index and ordinal maps. See {@link EnumInfoMapCollector}
* and {@link SwitchMapCollector} for details.
*/
public static EnumSwitchInfo analyzeSwitchOverEnum(
IntSwitch switchInsn, AppView<AppInfoWithLiveness> appView) {
Instruction input = switchInsn.inValues().get(0).definition;
if (input == null || !input.isArrayGet()) {
return null;
}
ArrayGet arrayGet = input.asArrayGet();
Instruction index = arrayGet.index().definition;
if (index == null || !index.isInvokeVirtual()) {
return null;
}
InvokeVirtual ordinalInvoke = index.asInvokeVirtual();
DexMethod ordinalMethod = ordinalInvoke.getInvokedMethod();
DexClass enumClass = appView.definitionFor(ordinalMethod.holder);
DexItemFactory dexItemFactory = appView.dexItemFactory();
// After member rebinding, enumClass will be the actual java.lang.Enum class.
if (enumClass == null
|| (!enumClass.accessFlags.isEnum() && enumClass.type != dexItemFactory.enumType)
|| ordinalMethod.name != dexItemFactory.ordinalMethodName
|| ordinalMethod.proto.returnType != dexItemFactory.intType
|| !ordinalMethod.proto.parameters.isEmpty()) {
return null;
}
Instruction array = arrayGet.array().definition;
if (array == null || !array.isStaticGet()) {
return null;
}
StaticGet staticGet = array.asStaticGet();
Int2ReferenceMap<DexField> indexMap = appView.appInfo().getSwitchMapFor(staticGet.getField());
if (indexMap == null || indexMap.isEmpty()) {
return null;
}
for (int key : switchInsn.getKeys()) {
if (!indexMap.containsKey(key)) {
return null;
}
}
// Due to member rebinding, only the fields are certain to provide the actual enums
// class.
DexType enumType = indexMap.values().iterator().next().holder;
Map<DexField, EnumValueInfo> valueInfoMap = appView.appInfo().getEnumValueInfoMapFor(enumType);
if (valueInfoMap == null) {
return null;
}
return new EnumSwitchInfo(enumType, ordinalInvoke, arrayGet, staticGet, indexMap,
valueInfoMap);
}
}