1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.naming; 5 6 import com.android.tools.r8.graph.DexField; 7 import com.android.tools.r8.graph.DexMethod; 8 import com.android.tools.r8.graph.DexType; 9 import com.android.tools.r8.graph.IndexedDexItem; 10 import com.android.tools.r8.naming.MemberNaming.FieldSignature; 11 import com.android.tools.r8.naming.MemberNaming.MethodSignature; 12 import com.android.tools.r8.naming.MemberNaming.Signature; 13 import com.android.tools.r8.utils.DescriptorUtils; 14 import com.google.common.collect.BiMap; 15 import com.google.common.collect.ImmutableBiMap; 16 import com.google.common.collect.ImmutableMap; 17 import java.io.IOException; 18 import java.io.StringWriter; 19 import java.io.Writer; 20 import java.util.Hashtable; 21 import java.util.Map; 22 import java.util.function.Consumer; 23 24 public class ClassNameMapper { 25 26 private final ImmutableMap<String, ClassNaming> classNameMappings; 27 private ImmutableBiMap<String, String> nameMapping; 28 29 private Hashtable<Signature, Signature> signatureMap = new Hashtable<>(); 30 ClassNameMapper(Map<String, ClassNaming> classNameMappings)31 ClassNameMapper(Map<String, ClassNaming> classNameMappings) { 32 this.classNameMappings = ImmutableMap.copyOf(classNameMappings); 33 } 34 canonicalizeSignature(Signature signature)35 private Signature canonicalizeSignature(Signature signature) { 36 Signature result = signatureMap.get(signature); 37 if (result != null) { 38 return result; 39 } 40 signatureMap.put(signature, signature); 41 return signature; 42 } 43 getRenamedMethodSignature(DexMethod method)44 public MethodSignature getRenamedMethodSignature(DexMethod method) { 45 DexType[] parameters = method.proto.parameters.values; 46 String[] parameterTypes = new String[parameters.length]; 47 for (int i = 0; i < parameters.length; i++) { 48 parameterTypes[i] = deobfuscateType(parameters[i].toDescriptorString()); 49 } 50 String returnType = deobfuscateType(method.proto.returnType.toDescriptorString()); 51 52 MethodSignature signature = new MethodSignature(method.name.toString(), returnType, 53 parameterTypes); 54 return (MethodSignature) canonicalizeSignature(signature); 55 } 56 getRenamedFieldSignature(DexField field)57 public Signature getRenamedFieldSignature(DexField field) { 58 String type = deobfuscateType(field.type.toDescriptorString()); 59 return canonicalizeSignature(new FieldSignature(field.name.toString(), type)); 60 } 61 62 /** 63 * Deobfuscate a class name. 64 * 65 * Returns the deobfuscated name if a mapping was found. Otherwise it returns the passed in name. 66 */ deobfuscateClassName(String name)67 public String deobfuscateClassName(String name) { 68 ClassNaming classNaming = classNameMappings.get(name); 69 if (classNaming == null) { 70 return name; 71 } 72 return classNaming.originalName; 73 } 74 deobfuscateType(String asString)75 private String deobfuscateType(String asString) { 76 return DescriptorUtils.descriptorToJavaType(asString, this); 77 } 78 getClassNaming(String name)79 public ClassNaming getClassNaming(String name) { 80 return classNameMappings.get(name); 81 } 82 write(Writer writer, boolean collapseRanges)83 public void write(Writer writer, boolean collapseRanges) throws IOException { 84 for (ClassNaming naming : classNameMappings.values()) { 85 naming.write(writer, collapseRanges); 86 } 87 } 88 forAllClassNamings(Consumer<ClassNaming> consumer)89 public void forAllClassNamings(Consumer<ClassNaming> consumer) { 90 classNameMappings.values().forEach(consumer); 91 } 92 93 @Override toString()94 public String toString() { 95 try { 96 StringWriter writer = new StringWriter(); 97 write(writer, false); 98 return writer.toString(); 99 } catch (IOException e) { 100 return e.toString(); 101 } 102 } 103 getObfuscatedToOriginalMapping()104 public BiMap<String, String> getObfuscatedToOriginalMapping() { 105 if (nameMapping == null) { 106 ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.builder(); 107 for (String name : classNameMappings.keySet()) { 108 builder.put(name, classNameMappings.get(name).originalName); 109 } 110 nameMapping = builder.build(); 111 } 112 return nameMapping; 113 } 114 115 @Override equals(Object o)116 public boolean equals(Object o) { 117 return o instanceof ClassNameMapper 118 && classNameMappings.equals(((ClassNameMapper) o).classNameMappings); 119 } 120 121 @Override hashCode()122 public int hashCode() { 123 return 31 * classNameMappings.hashCode(); 124 } 125 originalNameOf(IndexedDexItem item)126 public String originalNameOf(IndexedDexItem item) { 127 if (item instanceof DexField) { 128 return lookupName(getRenamedFieldSignature((DexField) item), ((DexField) item).clazz); 129 } else if (item instanceof DexMethod) { 130 return lookupName(getRenamedMethodSignature((DexMethod) item), ((DexMethod) item).holder); 131 } else if (item instanceof DexType) { 132 return DescriptorUtils.descriptorToJavaType(((DexType) item).toDescriptorString(), this); 133 } else { 134 return item.toString(); 135 } 136 } 137 lookupName(Signature signature, DexType clazz)138 private String lookupName(Signature signature, DexType clazz) { 139 String decoded = DescriptorUtils.descriptorToJavaType(clazz.descriptor.toString()); 140 ClassNaming classNaming = getClassNaming(decoded); 141 if (classNaming == null) { 142 return decoded + " " + signature.toString(); 143 } 144 MemberNaming memberNaming = classNaming.lookup(signature); 145 if (memberNaming == null) { 146 return classNaming.originalName + " " + signature.toString(); 147 } 148 return classNaming.originalName + " " + memberNaming.signature.toString(); 149 } 150 originalSignatureOf(DexMethod method)151 public Signature originalSignatureOf(DexMethod method) { 152 String decoded = DescriptorUtils 153 .descriptorToJavaType(method.holder.descriptor.toString()); 154 MethodSignature memberSignature = getRenamedMethodSignature(method); 155 ClassNaming classNaming = getClassNaming(decoded); 156 if (classNaming == null) { 157 return memberSignature; 158 } 159 MemberNaming memberNaming = classNaming.lookup(memberSignature); 160 if (memberNaming == null) { 161 return memberSignature; 162 } 163 return memberNaming.signature; 164 } 165 originalNameOf(DexType clazz)166 public String originalNameOf(DexType clazz) { 167 return deobfuscateType(clazz.descriptor.toString()); 168 } 169 } 170