Support Enum.compareTo(Object) in enum unboxer
This also moves the synthesis of the shared enum unboxing utility class and the compareTo utility method to the synthetic infrastructure.
Bug: 190098858
Change-Id: I237ee2f67afaebe12eea79eb02f80f4ce60a5030
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 36d67f5..49cab56 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.dex.Constants.ACC_STATIC;
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_PREFIX;
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
@@ -896,15 +895,11 @@
private Int2ReferenceSortedMap<FrameType> computeInitialLocals(
DexType context, DexEncodedMethod method, RewrittenPrototypeDescription protoTypeChanges) {
- int accessFlags =
- protoTypeChanges.isEmpty()
- ? method.accessFlags.modifiedFlags
- : method.accessFlags.originalFlags;
Int2ReferenceSortedMap<FrameType> initialLocals = new Int2ReferenceAVLTreeMap<>();
int index = 0;
if (method.isInstanceInitializer()) {
initialLocals.put(index++, FrameType.uninitializedThis());
- } else if (!MethodAccessFlags.isSet(ACC_STATIC, accessFlags)) {
+ } else if (!method.getAccessFlags().isStatic()) {
initialLocals.put(index++, FrameType.initialized(context));
}
ArgumentInfoCollection argumentsInfo = protoTypeChanges.getArgumentInfoCollection();
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 90c145e..25cf31b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1546,6 +1546,8 @@
public final DexMethod nameMethod;
public final DexMethod toString;
public final DexMethod compareTo;
+ public final DexMethod compareToWithObject =
+ createMethod(enumType, createProto(intType, objectType), "compareTo");
public final DexMethod equals;
public final DexMethod hashCode;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 4ba4555..9bcee66 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexProgramClass;
@@ -55,6 +56,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean isProcessedConcurrently(ProgramMethod method) {
// In D8 all methods are considered independently compiled.
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index d15bccf..fcfcb1f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -750,6 +750,11 @@
}
timing.end();
+ if (enumUnboxer != null) {
+ // TODO(b/190098858): Uncomment when methods are synthesized on-the-fly.
+ // enumUnboxer.unsetRewriter();
+ }
+
// All the code that should be impacted by the lenses inserted between phase 1 and phase 2
// have now been processed and rewritten, we clear code lens rewriting so that the class
// staticizer and phase 3 does not perform again the rewriting.
@@ -1206,7 +1211,7 @@
if (appView.graphLens().hasCodeRewritings()) {
assert lensCodeRewriter != null;
timing.begin("Lens rewrite");
- lensCodeRewriter.rewrite(code, context);
+ lensCodeRewriter.rewrite(code, context, methodProcessor);
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 70a2316..0cf836d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -140,9 +140,11 @@
}
/** Replace type appearances, invoke targets and field accesses with actual definitions. */
- public void rewrite(IRCode code, ProgramMethod method) {
+ public void rewrite(IRCode code, ProgramMethod method, MethodProcessor methodProcessor) {
Set<Phi> affectedPhis =
- enumUnboxer != null ? enumUnboxer.rewriteCode(code) : Sets.newIdentityHashSet();
+ enumUnboxer != null
+ ? enumUnboxer.rewriteCode(code, methodProcessor)
+ : Sets.newIdentityHashSet();
GraphLens graphLens = appView.graphLens();
DexItemFactory factory = appView.dexItemFactory();
// Rewriting types that affects phi can cause us to compute TOP for cyclic phi's. To solve this
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index 140e0ce..9857098 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
public abstract class MethodProcessor {
@@ -11,6 +12,8 @@
return false;
}
+ public abstract MethodProcessingContext createMethodProcessingContext(ProgramMethod method);
+
public abstract boolean isProcessedConcurrently(ProgramMethod method);
public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index f637225..e40cae1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -45,6 +45,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index fd1ecdf..a8e5508 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -43,6 +43,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
assert !wave.contains(method);
return !processed.contains(method);
@@ -132,8 +137,7 @@
assert feedback.noUpdatesLeft();
ThreadUtils.processItems(
wave,
- method ->
- consumer.accept(method, processorContext.createMethodProcessingContext(method)),
+ method -> consumer.accept(method, createMethodProcessingContext(method)),
executorService);
feedback.updateVisibleOptimizationInfo();
processed.addAll(wave);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index 8969cba..9bd75fe 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -41,6 +41,8 @@
private final PostMethodProcessor.Builder postMethodProcessorBuilder;
private final Deque<SortedProgramMethodSet> waves;
+ private ProcessorContext processorContext;
+
private PrimaryMethodProcessor(
AppView<AppInfoWithLiveness> appView,
PostMethodProcessor.Builder postMethodProcessorBuilder,
@@ -62,6 +64,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean isPrimaryMethodProcessor() {
return true;
}
@@ -125,7 +132,7 @@
TimingMerger merger =
timing.beginMerger("primary-processor", ThreadUtils.getNumberOfThreads(executorService));
while (!waves.isEmpty()) {
- ProcessorContext processorContext = appView.createProcessorContext();
+ processorContext = appView.createProcessorContext();
wave = waves.removeFirst();
assert !wave.isEmpty();
assert waveExtension.isEmpty();
@@ -135,9 +142,7 @@
ThreadUtils.processItemsWithResults(
wave,
method -> {
- Timing time =
- consumer.apply(
- method, processorContext.createMethodProcessingContext(method));
+ Timing time = consumer.apply(method, createMethodProcessingContext(method));
time.end();
return time;
},
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 9e82361..e4d045d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -732,7 +732,7 @@
if (inliningIRProvider.shouldApplyCodeRewritings(target)) {
assert lensCodeRewriter != null;
- lensCodeRewriter.rewrite(code, target);
+ lensCodeRewriter.rewrite(code, target, inliningIRProvider.getMethodProcessor());
}
if (options.testing.inlineeIrModifier != null) {
options.testing.inlineeIrModifier.accept(code);
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 7e0da20..ec450c9 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
@@ -71,6 +71,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
@@ -533,12 +534,12 @@
utilityClass -> utilityClass.getDefinition().forEachProgramMethod(postBuilder::add));
fieldAccessInfoCollectionModifierBuilder.build().modify(appView);
- enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumDataMap, utilityClasses);
EnumUnboxingTreeFixer.Result treeFixerResult =
new EnumUnboxingTreeFixer(appView, enumDataMap, enumClassesToUnbox, utilityClasses)
.fixupTypeReferences(converter, executorService);
EnumUnboxingLens enumUnboxingLens = treeFixerResult.getLens();
- enumUnboxerRewriter.setEnumUnboxingLens(enumUnboxingLens);
+ enumUnboxerRewriter =
+ new EnumUnboxingRewriter(appView, converter, enumUnboxingLens, enumDataMap, utilityClasses);
appView.setUnboxedEnums(enumDataMap);
GraphLens previousLens = appView.graphLens();
appView.rewriteWithLensAndApplication(enumUnboxingLens, appBuilder.build());
@@ -1287,8 +1288,13 @@
return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
// TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
- if (singleTargetReference == factory.enumMembers.compareTo) {
- return Reason.ELIGIBLE;
+ if (singleTargetReference == factory.enumMembers.compareTo
+ || singleTargetReference == factory.enumMembers.compareToWithObject) {
+ DexProgramClass otherEnumClass =
+ getEnumUnboxingCandidateOrNull(invoke.getLastArgument().getType());
+ if (otherEnumClass == enumClass || invoke.getLastArgument().getType().isNullType()) {
+ return Reason.ELIGIBLE;
+ }
} else if (singleTargetReference == factory.enumMembers.equals) {
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.nameMethod
@@ -1423,11 +1429,11 @@
return false;
}
- public Set<Phi> rewriteCode(IRCode code) {
+ public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
// This has no effect during primary processing since the enumUnboxerRewriter is set
// in between primary and post processing.
if (enumUnboxerRewriter != null) {
- return enumUnboxerRewriter.rewriteCode(code);
+ return enumUnboxerRewriter.rewriteCode(code, methodProcessor);
}
return Sets.newIdentityHashSet();
}
@@ -1438,4 +1444,8 @@
enumUnboxerRewriter.synthesizeEnumUnboxingUtilityMethods(converter, executorService);
}
}
+
+ public void unsetRewriter() {
+ enumUnboxerRewriter = null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 6b8a013..ccc044e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -69,10 +70,11 @@
private static final CfVersion REQUIRED_CLASS_FILE_VERSION = CfVersion.V1_8;
private final AppView<AppInfoWithLiveness> appView;
+ private final IRConverter converter;
private final DexItemFactory factory;
private final InternalOptions options;
private final EnumDataMap unboxedEnumsData;
- private EnumUnboxingLens enumUnboxingLens;
+ private final EnumUnboxingLens enumUnboxingLens;
private final EnumUnboxingUtilityClasses utilityClasses;
private final Map<DexMethod, DexEncodedMethod> utilityMethods = new ConcurrentHashMap<>();
@@ -85,11 +87,15 @@
EnumUnboxingRewriter(
AppView<AppInfoWithLiveness> appView,
+ IRConverter converter,
+ EnumUnboxingLens enumUnboxingLens,
EnumDataMap unboxedEnumsInstanceFieldData,
EnumUnboxingUtilityClasses utilityClasses) {
this.appView = appView;
+ this.converter = converter;
this.factory = appView.dexItemFactory();
this.options = appView.options();
+ this.enumUnboxingLens = enumUnboxingLens;
this.unboxedEnumsData = unboxedEnumsInstanceFieldData;
this.utilityClasses = utilityClasses;
@@ -131,11 +137,7 @@
return utilityClasses.getSharedUtilityClass();
}
- public void setEnumUnboxingLens(EnumUnboxingLens enumUnboxingLens) {
- this.enumUnboxingLens = enumUnboxingLens;
- }
-
- Set<Phi> rewriteCode(IRCode code) {
+ Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
// We should not process the enum methods, they will be removed and they may contain invalid
// rewriting rules.
if (unboxedEnumsData.isEmpty()) {
@@ -182,9 +184,13 @@
replaceEnumInvoke(
iterator, invokeMethod, equalsUtilityMethod, m -> synthesizeEqualsMethod());
continue;
- } else if (invokedMethod == factory.enumMembers.compareTo) {
+ } else if (invokedMethod == factory.enumMembers.compareTo
+ || invokedMethod == factory.enumMembers.compareToWithObject) {
replaceEnumInvoke(
- iterator, invokeMethod, compareToUtilityMethod, m -> synthesizeCompareToMethod());
+ iterator,
+ invokeMethod,
+ getSharedUtilityClass()
+ .ensureCompareToMethod(appView, converter, methodProcessor));
continue;
} else if (invokedMethod == factory.enumMembers.nameMethod) {
rewriteNameMethod(iterator, invokeMethod, enumType);
@@ -466,11 +472,18 @@
}
private void replaceEnumInvoke(
+ InstructionListIterator iterator, InvokeMethod invoke, ProgramMethod method) {
+ replaceEnumInvoke(iterator, invoke, method.getReference(), null);
+ }
+
+ private void replaceEnumInvoke(
InstructionListIterator iterator,
InvokeMethod invoke,
DexMethod method,
Function<DexMethod, DexEncodedMethod> synthesizor) {
- utilityMethods.computeIfAbsent(method, synthesizor);
+ if (synthesizor != null) {
+ utilityMethods.computeIfAbsent(method, synthesizor);
+ }
InvokeStatic replacement =
new InvokeStatic(
method, invoke.hasUnusedOutValue() ? null : invoke.outValue(), invoke.arguments());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
index 1753375..05d4955 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
@@ -73,7 +73,7 @@
SharedEnumUnboxingUtilityClass sharedUtilityClass =
SharedEnumUnboxingUtilityClass.builder(
appView, enumDataMap, enumsToUnbox, fieldAccessInfoCollectionModifierBuilder)
- .build(appBuilder);
+ .build();
ImmutableMap<DexType, LocalEnumUnboxingUtilityClass> localUtilityClasses =
createLocalUtilityClasses(enumsToUnbox, appBuilder);
this.localUtilityClasses = localUtilityClasses;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 19adc9f..3e556e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -26,21 +26,21 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
@@ -50,17 +50,16 @@
public class SharedEnumUnboxingUtilityClass extends EnumUnboxingUtilityClass {
- public static final String ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX =
- "$r8$EnumUnboxingSharedUtility";
-
private final DexProgramClass sharedUtilityClass;
- private final ProgramField valuesField;
+ private final DexProgramClass synthesizingContext;
private final ProgramMethod valuesMethod;
public SharedEnumUnboxingUtilityClass(
- DexProgramClass sharedUtilityClass, ProgramField valuesField, ProgramMethod valuesMethod) {
+ DexProgramClass sharedUtilityClass,
+ DexProgramClass synthesizingContext,
+ ProgramMethod valuesMethod) {
this.sharedUtilityClass = sharedUtilityClass;
- this.valuesField = valuesField;
+ this.synthesizingContext = synthesizingContext;
this.valuesMethod = valuesMethod;
}
@@ -73,15 +72,46 @@
appView, enumDataMap, enumsToUnbox, fieldAccessInfoCollectionModifierBuilder);
}
+ public ProgramMethod ensureCompareToMethod(
+ AppView<AppInfoWithLiveness> appView,
+ IRConverter converter,
+ MethodProcessor methodProcessor) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ // TODO(b/191957637): Consider creating free flowing static methods instead. The synthetic
+ // infrastructure needs to be augmented with a new method ensureFixedMethod() or
+ // ensureFixedFreeFlowingMethod() for this, if we want to create only one utility method (and
+ // not one per use context).
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClassMethod(
+ dexItemFactory.enumMembers.compareTo.getName(),
+ dexItemFactory.createProto(
+ dexItemFactory.intType, dexItemFactory.intType, dexItemFactory.intType),
+ SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS,
+ synthesizingContext,
+ appView,
+ ConsumerUtils.emptyConsumer(),
+ methodBuilder ->
+ methodBuilder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ method ->
+ EnumUnboxingCfMethods.EnumUnboxingMethods_compareTo(
+ appView.options(), method))
+ .setClassFileVersion(CfVersion.V1_6),
+ newMethod ->
+ converter.processDesugaredMethod(
+ newMethod,
+ OptimizationFeedbackSimple.getInstance(),
+ methodProcessor,
+ methodProcessor.createMethodProcessingContext(newMethod)));
+ }
+
@Override
public DexProgramClass getDefinition() {
return sharedUtilityClass;
}
- public ProgramField getValuesField() {
- return valuesField;
- }
-
public ProgramMethod getValuesMethod() {
return valuesMethod;
}
@@ -95,12 +125,10 @@
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
private final EnumDataMap enumDataMap;
- private final Set<DexProgramClass> enumsToUnbox;
private final FieldAccessInfoCollectionModifier.Builder
fieldAccessInfoCollectionModifierBuilder;
- private final DexType sharedUtilityClassType;
+ private final DexProgramClass synthesizingContext;
- private DexEncodedField valuesField;
private DexEncodedMethod valuesMethod;
private Builder(
@@ -108,52 +136,40 @@
EnumDataMap enumDataMap,
Set<DexProgramClass> enumsToUnbox,
FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder) {
+ DexProgramClass synthesizingContext = findDeterministicContextType(enumsToUnbox);
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.enumDataMap = enumDataMap;
- this.enumsToUnbox = enumsToUnbox;
this.fieldAccessInfoCollectionModifierBuilder = fieldAccessInfoCollectionModifierBuilder;
- this.sharedUtilityClassType =
- EnumUnboxingUtilityClasses.Builder.getUtilityClassType(
- findDeterministicContextType(enumsToUnbox),
- ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX,
- dexItemFactory);
-
- assert appView.appInfo().definitionForWithoutExistenceAssert(sharedUtilityClassType) == null;
+ this.synthesizingContext = synthesizingContext;
}
- SharedEnumUnboxingUtilityClass build(DirectMappedDexApplication.Builder appBuilder) {
+ SharedEnumUnboxingUtilityClass build() {
DexProgramClass clazz = createClass();
- appBuilder.addSynthesizedClass(clazz);
- appView.appInfo().addSynthesizedClassToBase(clazz, enumsToUnbox);
return new SharedEnumUnboxingUtilityClass(
- clazz, new ProgramField(clazz, valuesField), new ProgramMethod(clazz, valuesMethod));
+ clazz, synthesizingContext, new ProgramMethod(clazz, valuesMethod));
}
private DexProgramClass createClass() {
- DexEncodedField valuesField = createValuesField(sharedUtilityClassType);
- return new DexProgramClass(
- sharedUtilityClassType,
- null,
- new SynthesizedOrigin("enum unboxing", EnumUnboxer.class),
- ClassAccessFlags.createPublicFinalSynthetic(),
- dexItemFactory.objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- new DexEncodedField[] {valuesField},
- DexEncodedField.EMPTY_ARRAY,
- new DexEncodedMethod[] {
- createClassInitializer(valuesField), createValuesMethod(valuesField)
- },
- DexEncodedMethod.EMPTY_ARRAY,
- dexItemFactory.getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType);
+ DexProgramClass clazz =
+ appView
+ .getSyntheticItems()
+ .createFixedClass(
+ SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS,
+ synthesizingContext,
+ appView,
+ classBuilder -> {
+ DexType sharedUtilityClassType = classBuilder.getType();
+ DexEncodedField valuesField = createValuesField(sharedUtilityClassType);
+ classBuilder
+ .setDirectMethods(
+ ImmutableList.of(
+ createClassInitializer(sharedUtilityClassType, valuesField),
+ createValuesMethod(sharedUtilityClassType, valuesField)))
+ .setStaticFields(ImmutableList.of(valuesField));
+ });
+ assert clazz.getAccessFlags().equals(ClassAccessFlags.createPublicFinalSynthetic());
+ return clazz;
}
// Fields.
@@ -172,25 +188,26 @@
fieldAccessInfoCollectionModifierBuilder
.recordFieldReadInUnknownContext(valuesField.getReference())
.recordFieldWriteInUnknownContext(valuesField.getReference());
- this.valuesField = valuesField;
return valuesField;
}
// Methods.
- private DexEncodedMethod createClassInitializer(DexEncodedField valuesField) {
+ private DexEncodedMethod createClassInitializer(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
return new DexEncodedMethod(
dexItemFactory.createClassInitializer(sharedUtilityClassType),
MethodAccessFlags.createForClassInitializer(),
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- createClassInitializerCode(valuesField),
+ createClassInitializerCode(sharedUtilityClassType, valuesField),
DexEncodedMethod.D8_R8_SYNTHESIZED,
CfVersion.V1_6);
}
- private CfCode createClassInitializerCode(DexEncodedField valuesField) {
+ private CfCode createClassInitializerCode(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
int maxValuesArraySize = enumDataMap.getMaxValuesSize();
int numberOfInstructions = 4 + maxValuesArraySize * 4;
List<CfInstruction> instructions = new ArrayList<>(numberOfInstructions);
@@ -217,7 +234,8 @@
Collections.emptyList());
}
- private DexEncodedMethod createValuesMethod(DexEncodedField valuesField) {
+ private DexEncodedMethod createValuesMethod(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
DexEncodedMethod valuesMethod =
new DexEncodedMethod(
dexItemFactory.createMethod(
@@ -228,14 +246,15 @@
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- createValuesMethodCode(valuesField),
+ createValuesMethodCode(sharedUtilityClassType, valuesField),
DexEncodedMethod.D8_R8_SYNTHESIZED,
CfVersion.V1_6);
this.valuesMethod = valuesMethod;
return valuesMethod;
}
- private CfCode createValuesMethodCode(DexEncodedField valuesField) {
+ private CfCode createValuesMethodCode(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
int maxStack = 5;
int maxLocals = 2;
int argumentLocalSlot = 0;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
index dba0cde..35afed1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
@@ -60,6 +60,10 @@
assert existing == null;
}
+ public MethodProcessor getMethodProcessor() {
+ return methodProcessor;
+ }
+
public boolean verifyIRCacheIsEmpty() {
assert cache.isEmpty();
return true;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index f7e235f..f9bdee5 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
@@ -568,15 +570,37 @@
DexString name,
DexProto proto,
SyntheticKind kind,
- DexProgramClass context,
+ ProgramDefinition context,
AppView<?> appView,
Consumer<SyntheticProgramClassBuilder> buildClassCallback,
Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+ return ensureFixedClassMethod(
+ name,
+ proto,
+ kind,
+ context,
+ appView,
+ buildClassCallback,
+ buildMethodCallback,
+ emptyConsumer());
+ }
+
+ public ProgramMethod ensureFixedClassMethod(
+ DexString name,
+ DexProto proto,
+ SyntheticKind kind,
+ ProgramDefinition context,
+ AppView<?> appView,
+ Consumer<SyntheticProgramClassBuilder> buildClassCallback,
+ Consumer<SyntheticMethodBuilder> buildMethodCallback,
+ Consumer<ProgramMethod> newMethodCallback) {
DexProgramClass clazz =
- ensureFixedClass(kind, context, appView, buildClassCallback, ignored -> {});
+ ensureFixedClass(
+ kind, context.getContextClass(), appView, buildClassCallback, emptyConsumer());
DexMethod methodReference = appView.dexItemFactory().createMethod(clazz.getType(), proto, name);
DexEncodedMethod methodDefinition =
- internalEnsureMethod(methodReference, clazz, kind, appView, buildMethodCallback);
+ internalEnsureMethod(
+ methodReference, clazz, kind, appView, buildMethodCallback, newMethodCallback);
return new ProgramMethod(clazz, methodDefinition);
}
@@ -637,16 +661,18 @@
DexMethod methodReference =
appView.dexItemFactory().createMethod(clazz.getType(), methodProto, methodName);
DexEncodedMethod methodDefinition =
- internalEnsureMethod(methodReference, clazz, kind, appView, buildMethodCallback);
+ internalEnsureMethod(
+ methodReference, clazz, kind, appView, buildMethodCallback, emptyConsumer());
return DexClassAndMethod.create(clazz, methodDefinition);
}
- private DexEncodedMethod internalEnsureMethod(
+ private <T extends DexClassAndMethod> DexEncodedMethod internalEnsureMethod(
DexMethod methodReference,
DexClass clazz,
SyntheticKind kind,
AppView<?> appView,
- Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+ Consumer<SyntheticMethodBuilder> buildMethodCallback,
+ Consumer<T> newMethodCallback) {
MethodCollection methodCollection = clazz.getMethodCollection();
DexEncodedMethod methodDefinition = methodCollection.getMethod(methodReference);
if (methodDefinition != null) {
@@ -667,6 +693,7 @@
// and the creation of the method code. The code can then be constructed outside the lock.
methodDefinition = builder.build();
methodCollection.addMethod(methodDefinition);
+ newMethodCallback.accept((T) DexClassAndMethod.create(clazz, methodDefinition));
return methodDefinition;
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index c142281..edee559 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
public class SyntheticNaming {
@@ -23,6 +25,7 @@
*/
public enum SyntheticKind {
// Class synthetics.
+ ENUM_UNBOXING_SHARED_UTILITY_CLASS("$EnumUnboxingSharedUtility", 24, false, true),
RECORD_TAG("", 1, false, true, true),
COMPANION_CLASS("$-CC", 2, false, true),
EMULATED_INTERFACE_CLASS("$-EL", 3, false, true),
@@ -48,6 +51,10 @@
SERVICE_LOADER("ServiceLoad", 18, true),
OUTLINE("Outline", 19, true);
+ static {
+ assert verifyNoOverlappingIds();
+ }
+
public final String descriptor;
public final int id;
public final boolean isSingleSyntheticMethod;
@@ -100,6 +107,16 @@
}
return null;
}
+
+ private static boolean verifyNoOverlappingIds() {
+ Int2ReferenceMap<SyntheticKind> idToKind = new Int2ReferenceOpenHashMap<>();
+ for (SyntheticKind kind : values()) {
+ SyntheticKind kindWithSameId = idToKind.put(kind.id, kind);
+ assert kindWithSameId == null
+ : "Synthetic kind " + idToKind + " has same id as " + kindWithSameId;
+ }
+ return true;
+ }
}
private static final String SYNTHETIC_CLASS_SEPARATOR = "$$";
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
index e7b5dce..e77f42a 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
@@ -9,8 +9,8 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.enumunboxing.examplelib1.JavaLibrary1;
-import com.android.tools.r8.ir.optimize.enums.SharedEnumUnboxingUtilityClass;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
@@ -87,11 +87,9 @@
assertTrue(
codeInspector.allClasses().stream()
.anyMatch(
- c ->
- c.getOriginalName()
- .contains(
- SharedEnumUnboxingUtilityClass
- .ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX)));
+ clazz ->
+ SyntheticItemsTestUtils.isEnumUnboxingSharedUtilityClass(
+ clazz.getOriginalReference())));
}
static class App {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
index 4ef092d..e7651fb 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.enumunboxing.examplelib1.JavaLibrary1;
import com.android.tools.r8.enumunboxing.examplelib2.JavaLibrary2;
-import com.android.tools.r8.ir.optimize.enums.SharedEnumUnboxingUtilityClass;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
@@ -92,11 +92,9 @@
assertTrue(
codeInspector.allClasses().stream()
.anyMatch(
- c ->
- c.getOriginalName()
- .contains(
- SharedEnumUnboxingUtilityClass
- .ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX)));
+ clazz ->
+ SyntheticItemsTestUtils.isEnumUnboxingSharedUtilityClass(
+ clazz.getOriginalReference())));
}
static class App {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index ec665d3..252b11d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -212,6 +213,11 @@
static class PrimaryMethodProcessorMock extends MethodProcessorWithWave {
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ throw new Unreachable();
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index dfcf7e8..1b4ec31 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -65,6 +65,11 @@
originalMethod.getMethodDescriptor());
}
+ public static boolean isEnumUnboxingSharedUtilityClass(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(
+ reference, null, SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS);
+ }
+
public static boolean isExternalSynthetic(ClassReference reference) {
for (SyntheticKind kind : SyntheticKind.values()) {
if (kind == SyntheticKind.RECORD_TAG) {