|  | // Copyright (c) 2016, 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.graph; | 
|  |  | 
|  | import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; | 
|  | import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo; | 
|  |  | 
|  | import com.android.tools.r8.androidapi.ComputedApiLevel; | 
|  | import com.android.tools.r8.dex.MixedSectionCollection; | 
|  | import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; | 
|  | import com.android.tools.r8.ir.analysis.type.DynamicType; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeElement; | 
|  | import com.android.tools.r8.ir.optimize.info.DefaultFieldOptimizationInfo; | 
|  | import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo; | 
|  | import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo; | 
|  | import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; | 
|  | import com.android.tools.r8.kotlin.KotlinFieldLevelInfo; | 
|  | import com.android.tools.r8.kotlin.KotlinMetadataUtils; | 
|  | import com.android.tools.r8.utils.ConsumerUtils; | 
|  | import com.android.tools.r8.utils.structural.StructuralItem; | 
|  | import com.android.tools.r8.utils.structural.StructuralMapping; | 
|  | import com.android.tools.r8.utils.structural.StructuralSpecification; | 
|  | import java.util.function.Consumer; | 
|  | import java.util.function.Function; | 
|  |  | 
|  | public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField> | 
|  | implements StructuralItem<DexEncodedField> { | 
|  |  | 
|  | public static final DexEncodedField[] EMPTY_ARRAY = {}; | 
|  |  | 
|  | public final FieldAccessFlags accessFlags; | 
|  | private DexValue staticValue; | 
|  | private final boolean deprecated; | 
|  | /** Generic signature information if the attribute is present in the input */ | 
|  | private FieldTypeSignature genericSignature; | 
|  |  | 
|  | private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance(); | 
|  | private KotlinFieldLevelInfo kotlinMemberInfo = getNoKotlinInfo(); | 
|  |  | 
|  | // Mark indicating if this field has been identified as potentially inlined by javac. | 
|  | // This is to ensure consistent tracing in the second round of tree shaking. Remove this field | 
|  | // once conditional rules are represented by rule-instances rather than reevaluating rule-schemas. | 
|  | private boolean isInlinableByJavaC = false; | 
|  |  | 
|  | private static void specify(StructuralSpecification<DexEncodedField, ?> spec) { | 
|  | spec.withItem(DexEncodedField::getReference) | 
|  | .withItem(DexEncodedField::getAccessFlags) | 
|  | .withNullableItem(f -> f.staticValue) | 
|  | .withBool(DexEncodedField::isDeprecated) | 
|  | // TODO(b/171867022): The generic signature should be part of the definition. | 
|  | .withAssert(f -> f.genericSignature.hasNoSignature()); | 
|  | // TODO(b/171867022): Should the optimization info and member info be part of the definition? | 
|  | } | 
|  |  | 
|  | private DexEncodedField( | 
|  | DexField field, | 
|  | FieldAccessFlags accessFlags, | 
|  | FieldTypeSignature genericSignature, | 
|  | DexAnnotationSet annotations, | 
|  | DexValue staticValue, | 
|  | ComputedApiLevel apiLevel, | 
|  | boolean deprecated, | 
|  | boolean d8R8Synthesized) { | 
|  | super(field, annotations, d8R8Synthesized, apiLevel); | 
|  | this.accessFlags = accessFlags; | 
|  | this.staticValue = staticValue; | 
|  | this.deprecated = deprecated; | 
|  | this.genericSignature = genericSignature; | 
|  | assert genericSignature != null; | 
|  | assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(genericSignature, annotations); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public StructuralMapping<DexEncodedField> getStructuralMapping() { | 
|  | return DexEncodedField::specify; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedField self() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public DexType type() { | 
|  | return getReference().type; | 
|  | } | 
|  |  | 
|  | public boolean isDeprecated() { | 
|  | return deprecated; | 
|  | } | 
|  |  | 
|  | public boolean isProgramField(DexDefinitionSupplier definitions) { | 
|  | if (getReference().holder.isClassType()) { | 
|  | DexClass clazz = definitions.definitionFor(getReference().holder); | 
|  | return clazz != null && clazz.isProgramClass(); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public FieldOptimizationInfo getOptimizationInfo() { | 
|  | return optimizationInfo; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ComputedApiLevel getApiLevel() { | 
|  | return getApiLevelForDefinition(); | 
|  | } | 
|  |  | 
|  | public synchronized MutableFieldOptimizationInfo getMutableOptimizationInfo() { | 
|  | MutableFieldOptimizationInfo mutableInfo = optimizationInfo.toMutableOptimizationInfo(); | 
|  | optimizationInfo = mutableInfo; | 
|  | return mutableInfo; | 
|  | } | 
|  |  | 
|  | public void setOptimizationInfo(MutableFieldOptimizationInfo info) { | 
|  | optimizationInfo = info; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public KotlinFieldLevelInfo getKotlinInfo() { | 
|  | return kotlinMemberInfo; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void clearKotlinInfo() { | 
|  | kotlinMemberInfo = getNoKotlinInfo(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public FieldAccessFlags getAccessFlags() { | 
|  | return accessFlags; | 
|  | } | 
|  |  | 
|  | public void setKotlinMemberInfo(KotlinFieldLevelInfo kotlinMemberInfo) { | 
|  | assert this.kotlinMemberInfo == getNoKotlinInfo(); | 
|  | this.kotlinMemberInfo = kotlinMemberInfo; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected void collectMixedSectionItems(MixedSectionCollection mixedItems) { | 
|  | annotations().collectMixedSectionItems(mixedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Encoded field " + getReference(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toSmaliString() { | 
|  | return getReference().toSmaliString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toSourceString() { | 
|  | return getReference().toSourceString(); | 
|  | } | 
|  |  | 
|  | public DexType getType() { | 
|  | return getReference().getType(); | 
|  | } | 
|  |  | 
|  | public TypeElement getTypeElement(AppView<?> appView) { | 
|  | return getReference().getTypeElement(appView); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexEncodedField() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedField asDexEncodedField() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ProgramField asProgramMember(DexDefinitionSupplier definitions) { | 
|  | return asProgramField(definitions); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public <T> T apply( | 
|  | Function<DexEncodedField, T> fieldConsumer, Function<DexEncodedMethod, T> methodConsumer) { | 
|  | return fieldConsumer.apply(this); | 
|  | } | 
|  |  | 
|  | public DexClassAndField asClassField(DexDefinitionSupplier definitions) { | 
|  | assert getHolderType().isClassType(); | 
|  | DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference())); | 
|  | if (clazz != null) { | 
|  | return DexClassAndField.create(clazz, this); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public ProgramField asProgramField(DexDefinitionSupplier definitions) { | 
|  | assert getHolderType().isClassType(); | 
|  | DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference())); | 
|  | if (clazz != null) { | 
|  | return new ProgramField(clazz, this); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isEnum() { | 
|  | return accessFlags.isEnum(); | 
|  | } | 
|  |  | 
|  | public boolean isFinal() { | 
|  | return accessFlags.isFinal(); | 
|  | } | 
|  |  | 
|  | public boolean isInstance() { | 
|  | return !isStatic(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isStatic() { | 
|  | return accessFlags.isStatic(); | 
|  | } | 
|  |  | 
|  | public boolean isPackagePrivate() { | 
|  | return accessFlags.isPackagePrivate(); | 
|  | } | 
|  |  | 
|  | public boolean isProtected() { | 
|  | return accessFlags.isProtected(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isStaticMember() { | 
|  | return isStatic(); | 
|  | } | 
|  |  | 
|  | public boolean isSynthetic() { | 
|  | return accessFlags.isSynthetic(); | 
|  | } | 
|  |  | 
|  | public boolean isVolatile() { | 
|  | return accessFlags.isVolatile(); | 
|  | } | 
|  |  | 
|  | public boolean hasExplicitStaticValue() { | 
|  | assert accessFlags.isStatic(); | 
|  | return staticValue != null; | 
|  | } | 
|  |  | 
|  | public void setStaticValue(DexValue staticValue) { | 
|  | assert accessFlags.isStatic(); | 
|  | assert staticValue != null; | 
|  | this.staticValue = staticValue; | 
|  | } | 
|  |  | 
|  | public void clearStaticValue() { | 
|  | assert accessFlags.isStatic(); | 
|  | this.staticValue = null; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public DexValue getStaticValue() { | 
|  | assert accessFlags.isStatic(); | 
|  | return staticValue == null ? DexValue.defaultForType(getReference().type) : staticValue; | 
|  | } | 
|  |  | 
|  | public DexEncodedField toTypeSubstitutedField(AppView<?> appView, DexField field) { | 
|  | return toTypeSubstitutedField(appView, field, ConsumerUtils.emptyConsumer()); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public DexEncodedField toTypeSubstitutedField( | 
|  | AppView<?> appView, DexField field, Consumer<Builder> consumer) { | 
|  | if (this.getReference() == field) { | 
|  | return this; | 
|  | } | 
|  | return builder(this) | 
|  | .setField(field) | 
|  | .disableAndroidApiLevelCheckIf( | 
|  | !appView.options().apiModelingOptions().enableApiCallerIdentification | 
|  | || !appView.enableWholeProgramOptimizations()) | 
|  | .apply(consumer) | 
|  | .build(); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public boolean validateDexValue(DexItemFactory factory) { | 
|  | if (!accessFlags.isStatic() || staticValue == null) { | 
|  | return true; | 
|  | } | 
|  | if (getReference().type.isPrimitiveType()) { | 
|  | assert staticValue.getType(factory) == getReference().type | 
|  | : "Static " + getReference() + " has invalid static value " + staticValue + "."; | 
|  | } | 
|  | if (staticValue.isDexValueNull()) { | 
|  | assert getReference().type.isReferenceType() | 
|  | : "Static " + getReference() + " has invalid null static value."; | 
|  | } | 
|  | // TODO(b/150593449): Support non primitive DexValue (String, enum) and add assertions. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public FieldTypeSignature getGenericSignature() { | 
|  | return genericSignature; | 
|  | } | 
|  |  | 
|  | public void setGenericSignature(FieldTypeSignature genericSignature) { | 
|  | assert genericSignature != null; | 
|  | this.genericSignature = genericSignature; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void clearGenericSignature() { | 
|  | this.genericSignature = FieldTypeSignature.noSignature(); | 
|  | } | 
|  |  | 
|  | public static Builder builder() { | 
|  | return new Builder(false); | 
|  | } | 
|  |  | 
|  | public static Builder builder(DexEncodedField from) { | 
|  | return new Builder(from.isD8R8Synthesized(), from); | 
|  | } | 
|  |  | 
|  | public static Builder syntheticBuilder() { | 
|  | return new Builder(true); | 
|  | } | 
|  |  | 
|  | public void markAsInlinableByJavaC() { | 
|  | isInlinableByJavaC = true; | 
|  | } | 
|  |  | 
|  | public boolean getIsInlinableByJavaC() { | 
|  | return isInlinableByJavaC; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public boolean getOrComputeIsInlinableByJavaC(DexItemFactory dexItemFactory) { | 
|  | if (getIsInlinableByJavaC()) { | 
|  | return true; | 
|  | } | 
|  | if (!isStatic() || !isFinal()) { | 
|  | return false; | 
|  | } | 
|  | if (!hasExplicitStaticValue()) { | 
|  | return false; | 
|  | } | 
|  | if (getType().isPrimitiveType()) { | 
|  | return true; | 
|  | } | 
|  | if (getType() != dexItemFactory.stringType) { | 
|  | return false; | 
|  | } | 
|  | if (!getStaticValue().isDexValueString()) { | 
|  | return false; | 
|  | } | 
|  | markAsInlinableByJavaC(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public static class Builder { | 
|  |  | 
|  | private DexField field; | 
|  | private DexAnnotationSet annotations = DexAnnotationSet.empty(); | 
|  | private FieldAccessFlags accessFlags; | 
|  | private FieldTypeSignature genericSignature = FieldTypeSignature.noSignature(); | 
|  | private KotlinFieldLevelInfo kotlinInfo = KotlinMetadataUtils.getNoKotlinInfo(); | 
|  | private DexValue staticValue = null; | 
|  | private ComputedApiLevel apiLevel = ComputedApiLevel.notSet(); | 
|  | private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance(); | 
|  | private boolean deprecated; | 
|  | private final boolean d8R8Synthesized; | 
|  | private Consumer<DexEncodedField> buildConsumer = ConsumerUtils.emptyConsumer(); | 
|  |  | 
|  | // Checks to impose on the built method. They should always be active to start with and be | 
|  | // lowered on the use site. | 
|  | private boolean checkAndroidApiLevel = true; | 
|  |  | 
|  | private Builder(boolean d8R8Synthesized) { | 
|  | this.d8R8Synthesized = d8R8Synthesized; | 
|  | } | 
|  |  | 
|  | private Builder(boolean d8R8Synthesized, DexEncodedField from) { | 
|  | // Copy all the mutable state of a DexEncodedField here. | 
|  | field = from.getReference(); | 
|  | accessFlags = from.accessFlags.copy(); | 
|  | genericSignature = from.getGenericSignature(); | 
|  | kotlinInfo = from.getKotlinInfo(); | 
|  | annotations = from.annotations(); | 
|  | staticValue = from.staticValue; | 
|  | apiLevel = from.getApiLevel(); | 
|  | optimizationInfo = | 
|  | from.optimizationInfo.isMutableOptimizationInfo() | 
|  | ? from.optimizationInfo.asMutableFieldOptimizationInfo().mutableCopy() | 
|  | : from.optimizationInfo; | 
|  | deprecated = from.isDeprecated(); | 
|  | this.d8R8Synthesized = d8R8Synthesized; | 
|  | } | 
|  |  | 
|  | public Builder apply(Consumer<Builder> consumer) { | 
|  | consumer.accept(this); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder modifyAccessFlags(Consumer<FieldAccessFlags> consumer) { | 
|  | consumer.accept(accessFlags); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder clearDynamicType() { | 
|  | return addBuildConsumer( | 
|  | fixedUpField -> | 
|  | OptimizationFeedbackSimple.getInstance() | 
|  | .markFieldHasDynamicType(fixedUpField, DynamicType.unknown())); | 
|  | } | 
|  |  | 
|  | public Builder clearAnnotations() { | 
|  | return setAnnotations(DexAnnotationSet.empty()); | 
|  | } | 
|  |  | 
|  | public Builder setAnnotations(DexAnnotationSet annotations) { | 
|  | this.annotations = annotations; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | private Builder addBuildConsumer(Consumer<DexEncodedField> consumer) { | 
|  | this.buildConsumer = this.buildConsumer.andThen(consumer); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder setField(DexField field) { | 
|  | this.field = field; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder setAccessFlags(FieldAccessFlags accessFlags) { | 
|  | this.accessFlags = accessFlags; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder setApiLevel(ComputedApiLevel apiLevel) { | 
|  | this.apiLevel = apiLevel; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder setGenericSignature(FieldTypeSignature genericSignature) { | 
|  | this.genericSignature = genericSignature; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder setStaticValue(DexValue staticValue) { | 
|  | this.staticValue = staticValue; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder setDeprecated(boolean deprecated) { | 
|  | this.deprecated = deprecated; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder disableAndroidApiLevelCheck() { | 
|  | return disableAndroidApiLevelCheckIf(true); | 
|  | } | 
|  |  | 
|  | public Builder disableAndroidApiLevelCheckIf(boolean shouldDisable) { | 
|  | if (shouldDisable) { | 
|  | checkAndroidApiLevel = false; | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public DexEncodedField build() { | 
|  | assert field != null; | 
|  | assert accessFlags != null; | 
|  | assert genericSignature != null; | 
|  | assert annotations != null; | 
|  | assert !checkAndroidApiLevel || !apiLevel.isNotSetApiLevel(); | 
|  | DexEncodedField dexEncodedField = | 
|  | new DexEncodedField( | 
|  | field, | 
|  | accessFlags, | 
|  | genericSignature, | 
|  | annotations, | 
|  | staticValue, | 
|  | apiLevel, | 
|  | deprecated, | 
|  | d8R8Synthesized); | 
|  | dexEncodedField.setKotlinMemberInfo(kotlinInfo); | 
|  | dexEncodedField.optimizationInfo = optimizationInfo; | 
|  | buildConsumer.accept(dexEncodedField); | 
|  | return dexEncodedField; | 
|  | } | 
|  | } | 
|  | } |