blob: 6c773a620060fbb9b1d08e0cc58b40172a29efd5 [file] [log] [blame]
// Copyright (c) 2017, 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.code;
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.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
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.SingleFieldValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collections;
import java.util.List;
public abstract class FieldInstruction extends Instruction {
private final DexField field;
protected FieldInstruction(DexField field, Value dest, Value value) {
this(field, dest, Collections.singletonList(value));
}
protected FieldInstruction(DexField field, Value dest, List<Value> inValues) {
super(dest, inValues);
assert field != null;
this.field = field;
}
public abstract Value value();
public FieldMemberType getType() {
return FieldMemberType.fromDexType(field.type);
}
public DexField getField() {
return field;
}
@Override
public boolean isFieldInstruction() {
return true;
}
@Override
public FieldInstruction asFieldInstruction() {
return this;
}
@Override
public boolean instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
return instructionInstanceCanThrow(appView, context, SideEffectAssumption.NONE);
}
public boolean instructionInstanceCanThrow(
AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
return internalInstructionInstanceCanThrow(
appView,
context,
assumption,
appView.appInfo().resolveField(field, context).asSuccessfulResolution());
}
boolean internalInstructionInstanceCanThrow(
AppView<?> appView,
ProgramMethod context,
SideEffectAssumption assumption,
SuccessfulFieldResolutionResult resolutionResult) {
if (resolutionResult == null) {
return true;
}
DexEncodedField resolvedField = resolutionResult.getResolvedField();
// Check if the instruction may fail with an IncompatibleClassChangeError.
if (resolvedField.isStatic() != isStaticFieldInstruction()) {
return true;
}
// Check if the resolution target is accessible.
if (resolutionResult.getResolvedHolder() != context.getHolder()) {
if (resolutionResult
.isAccessibleFrom(context, appView.appInfo().withClassHierarchy())
.isPossiblyFalse()) {
return true;
}
}
// TODO(b/137168535): Without non-null tracking, only locally created receiver is allowed in D8.
// Check if the instruction may fail with a NullPointerException (null receiver).
if (isInstanceGet() || isInstancePut()) {
if (!assumption.canAssumeReceiverIsNotNull()) {
Value receiver = inValues.get(0);
if (receiver.isAlwaysNull(appView) || receiver.type.isNullable()) {
return true;
}
}
}
// For D8, reaching here means the field is in the same context, hence the class is guaranteed
// to be initialized already.
if (!appView.enableWholeProgramOptimizations()) {
return false;
}
boolean mayTriggerClassInitialization =
isStaticFieldInstruction() && !assumption.canAssumeClassIsAlreadyInitialized();
if (mayTriggerClassInitialization) {
// Only check for <clinit> side effects if there is no -assumenosideeffects rule.
if (appView.appInfo().hasLiveness()) {
AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
if (appInfoWithLiveness.noSideEffects.containsKey(resolvedField.getReference())) {
return false;
}
}
// May trigger <clinit> that may have side effects.
if (field.holder.classInitializationMayHaveSideEffectsInContext(appView, context)) {
return true;
}
}
return false;
}
@Override
public boolean hasInvariantOutType() {
// TODO(jsjeon): what if the target field is known to be non-null?
return true;
}
@Override
public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
if (instructionMayTriggerMethodInvocation(appView, context)) {
// This may trigger class initialization, which could potentially read any field.
return UnknownFieldSet.getInstance();
}
if (isFieldGet()) {
DexField field = getField();
DexEncodedField encodedField = null;
if (appView.enableWholeProgramOptimizations()) {
encodedField = appView.appInfo().resolveField(field).getResolvedField();
} else {
DexClass clazz = appView.definitionFor(field.holder);
if (clazz != null) {
encodedField = clazz.lookupField(field);
}
}
if (encodedField != null) {
return new ConcreteMutableFieldSet(encodedField);
}
return UnknownFieldSet.getInstance();
}
assert isFieldPut();
return EmptyFieldSet.getInstance();
}
/**
* Returns {@code true} if this instruction may store an instance of a class that has a non-
* default finalize() method in a field. In that case, it is not safe to remove this instruction,
* since that could change the lifetime of the value.
*/
boolean isStoringObjectWithFinalizer(
AppView<AppInfoWithLiveness> appView, DexEncodedField field) {
assert isFieldPut();
TypeElement type = value().getType();
TypeElement baseType = type.isArrayType() ? type.asArrayType().getBaseType() : type;
if (!baseType.isClassType()) {
return false;
}
AbstractValue abstractValue = field.getOptimizationInfo().getAbstractValue();
if (abstractValue.isSingleValue()) {
if (abstractValue.isSingleConstValue()) {
return false;
}
if (abstractValue.isSingleFieldValue()) {
SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
return singleFieldValue.mayHaveFinalizeMethodDirectlyOrIndirectly(appView);
}
}
AppInfoWithLiveness appInfo = appView.appInfo();
Value root = value().getAliasedValue();
if (!root.isPhi() && root.definition.isNewInstance()) {
DexClass clazz = appView.definitionFor(root.definition.asNewInstance().clazz);
if (clazz == null) {
return true;
}
if (clazz.superType == null) {
return false;
}
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexEncodedMethod resolutionResult =
appInfo
.resolveMethodOnClass(dexItemFactory.objectMembers.finalize, clazz)
.getSingleTarget();
return resolutionResult != null && resolutionResult.isProgramMethod(appView);
}
return appInfo.mayHaveFinalizeMethodDirectlyOrIndirectly(baseType.asClassType());
}
@Override
public AbstractValue getAbstractValue(
AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
assert isFieldGet();
DexEncodedField field = appView.appInfo().resolveField(getField()).getResolvedField();
if (field != null) {
return field.getOptimizationInfo().getAbstractValue();
}
return UnknownValue.getInstance();
}
}