blob: 7d0d5c00d14fba3c17a4bf84c3c2c26e61679069 [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.horizontalclassmerging;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass.FieldSetter;
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.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AnnotationFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.OptionalBool;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* The tree fixer traverses all program classes and finds and fixes references to old classes which
* have been remapped to new classes by the class merger (stored in {@link
* TreeFixer#mergedClasses}). While doing so, all updated changes are tracked in {@link
* TreeFixer#lensBuilder}.
*/
class TreeFixer {
private final Map<DexType, DexType> mergedClasses;
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
private final AppView<AppInfoWithLiveness> appView;
public TreeFixer(
AppView<AppInfoWithLiveness> appView,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
Map<DexType, DexType> mergedClasses) {
this.mergedClasses = mergedClasses;
this.lensBuilder = lensBuilder;
this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
this.appView = appView;
}
HorizontalClassMergerGraphLens fixupTypeReferences() {
// Globally substitute merged class types in protos and holders.
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.getMethodCollection().replaceMethods(this::fixupMethod);
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
fieldAccessChangesBuilder.build(this::fixupMethod).modify(appView);
if (lens != null) {
new AnnotationFixer(lens).run(appView.appInfo().classes());
}
return lens;
}
private DexEncodedMethod fixupMethod(DexEncodedMethod method) {
DexMethod methodReference = method.method;
DexMethod newMethodReference = fixupMethod(methodReference);
if (newMethodReference == methodReference) {
return method;
}
// If the method is a synthesized method, then don't record the original signature.
if ((method.getCode() instanceof ConstructorEntryPointSynthesizedCode)) {
assert lensBuilder.hasExtraSignatureMappingFor(methodReference);
lensBuilder.recordExtraOriginalSignature(methodReference, newMethodReference);
lensBuilder.mapMethod(methodReference, newMethodReference);
} else {
lensBuilder.moveMethod(methodReference, newMethodReference);
}
DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference);
if (newMethod.isNonPrivateVirtualMethod()) {
// Since we changed the return type or one of the parameters, this method cannot be a
// classpath or library method override, since we only class merge program classes.
assert !method.isLibraryMethodOverride().isTrue();
newMethod.setLibraryMethodOverride(OptionalBool.FALSE);
}
return newMethod;
}
private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
if (fields == null) {
return;
}
for (int i = 0; i < fields.size(); i++) {
DexEncodedField encodedField = fields.get(i);
DexField field = encodedField.field;
DexType newType = fixupType(field.type);
DexType newHolder = fixupType(field.holder);
DexField newField = appView.dexItemFactory().createField(newHolder, newType, field.name);
if (newField != encodedField.field) {
// TODO(b/165498187): track mapped fields
/* lensBuilder.map(field, newField); */
setter.setField(i, encodedField.toTypeSubstitutedField(newField));
}
}
}
private DexMethod fixupMethod(DexMethod method) {
return appView
.dexItemFactory()
.createMethod(fixupType(method.holder), fixupProto(method.proto), method.name);
}
private DexProto fixupProto(DexProto proto) {
DexProto result = protoFixupCache.get(proto);
if (result == null) {
DexType returnType = fixupType(proto.returnType);
DexType[] arguments = fixupTypes(proto.parameters.values);
result = appView.dexItemFactory().createProto(returnType, arguments);
protoFixupCache.put(proto, result);
}
return result;
}
private DexType fixupType(DexType type) {
if (type.isArrayType()) {
DexType base = type.toBaseType(appView.dexItemFactory());
DexType fixed = fixupType(base);
if (base == fixed) {
return type;
}
return type.replaceBaseType(fixed, appView.dexItemFactory());
}
if (type.isClassType()) {
while (mergedClasses.containsKey(type)) {
type = mergedClasses.get(type);
}
}
return type;
}
private DexType[] fixupTypes(DexType[] types) {
DexType[] result = new DexType[types.length];
for (int i = 0; i < result.length; i++) {
result[i] = fixupType(types[i]);
}
return result;
}
}