blob: dda477974a9da0faba397781f05cf1f4d04510fa [file] [log] [blame]
// 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 static com.android.tools.r8.ir.optimize.enums.EnumUnboxingRewriter.createValuesField;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
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.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.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
public class EnumUnboxingUtilityClasses {
public static final String ENUM_UNBOXING_LOCAL_UTILITY_CLASS_SUFFIX =
"$r8$EnumUnboxingLocalUtility";
public static final String ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX =
"$r8$EnumUnboxingSharedUtility";
// Synthetic classes for utilities specific to the unboxing of a single enum.
private final ImmutableMap<DexType, DexProgramClass> localEnumUnboxingUtilityClasses;
// Default enum unboxing utility synthetic class used to hold all the shared unboxed enum
// methods (ordinal(I), equals(II), etc.).
private final DexProgramClass sharedEnumUnboxingUtilityClass;
private EnumUnboxingUtilityClasses(
DexProgramClass sharedEnumUnboxingUtilityClass,
ImmutableMap<DexType, DexProgramClass> localEnumUnboxingUtilityClasses) {
this.sharedEnumUnboxingUtilityClass = sharedEnumUnboxingUtilityClass;
this.localEnumUnboxingUtilityClasses = localEnumUnboxingUtilityClasses;
}
public DexType getLocalEnumUnboxingUtilityClass(DexProgramClass enumClass) {
return getLocalEnumUnboxingUtilityClass(enumClass.getType());
}
public DexType getLocalEnumUnboxingUtilityClass(DexType enumType) {
DexProgramClass localEnumUnboxingUtilityClass = localEnumUnboxingUtilityClasses.get(enumType);
assert localEnumUnboxingUtilityClass != null;
return localEnumUnboxingUtilityClass.getType();
}
public DexProgramClass getSharedEnumUnboxingUtilityClass() {
return sharedEnumUnboxingUtilityClass;
}
public static Builder builder(AppView<AppInfoWithLiveness> appView) {
return new Builder(appView);
}
public static class Builder {
private final AppView<?> appView;
private final Map<DexType, DexProgramClass> localEnumUnboxingUtilityClasses =
new IdentityHashMap<>();
private DexProgramClass sharedEnumUnboxingUtilityClass;
public Builder(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
public Builder synthesizeEnumUnboxingUtilityClasses(
Set<DexProgramClass> enumsToUnbox,
DirectMappedDexApplication.Builder appBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder) {
synthesizeLocalUtilityClasses(
enumsToUnbox, appBuilder, fieldAccessInfoCollectionModifierBuilder);
synthesizeSharedUtilityClass(enumsToUnbox, appBuilder);
return this;
}
public EnumUnboxingUtilityClasses build() {
return new EnumUnboxingUtilityClasses(
sharedEnumUnboxingUtilityClass, ImmutableMap.copyOf(localEnumUnboxingUtilityClasses));
}
private void synthesizeLocalUtilityClasses(
Set<DexProgramClass> enumsToUnbox,
DirectMappedDexApplication.Builder appBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder) {
for (DexProgramClass enumToUnbox : enumsToUnbox) {
synthesizeLocalUtilityClass(
enumToUnbox, appBuilder, fieldAccessInfoCollectionModifierBuilder);
}
}
private void synthesizeLocalUtilityClass(
DexProgramClass enumToUnbox,
DirectMappedDexApplication.Builder appBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder) {
DexType localUtilityClassType = getLocalUtilityClassType(enumToUnbox);
assert appView.appInfo().definitionForWithoutExistenceAssert(localUtilityClassType) == null;
// Required fields.
DexField reference =
createValuesField(enumToUnbox.getType(), localUtilityClassType, appView.dexItemFactory());
DexEncodedField staticField =
new DexEncodedField(reference, FieldAccessFlags.createPublicStaticSynthetic());
fieldAccessInfoCollectionModifierBuilder
.recordFieldReadInUnknownContext(reference)
.recordFieldWriteInUnknownContext(reference);
DexProgramClass localUtilityClass =
new DexProgramClass(
localUtilityClassType,
null,
new SynthesizedOrigin("enum unboxing", EnumUnboxer.class),
ClassAccessFlags.createPublicFinalSynthetic(),
appView.dexItemFactory().objectType,
DexTypeList.empty(),
null,
null,
Collections.emptyList(),
null,
Collections.emptyList(),
ClassSignature.noSignature(),
DexAnnotationSet.empty(),
new DexEncodedField[] {staticField},
DexEncodedField.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
appView.dexItemFactory().getSkipNameValidationForTesting(),
DexProgramClass::checksumFromType);
appBuilder.addSynthesizedClass(localUtilityClass);
appView.appInfo().addSynthesizedClass(localUtilityClass, enumToUnbox);
localEnumUnboxingUtilityClasses.put(enumToUnbox.getType(), localUtilityClass);
}
private void synthesizeSharedUtilityClass(
Set<DexProgramClass> enumsToUnbox, DirectMappedDexApplication.Builder appBuilder) {
DexType type = getSharedUtilityClassType(findDeterministicContextType(enumsToUnbox));
assert appView.appInfo().definitionForWithoutExistenceAssert(type) == null;
DexProgramClass syntheticClass =
new DexProgramClass(
type,
null,
new SynthesizedOrigin("enum unboxing", EnumUnboxer.class),
ClassAccessFlags.createPublicFinalSynthetic(),
appView.dexItemFactory().objectType,
DexTypeList.empty(),
null,
null,
Collections.emptyList(),
null,
Collections.emptyList(),
ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
appView.dexItemFactory().getSkipNameValidationForTesting(),
DexProgramClass::checksumFromType);
appBuilder.addSynthesizedClass(syntheticClass);
appView.appInfo().addSynthesizedClassToBase(syntheticClass, enumsToUnbox);
sharedEnumUnboxingUtilityClass = syntheticClass;
}
private DexProgramClass findDeterministicContextType(Set<DexProgramClass> contexts) {
DexProgramClass deterministicContext = null;
for (DexProgramClass context : contexts) {
if (deterministicContext == null) {
deterministicContext = context;
} else if (context.type.compareTo(deterministicContext.type) < 0) {
deterministicContext = context;
}
}
return deterministicContext;
}
private DexType getLocalUtilityClassType(DexProgramClass context) {
return getUtilityClassType(context, ENUM_UNBOXING_LOCAL_UTILITY_CLASS_SUFFIX);
}
private DexType getSharedUtilityClassType(DexProgramClass context) {
return getUtilityClassType(context, ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX);
}
private DexType getUtilityClassType(DexProgramClass context, String suffix) {
return appView
.dexItemFactory()
.createType(
DescriptorUtils.getDescriptorFromClassBinaryName(
DescriptorUtils.getBinaryNameFromDescriptor(
context.getType().toDescriptorString())
+ suffix));
}
}
}