Enum unboxing: Split the enum unboxer class in 3
- Promote EnumUnboxingLens to its own class
- Promote EnumUnboxingTreeFixer to its own class
- Promote updateOptimizationInfos to its own method
Change-Id: Iff21a490d21814cc703639c5c26d8b83256b7c4b
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 3f2de2e..d4adf44 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClass.FieldSetter;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,11 +17,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue.DexValueInt;
-import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.FieldResolutionResult;
@@ -31,9 +26,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ProgramPackageCollection;
import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -49,7 +41,6 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.MemberType;
@@ -69,18 +60,14 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -379,11 +366,20 @@
.build();
enumUnboxerRewriter =
new EnumUnboxingRewriter(appView, enumsToUnbox, enumInstanceFieldDataMap, relocator);
- NestedGraphLens enumUnboxingLens = new TreeFixer(enumsToUnbox, relocator).fixupTypeReferences();
+ NestedGraphLens enumUnboxingLens =
+ new EnumUnboxingTreeFixer(appView, enumsToUnbox, relocator, enumUnboxerRewriter)
+ .fixupTypeReferences();
appView.setUnboxedEnums(enumUnboxerRewriter.getEnumsToUnbox());
GraphLens previousLens = appView.graphLens();
appView.rewriteWithLensAndApplication(enumUnboxingLens, appBuilder.build());
- // Update optimization info.
+ updateOptimizationInfos(executorService, feedback);
+ postBuilder.put(dependencies);
+ postBuilder.rewrittenWithLens(appView, previousLens);
+ }
+
+ private void updateOptimizationInfos(
+ ExecutorService executorService, OptimizationFeedbackDelayed feedback)
+ throws ExecutionException {
feedback.fixupOptimizationInfos(
appView,
executorService,
@@ -415,8 +411,6 @@
}
}
});
- postBuilder.put(dependencies);
- postBuilder.rewrittenWithLens(appView, previousLens);
}
private void updateKeepInfo(Set<DexProgramClass> enumsToUnbox) {
@@ -1109,305 +1103,4 @@
ENUM_METHOD_CALLED_WITH_NULL_RECEIVER,
OTHER_UNSUPPORTED_INSTRUCTION;
}
-
- private class TreeFixer {
-
- private final Map<DexType, List<DexEncodedMethod>> unboxedEnumsMethods =
- new IdentityHashMap<>();
- private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.builder();
- private final Set<DexType> enumsToUnbox;
- private final UnboxedEnumMemberRelocator relocator;
-
- private TreeFixer(Set<DexType> enumsToUnbox, UnboxedEnumMemberRelocator relocator) {
- this.enumsToUnbox = enumsToUnbox;
- this.relocator = relocator;
- }
-
- private NestedGraphLens fixupTypeReferences() {
- assert enumUnboxerRewriter != null;
- // Fix all methods and fields using enums to unbox.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (enumsToUnbox.contains(clazz.type)) {
- // Clear the initializers and move the static methods to the new location.
- Set<DexEncodedMethod> methodsToRemove = Sets.newIdentityHashSet();
- clazz
- .methods()
- .forEach(
- m -> {
- if (m.isInitializer()) {
- clearEnumToUnboxMethod(m);
- } else {
- DexType newHolder = relocator.getNewMemberLocationFor(clazz.type);
- List<DexEncodedMethod> movedMethods =
- unboxedEnumsMethods.computeIfAbsent(newHolder, k -> new ArrayList<>());
- movedMethods.add(fixupEncodedMethodToUtility(m, newHolder));
- methodsToRemove.add(m);
- }
- });
- clazz.getMethodCollection().removeMethods(methodsToRemove);
- } else {
- clazz.getMethodCollection().replaceMethods(this::fixupEncodedMethod);
- fixupFields(clazz.staticFields(), clazz::setStaticField);
- fixupFields(clazz.instanceFields(), clazz::setInstanceField);
- }
- }
- for (DexType toUnbox : enumsToUnbox) {
- lensBuilder.map(toUnbox, factory.intType);
- }
- unboxedEnumsMethods.forEach(
- (newHolderType, movedMethods) -> {
- DexProgramClass newHolderClass = appView.definitionFor(newHolderType).asProgramClass();
- newHolderClass.addDirectMethods(movedMethods);
- });
- return lensBuilder.build(factory, appView.graphLens(), enumsToUnbox);
- }
-
- private void clearEnumToUnboxMethod(DexEncodedMethod enumMethod) {
- // The compiler may have references to the enum methods, but such methods will be removed
- // and they cannot be reprocessed since their rewriting through the lensCodeRewriter/
- // enumUnboxerRewriter will generate invalid code.
- // To work around this problem we clear such methods, i.e., we replace the code object by
- // an empty throwing code object, so reprocessing won't take time and will be valid.
- enumMethod.setCode(enumMethod.buildEmptyThrowingCode(appView.options()), appView);
- }
-
- private DexEncodedMethod fixupEncodedMethodToUtility(
- DexEncodedMethod encodedMethod, DexType newHolder) {
- DexMethod method = encodedMethod.method;
- DexString newMethodName =
- factory.createString(
- enumUnboxerRewriter.compatibleName(method.holder)
- + "$"
- + (encodedMethod.isStatic() ? "s" : "v")
- + "$"
- + method.name.toString());
- DexProto proto =
- encodedMethod.isStatic() ? method.proto : factory.prependHolderToProto(method);
- DexMethod newMethod = factory.createMethod(newHolder, fixupProto(proto), newMethodName);
- assert appView.definitionFor(encodedMethod.holder()).lookupMethod(newMethod) == null;
- lensBuilder.move(method, encodedMethod.isStatic(), newMethod, true);
- encodedMethod.accessFlags.promoteToPublic();
- encodedMethod.accessFlags.promoteToStatic();
- encodedMethod.clearAnnotations();
- encodedMethod.clearParameterAnnotations();
- return encodedMethod.toTypeSubstitutedMethod(newMethod);
- }
-
- private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod encodedMethod) {
- DexProto newProto = fixupProto(encodedMethod.proto());
- if (newProto == encodedMethod.proto()) {
- return encodedMethod;
- }
- assert !encodedMethod.isClassInitializer();
- // We add the $enumunboxing$ suffix to make sure we do not create a library override.
- String newMethodName =
- encodedMethod.getName().toString()
- + (encodedMethod.isNonPrivateVirtualMethod() ? "$enumunboxing$" : "");
- DexMethod newMethod = factory.createMethod(encodedMethod.holder(), newProto, newMethodName);
- newMethod = ensureUniqueMethod(encodedMethod, newMethod);
- int numberOfExtraNullParameters = newMethod.getArity() - encodedMethod.method.getArity();
- boolean isStatic = encodedMethod.isStatic();
- lensBuilder.move(
- encodedMethod.method, isStatic, newMethod, isStatic, numberOfExtraNullParameters);
- DexEncodedMethod newEncodedMethod = encodedMethod.toTypeSubstitutedMethod(newMethod);
- assert !encodedMethod.isLibraryMethodOverride().isTrue()
- : "Enum unboxing is changing the signature of a library override in a non unboxed class.";
- if (newEncodedMethod.isNonPrivateVirtualMethod()) {
- newEncodedMethod.setLibraryMethodOverride(OptionalBool.FALSE);
- }
- return newEncodedMethod;
- }
-
- private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
- DexClass holder = appView.definitionFor(encodedMethod.holder());
- assert holder != null;
- if (encodedMethod.isInstanceInitializer()) {
- while (holder.lookupMethod(newMethod) != null) {
- newMethod =
- factory.createMethod(
- newMethod.holder,
- factory.appendTypeToProto(
- newMethod.proto, relocator.getDefaultEnumUnboxingUtility()),
- newMethod.name);
- }
- } else {
- int index = 0;
- while (holder.lookupMethod(newMethod) != null) {
- newMethod =
- factory.createMethod(
- newMethod.holder,
- newMethod.proto,
- encodedMethod.getName().toString() + "$enumunboxing$" + index++);
- }
- }
- return newMethod;
- }
-
- private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
- if (fields == null) {
- return;
- }
- for (int i = 0; i < fields.size(); i++) {
- DexEncodedField encodedField = fields.get(i);
- DexField field = encodedField.field;
- DexType newType = fixupType(field.type);
- if (newType != field.type) {
- DexField newField = factory.createField(field.holder, newType, field.name);
- lensBuilder.move(field, newField);
- DexEncodedField newEncodedField = encodedField.toTypeSubstitutedField(newField);
- setter.setField(i, newEncodedField);
- if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
- assert encodedField.getStaticValue() == DexValueNull.NULL;
- newEncodedField.setStaticValue(DexValueInt.DEFAULT);
- // TODO(b/150593449): Support conversion from DexValueEnum to DexValueInt.
- }
- }
- }
- }
-
- private DexProto fixupProto(DexProto proto) {
- DexType returnType = fixupType(proto.returnType);
- DexType[] arguments = fixupTypes(proto.parameters.values);
- return factory.createProto(returnType, arguments);
- }
-
- private DexType fixupType(DexType type) {
- if (type.isArrayType()) {
- DexType base = type.toBaseType(factory);
- DexType fixed = fixupType(base);
- if (base == fixed) {
- return type;
- }
- return type.replaceBaseType(fixed, factory);
- }
- if (type.isClassType() && enumsToUnbox.contains(type)) {
- DexType intType = factory.intType;
- lensBuilder.map(type, intType);
- return intType;
- }
- return type;
- }
-
- private DexType[] fixupTypes(DexType[] types) {
- DexType[] result = new DexType[types.length];
- for (int i = 0; i < result.length; i++) {
- result[i] = fixupType(types[i]);
- }
- return result;
- }
- }
-
- private static class EnumUnboxingLens extends NestedGraphLens {
-
- private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod;
- private final Set<DexType> unboxedEnums;
-
- EnumUnboxingLens(
- Map<DexType, DexType> typeMap,
- Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap,
- BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
- GraphLens previousLens,
- DexItemFactory dexItemFactory,
- Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod,
- Set<DexType> unboxedEnums) {
- super(
- typeMap,
- methodMap,
- fieldMap,
- originalFieldSignatures,
- originalMethodSignatures,
- previousLens,
- dexItemFactory);
- this.prototypeChangesPerMethod = prototypeChangesPerMethod;
- this.unboxedEnums = unboxedEnums;
- }
-
- @Override
- protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
- // During the second IR processing enum unboxing is the only optimization rewriting
- // prototype description, if this does not hold, remove the assertion and merge
- // the two prototype changes.
- assert prototypeChanges.isEmpty();
- return prototypeChangesPerMethod.getOrDefault(method, RewrittenPrototypeDescription.none());
- }
-
- @Override
- protected Invoke.Type mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, Invoke.Type type) {
- if (unboxedEnums.contains(originalMethod.holder)) {
- // Methods moved from unboxed enums to the utility class are either static or statified.
- assert newMethod != originalMethod;
- return Invoke.Type.STATIC;
- }
- return type;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- private static class Builder extends NestedGraphLens.Builder {
-
- private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
- new IdentityHashMap<>();
-
- public void move(DexMethod from, boolean fromStatic, DexMethod to, boolean toStatic) {
- move(from, fromStatic, to, toStatic, 0);
- }
-
- public void move(
- DexMethod from,
- boolean fromStatic,
- DexMethod to,
- boolean toStatic,
- int numberOfExtraNullParameters) {
- super.move(from, to);
- int offsetDiff = 0;
- int toOffset = BooleanUtils.intValue(!toStatic);
- ArgumentInfoCollection.Builder builder = ArgumentInfoCollection.builder();
- if (fromStatic != toStatic) {
- assert toStatic;
- offsetDiff = 1;
- builder.addArgumentInfo(
- 0, new RewrittenTypeInfo(from.holder, to.proto.parameters.values[0]));
- }
- for (int i = 0; i < from.proto.parameters.size(); i++) {
- DexType fromType = from.proto.parameters.values[i];
- DexType toType = to.proto.parameters.values[i + offsetDiff];
- if (fromType != toType) {
- builder.addArgumentInfo(
- i + offsetDiff + toOffset, new RewrittenTypeInfo(fromType, toType));
- }
- }
- RewrittenTypeInfo returnInfo =
- from.proto.returnType == to.proto.returnType
- ? null
- : new RewrittenTypeInfo(from.proto.returnType, to.proto.returnType);
- prototypeChangesPerMethod.put(
- to,
- RewrittenPrototypeDescription.createForRewrittenTypes(returnInfo, builder.build())
- .withExtraUnusedNullParameters(numberOfExtraNullParameters));
- }
-
- public EnumUnboxingLens build(
- DexItemFactory dexItemFactory, GraphLens previousLens, Set<DexType> unboxedEnums) {
- if (typeMap.isEmpty() && methodMap.isEmpty() && fieldMap.isEmpty()) {
- return null;
- }
- return new EnumUnboxingLens(
- typeMap,
- methodMap,
- fieldMap,
- originalFieldSignatures,
- originalMethodSignatures,
- previousLens,
- dexItemFactory,
- ImmutableMap.copyOf(prototypeChangesPerMethod),
- ImmutableSet.copyOf(unboxedEnums));
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
new file mode 100644
index 0000000..869cb6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -0,0 +1,139 @@
+// Copyright (c) 2020, 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.enums;
+
+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.graph.GraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+class EnumUnboxingLens extends GraphLens.NestedGraphLens {
+
+ private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod;
+ private final Set<DexType> unboxedEnums;
+
+ EnumUnboxingLens(
+ Map<DexType, DexType> typeMap,
+ Map<DexMethod, DexMethod> methodMap,
+ Map<DexField, DexField> fieldMap,
+ BiMap<DexField, DexField> originalFieldSignatures,
+ BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ GraphLens previousLens,
+ DexItemFactory dexItemFactory,
+ Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod,
+ Set<DexType> unboxedEnums) {
+ super(
+ typeMap,
+ methodMap,
+ fieldMap,
+ originalFieldSignatures,
+ originalMethodSignatures,
+ previousLens,
+ dexItemFactory);
+ this.prototypeChangesPerMethod = prototypeChangesPerMethod;
+ this.unboxedEnums = unboxedEnums;
+ }
+
+ @Override
+ protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
+ RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
+ // During the second IR processing enum unboxing is the only optimization rewriting
+ // prototype description, if this does not hold, remove the assertion and merge
+ // the two prototype changes.
+ assert prototypeChanges.isEmpty();
+ return prototypeChangesPerMethod.getOrDefault(method, RewrittenPrototypeDescription.none());
+ }
+
+ @Override
+ protected Invoke.Type mapInvocationType(
+ DexMethod newMethod, DexMethod originalMethod, Invoke.Type type) {
+ if (unboxedEnums.contains(originalMethod.holder)) {
+ // Methods moved from unboxed enums to the utility class are either static or statified.
+ assert newMethod != originalMethod;
+ return Invoke.Type.STATIC;
+ }
+ return type;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder extends NestedGraphLens.Builder {
+
+ private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
+ new IdentityHashMap<>();
+
+ public void move(DexMethod from, boolean fromStatic, DexMethod to, boolean toStatic) {
+ move(from, fromStatic, to, toStatic, 0);
+ }
+
+ public void move(
+ DexMethod from,
+ boolean fromStatic,
+ DexMethod to,
+ boolean toStatic,
+ int numberOfExtraNullParameters) {
+ super.move(from, to);
+ int offsetDiff = 0;
+ int toOffset = BooleanUtils.intValue(!toStatic);
+ RewrittenPrototypeDescription.ArgumentInfoCollection.Builder builder =
+ RewrittenPrototypeDescription.ArgumentInfoCollection.builder();
+ if (fromStatic != toStatic) {
+ assert toStatic;
+ offsetDiff = 1;
+ builder.addArgumentInfo(
+ 0,
+ new RewrittenPrototypeDescription.RewrittenTypeInfo(
+ from.holder, to.proto.parameters.values[0]));
+ }
+ for (int i = 0; i < from.proto.parameters.size(); i++) {
+ DexType fromType = from.proto.parameters.values[i];
+ DexType toType = to.proto.parameters.values[i + offsetDiff];
+ if (fromType != toType) {
+ builder.addArgumentInfo(
+ i + offsetDiff + toOffset,
+ new RewrittenPrototypeDescription.RewrittenTypeInfo(fromType, toType));
+ }
+ }
+ RewrittenPrototypeDescription.RewrittenTypeInfo returnInfo =
+ from.proto.returnType == to.proto.returnType
+ ? null
+ : new RewrittenPrototypeDescription.RewrittenTypeInfo(
+ from.proto.returnType, to.proto.returnType);
+ prototypeChangesPerMethod.put(
+ to,
+ RewrittenPrototypeDescription.createForRewrittenTypes(returnInfo, builder.build())
+ .withExtraUnusedNullParameters(numberOfExtraNullParameters));
+ }
+
+ public EnumUnboxingLens build(
+ DexItemFactory dexItemFactory, GraphLens previousLens, Set<DexType> unboxedEnums) {
+ if (typeMap.isEmpty() && methodMap.isEmpty() && fieldMap.isEmpty()) {
+ return null;
+ }
+ return new EnumUnboxingLens(
+ typeMap,
+ methodMap,
+ fieldMap,
+ originalFieldSignatures,
+ originalMethodSignatures,
+ previousLens,
+ dexItemFactory,
+ ImmutableMap.copyOf(prototypeChangesPerMethod),
+ ImmutableSet.copyOf(unboxedEnums));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
new file mode 100644
index 0000000..879c909
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -0,0 +1,221 @@
+// Copyright (c) 2020, 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.enums;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+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.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+class EnumUnboxingTreeFixer {
+
+ private final Map<DexType, List<DexEncodedMethod>> unboxedEnumsMethods = new IdentityHashMap<>();
+ private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.builder();
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+ private final Set<DexType> enumsToUnbox;
+ private final UnboxedEnumMemberRelocator relocator;
+ private final EnumUnboxingRewriter enumUnboxerRewriter;
+
+ EnumUnboxingTreeFixer(
+ AppView<?> appView,
+ Set<DexType> enumsToUnbox,
+ UnboxedEnumMemberRelocator relocator,
+ EnumUnboxingRewriter enumUnboxerRewriter) {
+ this.appView = appView;
+ this.factory = appView.dexItemFactory();
+ this.enumsToUnbox = enumsToUnbox;
+ this.relocator = relocator;
+ this.enumUnboxerRewriter = enumUnboxerRewriter;
+ }
+
+ GraphLens.NestedGraphLens fixupTypeReferences() {
+ assert enumUnboxerRewriter != null;
+ // Fix all methods and fields using enums to unbox.
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (enumsToUnbox.contains(clazz.type)) {
+ // Clear the initializers and move the static methods to the new location.
+ Set<DexEncodedMethod> methodsToRemove = Sets.newIdentityHashSet();
+ clazz
+ .methods()
+ .forEach(
+ m -> {
+ if (m.isInitializer()) {
+ clearEnumToUnboxMethod(m);
+ } else {
+ DexType newHolder = relocator.getNewMemberLocationFor(clazz.type);
+ List<DexEncodedMethod> movedMethods =
+ unboxedEnumsMethods.computeIfAbsent(newHolder, k -> new ArrayList<>());
+ movedMethods.add(fixupEncodedMethodToUtility(m, newHolder));
+ methodsToRemove.add(m);
+ }
+ });
+ clazz.getMethodCollection().removeMethods(methodsToRemove);
+ } else {
+ clazz.getMethodCollection().replaceMethods(this::fixupEncodedMethod);
+ fixupFields(clazz.staticFields(), clazz::setStaticField);
+ fixupFields(clazz.instanceFields(), clazz::setInstanceField);
+ }
+ }
+ for (DexType toUnbox : enumsToUnbox) {
+ lensBuilder.map(toUnbox, factory.intType);
+ }
+ unboxedEnumsMethods.forEach(
+ (newHolderType, movedMethods) -> {
+ DexProgramClass newHolderClass = appView.definitionFor(newHolderType).asProgramClass();
+ newHolderClass.addDirectMethods(movedMethods);
+ });
+ return lensBuilder.build(factory, appView.graphLens(), enumsToUnbox);
+ }
+
+ private void clearEnumToUnboxMethod(DexEncodedMethod enumMethod) {
+ // The compiler may have references to the enum methods, but such methods will be removed
+ // and they cannot be reprocessed since their rewriting through the lensCodeRewriter/
+ // enumUnboxerRewriter will generate invalid code.
+ // To work around this problem we clear such methods, i.e., we replace the code object by
+ // an empty throwing code object, so reprocessing won't take time and will be valid.
+ enumMethod.setCode(enumMethod.buildEmptyThrowingCode(appView.options()), appView);
+ }
+
+ private DexEncodedMethod fixupEncodedMethodToUtility(
+ DexEncodedMethod encodedMethod, DexType newHolder) {
+ DexMethod method = encodedMethod.method;
+ DexString newMethodName =
+ factory.createString(
+ enumUnboxerRewriter.compatibleName(method.holder)
+ + "$"
+ + (encodedMethod.isStatic() ? "s" : "v")
+ + "$"
+ + method.name.toString());
+ DexProto proto = encodedMethod.isStatic() ? method.proto : factory.prependHolderToProto(method);
+ DexMethod newMethod = factory.createMethod(newHolder, fixupProto(proto), newMethodName);
+ assert appView.definitionFor(encodedMethod.holder()).lookupMethod(newMethod) == null;
+ lensBuilder.move(method, encodedMethod.isStatic(), newMethod, true);
+ encodedMethod.accessFlags.promoteToPublic();
+ encodedMethod.accessFlags.promoteToStatic();
+ encodedMethod.clearAnnotations();
+ encodedMethod.clearParameterAnnotations();
+ return encodedMethod.toTypeSubstitutedMethod(newMethod);
+ }
+
+ private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod encodedMethod) {
+ DexProto newProto = fixupProto(encodedMethod.proto());
+ if (newProto == encodedMethod.proto()) {
+ return encodedMethod;
+ }
+ assert !encodedMethod.isClassInitializer();
+ // We add the $enumunboxing$ suffix to make sure we do not create a library override.
+ String newMethodName =
+ encodedMethod.getName().toString()
+ + (encodedMethod.isNonPrivateVirtualMethod() ? "$enumunboxing$" : "");
+ DexMethod newMethod = factory.createMethod(encodedMethod.holder(), newProto, newMethodName);
+ newMethod = ensureUniqueMethod(encodedMethod, newMethod);
+ int numberOfExtraNullParameters = newMethod.getArity() - encodedMethod.method.getArity();
+ boolean isStatic = encodedMethod.isStatic();
+ lensBuilder.move(
+ encodedMethod.method, isStatic, newMethod, isStatic, numberOfExtraNullParameters);
+ DexEncodedMethod newEncodedMethod = encodedMethod.toTypeSubstitutedMethod(newMethod);
+ assert !encodedMethod.isLibraryMethodOverride().isTrue()
+ : "Enum unboxing is changing the signature of a library override in a non unboxed class.";
+ if (newEncodedMethod.isNonPrivateVirtualMethod()) {
+ newEncodedMethod.setLibraryMethodOverride(OptionalBool.FALSE);
+ }
+ return newEncodedMethod;
+ }
+
+ private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
+ DexClass holder = appView.definitionFor(encodedMethod.holder());
+ assert holder != null;
+ if (encodedMethod.isInstanceInitializer()) {
+ while (holder.lookupMethod(newMethod) != null) {
+ newMethod =
+ factory.createMethod(
+ newMethod.holder,
+ factory.appendTypeToProto(
+ newMethod.proto, relocator.getDefaultEnumUnboxingUtility()),
+ newMethod.name);
+ }
+ } else {
+ int index = 0;
+ while (holder.lookupMethod(newMethod) != null) {
+ newMethod =
+ factory.createMethod(
+ newMethod.holder,
+ newMethod.proto,
+ encodedMethod.getName().toString() + "$enumunboxing$" + index++);
+ }
+ }
+ return newMethod;
+ }
+
+ private void fixupFields(List<DexEncodedField> fields, DexClass.FieldSetter setter) {
+ if (fields == null) {
+ return;
+ }
+ for (int i = 0; i < fields.size(); i++) {
+ DexEncodedField encodedField = fields.get(i);
+ DexField field = encodedField.field;
+ DexType newType = fixupType(field.type);
+ if (newType != field.type) {
+ DexField newField = factory.createField(field.holder, newType, field.name);
+ lensBuilder.move(field, newField);
+ DexEncodedField newEncodedField = encodedField.toTypeSubstitutedField(newField);
+ setter.setField(i, newEncodedField);
+ if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
+ assert encodedField.getStaticValue() == DexValue.DexValueNull.NULL;
+ newEncodedField.setStaticValue(DexValue.DexValueInt.DEFAULT);
+ // TODO(b/150593449): Support conversion from DexValueEnum to DexValueInt.
+ }
+ }
+ }
+ }
+
+ private DexProto fixupProto(DexProto proto) {
+ DexType returnType = fixupType(proto.returnType);
+ DexType[] arguments = fixupTypes(proto.parameters.values);
+ return factory.createProto(returnType, arguments);
+ }
+
+ private DexType fixupType(DexType type) {
+ if (type.isArrayType()) {
+ DexType base = type.toBaseType(factory);
+ DexType fixed = fixupType(base);
+ if (base == fixed) {
+ return type;
+ }
+ return type.replaceBaseType(fixed, factory);
+ }
+ if (type.isClassType() && enumsToUnbox.contains(type)) {
+ DexType intType = factory.intType;
+ lensBuilder.map(type, intType);
+ return intType;
+ }
+ return type;
+ }
+
+ private DexType[] fixupTypes(DexType[] types) {
+ DexType[] result = new DexType[types.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = fixupType(types[i]);
+ }
+ return result;
+ }
+}