blob: a222acbe1799b60a5b2747d6d81685773124dd04 [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.naming;
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.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* Stores name information for a class.
* <p>
* The main differences of this against {@link ClassNamingForNameMapper} are:
* 1) field and method mappings are maintained and searched separately for faster lookup;
* 2) similar to the relation between {@link ClassNameMapper} and {@link SeedMapper}, this one
* uses original {@link Signature} as a key to look up {@link MemberNaming},
* whereas {@link ClassNamingForNameMapper} uses renamed {@link Signature} as a key; and thus
* 3) logic of {@link #lookup} and {@link #lookupByOriginalSignature} are inverted; and
* 4) {@link #lookupByOriginalItem}'s are introduced for lightweight lookup.
*/
public class ClassNamingForMapApplier implements ClassNaming {
public static class Builder extends ClassNaming.Builder {
private final String originalName;
private final String renamedName;
private final Position position;
private final Reporter reporter;
private final Map<MethodSignature, List<MemberNaming>> qualifiedMethodMembers = new HashMap<>();
private final Map<MethodSignature, MemberNaming> methodMembers = new HashMap<>();
private final Map<FieldSignature, MemberNaming> fieldMembers = new HashMap<>();
private Builder(String renamedName, String originalName, Position position, Reporter reporter) {
this.originalName = originalName;
this.renamedName = renamedName;
this.position = position;
this.reporter = reporter;
}
@Override
public ClassNaming.Builder addMemberEntry(MemberNaming entry) {
// Unlike {@link ClassNamingForNameMapper.Builder#addMemberEntry},
// the key is original signature.
if (entry.isMethodNaming()) {
MethodSignature signature = (MethodSignature) entry.getOriginalSignature();
if (signature.isQualified()) {
qualifiedMethodMembers.computeIfAbsent(signature, k -> new ArrayList<>(2)).add(entry);
} else if (methodMembers.put(signature, entry) != null) {
reporter.error(
ProguardMapError.duplicateSourceMember(
signature.toString(), this.originalName, entry.position));
}
} else {
FieldSignature signature = (FieldSignature) entry.getOriginalSignature();
if (!signature.isQualified() && fieldMembers.put(signature, entry) != null) {
reporter.error(
ProguardMapError.duplicateSourceMember(
signature.toString(), this.originalName, entry.position));
}
}
return this;
}
@Override
public ClassNamingForMapApplier build() {
return new ClassNamingForMapApplier(
renamedName, originalName, position, qualifiedMethodMembers, methodMembers, fieldMembers);
}
@Override
public MappedRange addMappedRange(
Range obfuscatedRange,
MemberNaming.MethodSignature originalSignature,
Object originalRange,
String obfuscatedName) {
return null;
}
@Override
public void addMappingInformation(
MappingInformation info, Consumer<MappingInformation> onProhibitedAddition) {
// Intentionally empty.
}
}
static Builder builder(
String renamedName, String originalName, Position position, Reporter reporter) {
return new Builder(renamedName, originalName, position, reporter);
}
private final String originalName;
final String renamedName;
final Position position;
private final ImmutableMap<MethodSignature, List<MemberNaming>> qualifiedMethodMembers;
private final ImmutableMap<MethodSignature, MemberNaming> methodMembers;
private final ImmutableMap<FieldSignature, MemberNaming> fieldMembers;
// Constructor to help chaining {@link ClassNamingForMapApplier} according to class hierarchy.
ClassNamingForMapApplier(ClassNamingForMapApplier proxy) {
this(
proxy.renamedName,
proxy.originalName,
proxy.position,
proxy.qualifiedMethodMembers,
proxy.methodMembers,
proxy.fieldMembers);
}
private ClassNamingForMapApplier(
String renamedName,
String originalName,
Position position,
Map<MethodSignature, List<MemberNaming>> qualifiedMethodMembers,
Map<MethodSignature, MemberNaming> methodMembers,
Map<FieldSignature, MemberNaming> fieldMembers) {
this.renamedName = renamedName;
this.originalName = originalName;
this.position = position;
this.qualifiedMethodMembers = ImmutableMap.copyOf(qualifiedMethodMembers);
this.methodMembers = ImmutableMap.copyOf(methodMembers);
this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
}
public ImmutableMap<MethodSignature, List<MemberNaming>> getQualifiedMethodMembers() {
return qualifiedMethodMembers;
}
@Override
public <T extends Throwable> void forAllMemberNaming(
ThrowingConsumer<MemberNaming, T> consumer) throws T {
forAllFieldNaming(consumer);
forAllMethodNaming(consumer);
}
@Override
public <T extends Throwable> void forAllFieldNaming(
ThrowingConsumer<MemberNaming, T> consumer) throws T {
for (MemberNaming naming : fieldMembers.values()) {
consumer.accept(naming);
}
}
@Override
public <T extends Throwable> void forAllMethodNaming(
ThrowingConsumer<MemberNaming, T> consumer) throws T {
for (MemberNaming naming : methodMembers.values()) {
consumer.accept(naming);
}
}
@Override
public MemberNaming lookup(Signature renamedSignature) {
// As the key is inverted, this looks a lot like
// {@link ClassNamingForNameMapper#lookupByOriginalSignature}.
if (renamedSignature.kind() == SignatureKind.METHOD) {
for (MemberNaming memberNaming : methodMembers.values()) {
if (memberNaming.getRenamedSignature().equals(renamedSignature)) {
return memberNaming;
}
}
return null;
} else {
assert renamedSignature.kind() == SignatureKind.FIELD;
for (MemberNaming memberNaming : fieldMembers.values()) {
if (memberNaming.getRenamedSignature().equals(renamedSignature)) {
return memberNaming;
}
}
return null;
}
}
@Override
public MemberNaming lookupByOriginalSignature(Signature original) {
// As the key is inverted, this looks a lot like {@link ClassNamingForNameMapper#lookup}.
if (original.kind() == SignatureKind.METHOD) {
return methodMembers.get(original);
} else {
assert original.kind() == SignatureKind.FIELD;
return fieldMembers.get(original);
}
}
MemberNaming lookupByOriginalItem(DexField field) {
for (Map.Entry<FieldSignature, MemberNaming> entry : fieldMembers.entrySet()) {
FieldSignature signature = entry.getKey();
if (signature.name.equals(field.name.toSourceString())
&& signature.type.equals(field.type.toSourceString())) {
return entry.getValue();
}
}
return null;
}
protected MemberNaming lookupByOriginalItem(DexMethod method) {
for (Map.Entry<MethodSignature, MemberNaming> entry : methodMembers.entrySet()) {
MethodSignature signature = entry.getKey();
if (signature.name.equals(method.name.toSourceString())
&& signature.type.equals(method.proto.returnType.toSourceString())
&& Arrays.equals(signature.parameters,
Arrays.stream(method.proto.parameters.values)
.map(DexType::toSourceString).toArray(String[]::new))) {
return entry.getValue();
}
}
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ClassNamingForMapApplier)) {
return false;
}
ClassNamingForMapApplier that = (ClassNamingForMapApplier) o;
return originalName.equals(that.originalName)
&& renamedName.equals(that.renamedName)
&& qualifiedMethodMembers.equals(that.qualifiedMethodMembers)
&& methodMembers.equals(that.methodMembers)
&& fieldMembers.equals(that.fieldMembers);
}
@Override
public int hashCode() {
return Objects.hashCode(
originalName, renamedName, qualifiedMethodMembers, methodMembers, fieldMembers);
}
}