blob: 80684172bc5497237914e746589a6ba1f007ea5c [file] [log] [blame]
// 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.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
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.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField> {
public static final DexEncodedField[] EMPTY_ARRAY = {};
public final DexField field;
public final FieldAccessFlags accessFlags;
private DexValue staticValue;
private final boolean deprecated;
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
private KotlinFieldLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
DexAnnotationSet annotations,
DexValue staticValue,
boolean deprecated) {
super(annotations);
this.field = field;
this.accessFlags = accessFlags;
this.staticValue = staticValue;
this.deprecated = deprecated;
}
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
DexAnnotationSet annotations,
DexValue staticValue) {
this(field, accessFlags, annotations, staticValue, false);
}
public DexType type() {
return field.type;
}
public boolean isDeprecated() {
return deprecated;
}
public boolean isProgramField(DexDefinitionSupplier definitions) {
if (field.holder.isClassType()) {
DexClass clazz = definitions.definitionFor(field.holder);
return clazz != null && clazz.isProgramClass();
}
return false;
}
public FieldOptimizationInfo getOptimizationInfo() {
return optimizationInfo;
}
public synchronized MutableFieldOptimizationInfo getMutableOptimizationInfo() {
if (optimizationInfo.isDefaultFieldOptimizationInfo()) {
MutableFieldOptimizationInfo mutableOptimizationInfo = new MutableFieldOptimizationInfo();
optimizationInfo = mutableOptimizationInfo;
return mutableOptimizationInfo;
}
assert optimizationInfo.isMutableFieldOptimizationInfo();
return optimizationInfo.asMutableFieldOptimizationInfo();
}
public void setOptimizationInfo(MutableFieldOptimizationInfo info) {
optimizationInfo = info;
}
public KotlinFieldLevelInfo getKotlinMemberInfo() {
return kotlinMemberInfo;
}
@Override
public FieldAccessFlags getAccessFlags() {
return accessFlags;
}
public void setKotlinMemberInfo(KotlinFieldLevelInfo kotlinMemberInfo) {
assert this.kotlinMemberInfo == NO_KOTLIN_INFO;
this.kotlinMemberInfo = kotlinMemberInfo;
}
@Override
void collectMixedSectionItems(MixedSectionCollection mixedItems) {
annotations().collectMixedSectionItems(mixedItems);
}
@Override
public String toString() {
return "Encoded field " + field;
}
@Override
public String toSmaliString() {
return field.toSmaliString();
}
@Override
public String toSourceString() {
return field.toSourceString();
}
@Override
public DexField toReference() {
return field;
}
@Override
public boolean isDexEncodedField() {
return true;
}
@Override
public DexEncodedField asDexEncodedField() {
return this;
}
@Override
public ProgramField asProgramMember(DexDefinitionSupplier definitions) {
return asProgramField(definitions);
}
public ProgramField asProgramField(DexDefinitionSupplier definitions) {
assert holder().isClassType();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(field));
if (clazz != null) {
return new ProgramField(clazz, this);
}
return null;
}
public boolean isEnum() {
return accessFlags.isEnum();
}
public boolean isFinal() {
return accessFlags.isFinal();
}
@Override
public boolean isStatic() {
return accessFlags.isStatic();
}
public boolean isPackagePrivate() {
return accessFlags.isPackagePrivate();
}
public boolean isPrivate() {
return accessFlags.isPrivate();
}
public boolean isProtected() {
return accessFlags.isProtected();
}
public boolean isPublic() {
return accessFlags.isPublic();
}
@Override
public boolean isStaticMember() {
return isStatic();
}
public boolean isVolatile() {
return accessFlags.isVolatile();
}
public boolean hasAnnotation() {
return !annotations().isEmpty();
}
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;
}
public DexValue getStaticValue() {
assert accessFlags.isStatic();
return staticValue == null ? DexValue.defaultForType(field.type) : staticValue;
}
/**
* Returns a const instructions if this field is a compile time final const.
*
* <p>NOTE: It is the responsibility of the caller to check if this field is pinned or not.
*/
public Instruction valueAsConstInstruction(
IRCode code, DebugLocalInfo local, AppView<AppInfoWithLiveness> appView) {
boolean isWritten = appView.appInfo().isFieldWrittenByFieldPutInstruction(this);
if (!isWritten) {
// Since the field is not written, we can simply return the default value for the type.
DexValue value = isStatic() ? getStaticValue() : DexValue.defaultForType(field.type);
return value.asConstInstruction(appView, code, local);
}
// Check if we have a single value for the field according to the field optimization info.
AbstractValue abstractValue = getOptimizationInfo().getAbstractValue();
if (abstractValue.isSingleValue()) {
SingleValue singleValue = abstractValue.asSingleValue();
if (singleValue.isSingleFieldValue()
&& singleValue.asSingleFieldValue().getField() == field) {
return null;
}
if (singleValue.isMaterializableInContext(appView, code.context())) {
TypeElement type = TypeElement.fromDexType(field.type, maybeNull(), appView);
return singleValue.createMaterializingInstruction(
appView, code, TypeAndLocalInfoSupplier.create(type, local));
}
}
// The only way to figure out whether the static value contains the final value is ensure the
// value is not the default or check that <clinit> is not present.
if (accessFlags.isFinal() && isStatic()) {
DexClass clazz = appView.definitionFor(field.holder);
if (clazz == null || clazz.hasClassInitializer()) {
return null;
}
DexValue staticValue = getStaticValue();
if (!staticValue.isDefault(field.type)) {
return staticValue.asConstInstruction(appView, code, local);
}
}
return null;
}
public boolean mayTriggerClassInitializationSideEffects(
AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
// Only static field matters when it comes to class initialization side effects.
if (!isStatic()) {
return false;
}
DexClass clazz = appView.definitionFor(field.holder);
if (clazz == null) {
return true;
}
if (clazz.classInitializationMayHaveSideEffects(
appView,
// Types that are a super type of the current context are guaranteed to be initialized
// already.
type -> appView.appInfo().isSubtype(context.getHolderType(), type),
Sets.newIdentityHashSet())) {
// Ignore class initialization side-effects for dead proto extension fields to ensure that
// we force replace these field reads by null.
boolean ignore =
appView.withGeneratedExtensionRegistryShrinker(
shrinker -> shrinker.isDeadProtoExtensionField(field), false);
return !ignore;
}
return false;
}
public DexEncodedField toTypeSubstitutedField(DexField field) {
if (this.field == field) {
return this;
}
DexEncodedField result = new DexEncodedField(field, accessFlags, annotations(), staticValue);
result.optimizationInfo =
optimizationInfo.isMutableFieldOptimizationInfo()
? optimizationInfo.asMutableFieldOptimizationInfo().mutableCopy()
: DefaultFieldOptimizationInfo.getInstance();
return result;
}
public boolean validateDexValue(DexItemFactory factory) {
if (!accessFlags.isStatic() || staticValue == null) {
return true;
}
if (field.type.isPrimitiveType()) {
assert staticValue.getType(factory) == field.type
: "Static " + field + " has invalid static value " + staticValue + ".";
}
if (staticValue.isDexValueNull()) {
assert field.type.isReferenceType() : "Static " + field + " has invalid null static value.";
}
// TODO(b/150593449): Support non primitive DexValue (String, enum) and add assertions.
return true;
}
}