blob: c006a417b8df1e4b308da3853bc4bcddbea70ab2 [file] [log] [blame]
// Copyright (c) 2022, 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.horizontalclassmerging;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleConstValue;
import com.android.tools.r8.ir.analysis.value.SingleDexItemBasedStringValue;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirEncodingStrategy;
import com.android.tools.r8.lightir.LirStrategy;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ListUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
/**
* Similar to CfCode, but with a marker that makes it possible to recognize this is synthesized by
* the horizontal class merger.
*/
public class IncompleteMergedInstanceInitializerCode extends IncompleteHorizontalClassMergerCode {
private final DexField classIdField;
private int numberOfUnusedArguments;
private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre;
private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost;
private final DexMethod parentConstructor;
private final List<InstanceFieldInitializationInfo> parentConstructorArguments;
IncompleteMergedInstanceInitializerCode(
DexField classIdField,
int numberOfUnusedArguments,
Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre,
Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost,
DexMethod parentConstructor,
List<InstanceFieldInitializationInfo> parentConstructorArguments) {
this.classIdField = classIdField;
this.numberOfUnusedArguments = numberOfUnusedArguments;
this.instanceFieldAssignmentsPre = instanceFieldAssignmentsPre;
this.instanceFieldAssignmentsPost = instanceFieldAssignmentsPost;
this.parentConstructor = parentConstructor;
this.parentConstructorArguments = parentConstructorArguments;
}
@Override
public void addExtraUnusedArguments(int numberOfUnusedArguments) {
this.numberOfUnusedArguments += numberOfUnusedArguments;
}
@Override
public LirCode<Integer> toLirCode(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod method,
HorizontalClassMergerGraphLens lens) {
LirEncodingStrategy<Value, Integer> strategy =
LirStrategy.getDefaultStrategy().getEncodingStrategy();
LirBuilder<Value, Integer> lirBuilder =
LirCode.builder(
method.getReference(),
method.getDefinition().isD8R8Synthesized(),
strategy,
appView.options());
int instructionIndex = 0;
List<Value> argumentValues = new ArrayList<>();
// Add receiver argument.
DexType receiverType = method.getHolderType();
TypeElement receiverTypeElement = receiverType.toTypeElement(appView, definitelyNotNull());
Value receiverValue = Value.createNoDebugLocal(instructionIndex, receiverTypeElement);
argumentValues.add(receiverValue);
strategy.defineValue(receiverValue, receiverValue.getNumber());
lirBuilder.addArgument(receiverValue.getNumber(), false);
instructionIndex++;
// Add non-receiver arguments.
for (; instructionIndex < method.getDefinition().getNumberOfArguments(); instructionIndex++) {
DexType argumentType = method.getArgumentType(instructionIndex);
TypeElement argumentTypeElement = argumentType.toTypeElement(appView);
Value argumentValue = Value.createNoDebugLocal(instructionIndex, argumentTypeElement);
argumentValues.add(argumentValue);
strategy.defineValue(argumentValue, argumentValue.getNumber());
lirBuilder.addArgument(argumentValue.getNumber(), argumentType.isBooleanType());
}
// Assign class id.
if (classIdField != null) {
Value classIdValue = argumentValues.get(argumentValues.size() - 1 - numberOfUnusedArguments);
lirBuilder.addInstancePut(
lens.getNextFieldSignature(classIdField), receiverValue, classIdValue);
instructionIndex++;
}
// Assign each field.
instructionIndex =
addLirInstructionsForInstanceFieldAssignments(
appView,
method,
lirBuilder,
strategy,
argumentValues,
instructionIndex,
instanceFieldAssignmentsPre,
lens);
// Load constructor arguments.
MethodLookupResult parentConstructorLookup =
lens.lookupInvokeDirect(parentConstructor, method, appView.codeLens());
List<Value> parentConstructorArgumentValues = new ArrayList<>();
parentConstructorArgumentValues.add(receiverValue);
int parentConstructorArgumentIndex = 0;
for (InstanceFieldInitializationInfo initializationInfo : parentConstructorArguments) {
instructionIndex =
addLirInstructionsForInitializationInfo(
appView,
lirBuilder,
strategy,
initializationInfo,
argumentValues,
instructionIndex,
parentConstructorLookup.getReference().getParameter(parentConstructorArgumentIndex),
parentConstructorArgumentValues::add);
parentConstructorArgumentIndex++;
}
for (ExtraParameter extraParameter :
parentConstructorLookup.getPrototypeChanges().getExtraParameters()) {
instructionIndex =
addLirInstructionsForInitializationInfo(
appView,
lirBuilder,
strategy,
extraParameter.getValue(appView),
argumentValues,
instructionIndex,
parentConstructorLookup.getReference().getParameter(parentConstructorArgumentIndex),
parentConstructorArgumentValues::add);
parentConstructorArgumentIndex++;
}
// Invoke parent constructor.
lirBuilder.addInvokeDirect(
parentConstructorLookup.getReference(), parentConstructorArgumentValues, false);
instructionIndex++;
// Assign each field.
addLirInstructionsForInstanceFieldAssignments(
appView,
method,
lirBuilder,
strategy,
argumentValues,
instructionIndex,
instanceFieldAssignmentsPost,
lens);
// Return.
lirBuilder.addReturnVoid();
instructionIndex++;
return new LirCode<>(lirBuilder.build()) {
@Override
public boolean hasExplicitCodeLens() {
return true;
}
@Override
public GraphLens getCodeLens(AppView<?> appView) {
return lens;
}
};
}
private static int addLirInstructionsForInstanceFieldAssignments(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod method,
LirBuilder<Value, Integer> lirBuilder,
LirEncodingStrategy<Value, Integer> strategy,
List<Value> argumentValues,
int instructionIndex,
Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments,
HorizontalClassMergerGraphLens lens) {
for (Entry<DexField, InstanceFieldInitializationInfo> entry :
instanceFieldAssignments.entrySet()) {
DexField field = entry.getKey();
InstanceFieldInitializationInfo initializationInfo = entry.getValue();
// Load the field value and then set the field.
Box<Value> fieldValueBox = new Box<>();
instructionIndex =
addLirInstructionsForInitializationInfo(
appView,
lirBuilder,
strategy,
initializationInfo,
argumentValues,
instructionIndex,
field.getType(),
fieldValueBox::set);
Value fieldValue = fieldValueBox.get();
// Insert a check to ensure the program continues to type check according to Java type
// checking. Otherwise, instance initializer merging may cause open interfaces. If
// <init>(A) and <init>(B) both have the behavior `this.i = arg; this.j = arg` where the
// type of `i` is I and the type of `j` is J, and both A and B implements I and J, then
// the constructors are merged into a single constructor <init>(java.lang.Object), which
// is no longer strictly type checking. Note that no choice of parameter type would solve
// this.
DexField rewrittenField = lens.getNextFieldSignature(field);
if (initializationInfo.isArgumentInitializationInfo()) {
int argumentIndex = initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
if (argumentIndex > 0) {
DexType argumentType = method.getArgumentType(argumentIndex);
if (argumentType.isClassType()
&& !appView.appInfo().isSubtype(argumentType, rewrittenField.getType())) {
TypeElement newFieldValueTypeElement =
rewrittenField.getType().toTypeElement(appView, fieldValue.getType().nullability());
Value newFieldValue =
Value.createNoDebugLocal(instructionIndex, newFieldValueTypeElement);
strategy.defineValue(newFieldValue, newFieldValue.getNumber());
lirBuilder.addSafeCheckCast(rewrittenField.getType(), fieldValue);
fieldValue = newFieldValue;
instructionIndex++;
}
}
}
lirBuilder.addInstancePut(rewrittenField, ListUtils.first(argumentValues), fieldValue);
instructionIndex++;
}
return instructionIndex;
}
private static int addLirInstructionsForInitializationInfo(
AppView<? extends AppInfoWithClassHierarchy> appView,
LirBuilder<Value, Integer> lirBuilder,
LirEncodingStrategy<Value, Integer> strategy,
InstanceFieldInitializationInfo initializationInfo,
List<Value> argumentValues,
int instructionIndex,
DexType type,
Consumer<Value> valueConsumer) {
Value value;
if (initializationInfo.isArgumentInitializationInfo()) {
int argumentIndex = initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
value = argumentValues.get(argumentIndex);
} else {
assert initializationInfo.isSingleValue();
assert initializationInfo.asSingleValue().isSingleConstValue();
SingleConstValue singleConstValue = initializationInfo.asSingleValue().asSingleConstValue();
TypeElement valueTypeElement;
if (singleConstValue.isSingleConstClassValue()) {
DexType classType = singleConstValue.asSingleConstClassValue().getType();
lirBuilder.addConstClass(classType, false);
valueTypeElement = TypeElement.classClassType(appView, definitelyNotNull());
} else if (singleConstValue.isSingleDexItemBasedStringValue()) {
SingleDexItemBasedStringValue dexItemBasedStringValue =
singleConstValue.asSingleDexItemBasedStringValue();
lirBuilder.addDexItemBasedConstString(
dexItemBasedStringValue.getItem(), dexItemBasedStringValue.getNameComputationInfo());
valueTypeElement = TypeElement.stringClassType(appView, definitelyNotNull());
} else if (singleConstValue.isNull()) {
assert type.isReferenceType();
lirBuilder.addConstNull();
valueTypeElement = TypeElement.getNull();
} else if (singleConstValue.isSingleNumberValue()) {
assert type.isPrimitiveType();
long numberValue = singleConstValue.asSingleNumberValue().getValue();
lirBuilder.addConstNumber(ValueType.fromDexType(type), numberValue);
valueTypeElement = type.toTypeElement(appView);
} else {
assert singleConstValue.isSingleStringValue();
DexString string = singleConstValue.asSingleStringValue().getDexString();
lirBuilder.addConstString(string);
valueTypeElement = TypeElement.stringClassType(appView, definitelyNotNull());
}
value = Value.createNoDebugLocal(instructionIndex, valueTypeElement);
strategy.defineValue(value, value.getNumber());
instructionIndex++;
}
valueConsumer.accept(value);
return instructionIndex;
}
@Override
public String toString() {
return "IncompleteMergedInstanceInitializerCode";
}
}