blob: 5dcc7b8f6dd7fc5e588e7d37c0aca39090bb8ebc [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.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.utils.ThrowingConsumer;
import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 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 Map<MethodSignature, MemberNaming> methodMembers = new HashMap<>();
private final Map<FieldSignature, MemberNaming> fieldMembers = new HashMap<>();
private Builder(String renamedName, String originalName) {
this.originalName = originalName;
this.renamedName = renamedName;
}
@Override
public ClassNaming.Builder addMemberEntry(MemberNaming entry) {
// Unlike {@link ClassNamingForNameMapper.Builder#addMemberEntry},
// the key is original signature.
if (entry.isMethodNaming()) {
methodMembers.put((MethodSignature) entry.getOriginalSignature(), entry);
} else {
fieldMembers.put((FieldSignature) entry.getOriginalSignature(), entry);
}
return this;
}
@Override
public ClassNamingForMapApplier build() {
return new ClassNamingForMapApplier(renamedName, originalName, methodMembers, fieldMembers);
}
@Override
/** No-op */
public void addMappedRange(
Range obfuscatedRange,
MemberNaming.MethodSignature originalSignature,
Object originalRange,
String obfuscatedName) {}
}
static Builder builder(String renamedName, String originalName) {
return new Builder(renamedName, originalName);
}
private final String originalName;
final String renamedName;
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.methodMembers, proxy.fieldMembers);
}
private ClassNamingForMapApplier(
String renamedName,
String originalName,
Map<MethodSignature, MemberNaming> methodMembers,
Map<FieldSignature, MemberNaming> fieldMembers) {
this.renamedName = renamedName;
this.originalName = originalName;
this.methodMembers = ImmutableMap.copyOf(methodMembers);
this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
}
@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.toString())
&& signature.type.equals(field.type.getName())) {
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.toString())
&& signature.type.equals(method.proto.returnType.toString())
&& Arrays.equals(signature.parameters,
Arrays.stream(method.proto.parameters.values)
.map(DexType::toString).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)
&& methodMembers.equals(that.methodMembers)
&& fieldMembers.equals(that.fieldMembers);
}
@Override
public int hashCode() {
int result = originalName.hashCode();
result = 31 * result + renamedName.hashCode();
result = 31 * result + methodMembers.hashCode();
result = 31 * result + fieldMembers.hashCode();
return result;
}
}