blob: 395409f48e41a9c0be27c4895ed7df96e81e5689 [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.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class HorizontalClassMergerGraphLens extends NestedGraphLens {
private final AppView<?> appView;
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
private final Map<DexMethod, DexMethod> originalConstructorSignatures;
private HorizontalClassMergerGraphLens(
AppView<?> appView,
Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
Map<DexType, DexType> typeMap,
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
BiMap<DexField, DexField> originalFieldSignatures,
BiMap<DexMethod, DexMethod> originalMethodSignatures,
Map<DexMethod, DexMethod> originalConstructorSignatures,
GraphLens previousLens) {
super(
typeMap,
methodMap,
fieldMap,
originalFieldSignatures,
originalMethodSignatures,
previousLens,
appView.dexItemFactory());
this.appView = appView;
this.methodExtraParameters = methodExtraParameters;
this.originalConstructorSignatures = originalConstructorSignatures;
}
@Override
public DexMethod getOriginalMethodSignature(DexMethod method) {
DexMethod originalConstructor = originalConstructorSignatures.get(method);
if (originalConstructor == null) {
return super.getOriginalMethodSignature(method);
}
return getPrevious().getOriginalMethodSignature(originalConstructor);
}
public HorizontallyMergedClasses getHorizontallyMergedClasses() {
return new HorizontallyMergedClasses(this.typeMap);
}
/**
* If an overloaded constructor is requested, add the constructor id as a parameter to the
* constructor. Otherwise return the lookup on the underlying graph lens.
*/
@Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context) {
List<ExtraParameter> extraParameters = methodExtraParameters.get(previous.getReference());
MethodLookupResult lookup = super.internalDescribeLookupMethod(previous, context);
if (extraParameters == null) {
return lookup;
}
return MethodLookupResult.builder(this)
.setReference(lookup.getReference())
.setPrototypeChanges(lookup.getPrototypeChanges().withExtraParameters(extraParameters))
.setType(lookup.getType())
.build();
}
public static class Builder {
private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
private final BiMap<DexField, DexField> fieldMap = HashBiMap.create();
private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
private final Map<DexMethod, Set<DexMethod>> completeInverseMethodMap = new IdentityHashMap<>();
private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
new IdentityHashMap<>();
Builder() {}
public HorizontalClassMergerGraphLens build(AppView<?> appView) {
assert !typeMap.isEmpty();
BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
return new HorizontalClassMergerGraphLens(
appView,
methodExtraParameters,
typeMap,
fieldMap,
methodMap,
originalFieldSignatures,
originalMethodSignatures,
extraOriginalMethodSignatures,
appView.graphLens());
}
public DexType lookupType(DexType type) {
return typeMap.getOrDefault(type, type);
}
public Builder mapType(DexType from, DexType to) {
typeMap.put(from, to);
return this;
}
/** Bidirectional mapping from one method to another. */
public Builder moveMethod(DexMethod from, DexMethod to) {
if (from == to) {
return this;
}
mapMethod(from, to);
recordOriginalSignature(from, to);
return this;
}
public Builder recordOriginalSignature(DexMethod from, DexMethod to) {
if (from == to) {
return this;
}
originalMethodSignatures.forcePut(to, originalMethodSignatures.getOrDefault(from, from));
return this;
}
/** Unidirectional mapping from one method to another. */
public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) {
if (from == to) {
return this;
}
extraOriginalMethodSignatures.put(to, extraOriginalMethodSignatures.getOrDefault(from, from));
return this;
}
/** Unidirectional mapping from one method to another. */
public Builder mapMethod(DexMethod from, DexMethod to) {
if (from == to) {
return this;
}
for (DexMethod existingFrom :
completeInverseMethodMap.getOrDefault(from, Collections.emptySet())) {
methodMap.put(existingFrom, to);
// We currently assume that a single method can only be remapped twice.
assert completeInverseMethodMap
.getOrDefault(existingFrom, Collections.emptySet())
.isEmpty();
}
methodMap.put(from, to);
completeInverseMethodMap.computeIfAbsent(to, ignore -> new HashSet<>()).add(from);
return this;
}
public boolean hasExtraSignatureMappingFor(DexMethod method) {
return extraOriginalMethodSignatures.containsKey(method);
}
public boolean hasOriginalSignatureMappingFor(DexMethod method) {
return originalMethodSignatures.containsKey(method);
}
/**
* One way mapping from one constructor to another. This is used for synthesized constructors,
* where many constructors are merged into a single constructor. The synthesized constructor
* therefore does not have a unique reverse constructor.
*/
public Builder mapMergedConstructor(
DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) {
mapMethod(from, to);
methodExtraParameters.put(from, extraParameters);
return this;
}
}
}