blob: d96e38c593758b19c337e8e88431f821c9c12b9e [file] [log] [blame]
// Copyright (c) 2023, 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.fixup;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.collections.DexMethodSignatureBiMap;
import com.google.common.collect.BiMap;
import java.util.function.BiConsumer;
public class MethodNamingUtility {
private final DexItemFactory factory;
private final DexMethodSignatureBiMap<DexMethodSignature> inheritedSignatures;
private final BiMap<DexMethod, DexMethod> localSignatures;
public MethodNamingUtility(
DexItemFactory factory,
DexMethodSignatureBiMap<DexMethodSignature> inheritedSignatures,
BiMap<DexMethod, DexMethod> localSignatures) {
this.factory = factory;
this.inheritedSignatures = inheritedSignatures;
this.localSignatures = localSignatures;
}
@SuppressWarnings("ReferenceEquality")
public DexMethod nextUniqueMethod(
DexEncodedMethod method, DexProto newProto, DexType initExtraType) {
DexMethod reference = method.getReference();
if (method.isClassInitializer()) {
assert reference.getProto() == newProto;
return reference;
}
if (method.isInstanceInitializer()) {
assert initExtraType != null;
return nextUniqueInitializer(reference, newProto, initExtraType);
}
if (method.isNonPrivateVirtualMethod()) {
return nextUniqueVirtualMethod(reference, newProto);
}
return nextUniquePrivateOrStaticMethod(reference, newProto);
}
@SuppressWarnings("ReferenceEquality")
private DexMethod nextUniqueInitializer(
DexMethod reference, DexProto newProto, DexType initExtraType) {
assert !inheritedSignatures.containsKey(reference.getSignature());
// 1) We check if the reference has already been reserved (pinning).
DexMethod remapped = localSignatures.get(reference);
if (remapped != null) {
assert remapped.getProto() == newProto;
return remapped.withHolder(reference.getHolderType(), factory);
}
// 2) We check for collision with already mapped methods.
DexMethod newMethod = reference.withProto(newProto, factory);
if (localSignatures.containsValue(newMethod)) {
// This collides with something that has been renamed into this.
newMethod =
factory.createInstanceInitializerWithFreshProto(
newMethod, initExtraType, tryMethod -> !localSignatures.containsValue(tryMethod));
}
// 3) Finally register the new method and return it.
assert !localSignatures.containsValue(newMethod);
localSignatures.put(reference, newMethod);
return newMethod;
}
private DexMethod nextUniquePrivateOrStaticMethod(DexMethod reference, DexProto newProto) {
return nextUniqueMethod(reference, newProto, localSignatures::put);
}
private DexMethod nextUniqueVirtualMethod(DexMethod reference, DexProto newProto) {
return nextUniqueMethod(
reference,
newProto,
(from, to) -> inheritedSignatures.put(from.getSignature(), to.getSignature()));
}
private boolean anyCollision(DexMethod method) {
return localSignatures.containsValue(method)
|| inheritedSignatures.containsValue(method.getSignature());
}
@SuppressWarnings("ReferenceEquality")
private DexMethod nextUniqueMethod(
DexMethod reference, DexProto newProto, BiConsumer<DexMethod, DexMethod> registration) {
// 1) We check if the reference has already been reserved (pinning or override).
DexMethodSignature remappedSignature = inheritedSignatures.get(reference.getSignature());
if (remappedSignature != null) {
assert remappedSignature.getProto() == newProto;
return remappedSignature.withHolder(reference.getHolderType(), factory);
}
// 2) We check for collision with already mapped methods.
DexMethod newMethod = reference.withProto(newProto, factory);
if (anyCollision(newMethod)) {
newMethod =
factory.createFreshMethodNameWithoutHolder(
newMethod.getName().toString(),
newMethod.getProto(),
newMethod.getHolderType(),
tryMethod -> !anyCollision(tryMethod));
}
// 3) Finally register the new method and return it.
assert !anyCollision(newMethod);
registration.accept(reference, newMethod);
return newMethod;
}
}