summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
diff options
context:
space:
mode:
authorGravatar Thog2017-03-08 08:17:04 +0100
committerGravatar Thog2017-03-08 08:17:04 +0100
commit6e464ea251cab63c776ece0b2a356f1498ffa294 (patch)
tree5ed30c03f5ac4cd2d6877874f5ede576049954f7 /src/main/java/cuchaz/enigma/convert/MappingsConverter.java
parentDrop unix case style and implement hashCode when equals is overrided (diff)
downloadenigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.gz
enigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.xz
enigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.zip
Follow Fabric guidelines
Diffstat (limited to 'src/main/java/cuchaz/enigma/convert/MappingsConverter.java')
-rw-r--r--src/main/java/cuchaz/enigma/convert/MappingsConverter.java1363
1 files changed, 679 insertions, 684 deletions
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
index a5ded67..fa3e936 100644
--- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
+++ b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.convert; 12package cuchaz.enigma.convert;
12 13
13import com.google.common.collect.*; 14import com.google.common.collect.*;
@@ -29,688 +30,682 @@ import java.util.jar.JarFile;
29 30
30public class MappingsConverter { 31public class MappingsConverter {
31 32
32 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { 33 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) {
33 34
34 // index jars 35 // index jars
35 System.out.println("Indexing source jar..."); 36 System.out.println("Indexing source jar...");
36 JarIndex sourceIndex = new JarIndex(); 37 JarIndex sourceIndex = new JarIndex();
37 sourceIndex.indexJar(sourceJar, false); 38 sourceIndex.indexJar(sourceJar, false);
38 System.out.println("Indexing dest jar..."); 39 System.out.println("Indexing dest jar...");
39 JarIndex destIndex = new JarIndex(); 40 JarIndex destIndex = new JarIndex();
40 destIndex.indexJar(destJar, false); 41 destIndex.indexJar(destJar, false);
41 42
42 // compute the matching 43 // compute the matching
43 ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); 44 ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null);
44 return new ClassMatches(matching.matches()); 45 return new ClassMatches(matching.matches());
45 } 46 }
46 47
47 public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) { 48 public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) {
48 49
49 System.out.println("Iteratively matching classes"); 50 System.out.println("Iteratively matching classes");
50 51
51 ClassMatching lastMatching = null; 52 ClassMatching lastMatching = null;
52 int round = 0; 53 int round = 0;
53 SidedClassNamer sourceNamer = null; 54 SidedClassNamer sourceNamer = null;
54 SidedClassNamer destNamer = null; 55 SidedClassNamer destNamer = null;
55 for (boolean useReferences : Arrays.asList(false, true)) { 56 for (boolean useReferences : Arrays.asList(false, true)) {
56 57
57 int numUniqueMatchesLastTime = 0; 58 int numUniqueMatchesLastTime = 0;
58 if (lastMatching != null) { 59 if (lastMatching != null) {
59 numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); 60 numUniqueMatchesLastTime = lastMatching.uniqueMatches().size();
60 } 61 }
61 62
62 while (true) { 63 while (true) {
63 64
64 System.out.println("Round " + (++round) + "..."); 65 System.out.println("Round " + (++round) + "...");
65 66
66 // init the matching with identity settings 67 // init the matching with identity settings
67 ClassMatching matching = new ClassMatching( 68 ClassMatching matching = new ClassMatching(
68 new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), 69 new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences),
69 new ClassIdentifier(destJar, destIndex, destNamer, useReferences) 70 new ClassIdentifier(destJar, destIndex, destNamer, useReferences)
70 ); 71 );
71 72
72 if (knownMatches != null) { 73 if (knownMatches != null) {
73 matching.addKnownMatches(knownMatches); 74 matching.addKnownMatches(knownMatches);
74 } 75 }
75 76
76 if (lastMatching == null) { 77 if (lastMatching == null) {
77 // search all classes 78 // search all classes
78 matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); 79 matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries());
79 } else { 80 } else {
80 // we already know about these matches from last time 81 // we already know about these matches from last time
81 matching.addKnownMatches(lastMatching.uniqueMatches()); 82 matching.addKnownMatches(lastMatching.uniqueMatches());
82 83
83 // search unmatched and ambiguously-matched classes 84 // search unmatched and ambiguously-matched classes
84 matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); 85 matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses());
85 for (ClassMatch match : lastMatching.ambiguousMatches()) { 86 for (ClassMatch match : lastMatching.ambiguousMatches()) {
86 matching.match(match.sourceClasses, match.destClasses); 87 matching.match(match.sourceClasses, match.destClasses);
87 } 88 }
88 } 89 }
89 System.out.println(matching); 90 System.out.println(matching);
90 BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches(); 91 BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches();
91 92
92 // did we match anything new this time? 93 // did we match anything new this time?
93 if (uniqueMatches.size() > numUniqueMatchesLastTime) { 94 if (uniqueMatches.size() > numUniqueMatchesLastTime) {
94 numUniqueMatchesLastTime = uniqueMatches.size(); 95 numUniqueMatchesLastTime = uniqueMatches.size();
95 lastMatching = matching; 96 lastMatching = matching;
96 } else { 97 } else {
97 break; 98 break;
98 } 99 }
99 100
100 // update the namers 101 // update the namers
101 ClassNamer namer = new ClassNamer(uniqueMatches); 102 ClassNamer namer = new ClassNamer(uniqueMatches);
102 sourceNamer = namer.getSourceNamer(); 103 sourceNamer = namer.getSourceNamer();
103 destNamer = namer.getDestNamer(); 104 destNamer = namer.getDestNamer();
104 } 105 }
105 } 106 }
106 107
107 return lastMatching; 108 return lastMatching;
108 } 109 }
109 110
110 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) 111 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator)
111 throws MappingConflict { 112 throws MappingConflict {
112 // sort the unique matches by size of inner class chain 113 // sort the unique matches by size of inner class chain
113 Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create(); 114 Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create();
114 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) { 115 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) {
115 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); 116 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size();
116 matchesByDestChainSize.put(chainSize, match); 117 matchesByDestChainSize.put(chainSize, match);
117 } 118 }
118 119
119 // build the mappings (in order of small-to-large inner chains) 120 // build the mappings (in order of small-to-large inner chains)
120 Mappings newMappings = new Mappings(); 121 Mappings newMappings = new Mappings();
121 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); 122 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet());
122 Collections.sort(chainSizes); 123 Collections.sort(chainSizes);
123 for (int chainSize : chainSizes) { 124 for (int chainSize : chainSizes) {
124 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) { 125 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) {
125 // get class info 126 // get class info
126 ClassEntry obfSourceClassEntry = match.getKey(); 127 ClassEntry obfSourceClassEntry = match.getKey();
127 ClassEntry obfDestClassEntry = match.getValue(); 128 ClassEntry obfDestClassEntry = match.getValue();
128 List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); 129 List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry);
129 130
130 ClassMapping sourceMapping; 131 ClassMapping sourceMapping;
131 if (obfSourceClassEntry.isInnerClass()) { 132 if (obfSourceClassEntry.isInnerClass()) {
132 List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); 133 List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry);
133 sourceMapping = srcClassChain.get(srcClassChain.size() - 1); 134 sourceMapping = srcClassChain.get(srcClassChain.size() - 1);
134 } else { 135 } else {
135 sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); 136 sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry);
136 } 137 }
137 138
138 if (sourceMapping == null) { 139 if (sourceMapping == null) {
139 // if this class was never deobfuscated, don't try to match it 140 // if this class was never deobfuscated, don't try to match it
140 continue; 141 continue;
141 } 142 }
142 143
143 // find out where to make the dest class mapping 144 // find out where to make the dest class mapping
144 if (destClassChain.size() == 1) { 145 if (destClassChain.size() == 1) {
145 // not an inner class, add directly to mappings 146 // not an inner class, add directly to mappings
146 newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); 147 newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false));
147 } else { 148 } else {
148 // inner class, find the outer class mapping 149 // inner class, find the outer class mapping
149 ClassMapping destMapping = null; 150 ClassMapping destMapping = null;
150 for (int i = 0; i < destClassChain.size() - 1; i++) { 151 for (int i = 0; i < destClassChain.size() - 1; i++) {
151 ClassEntry destChainClassEntry = destClassChain.get(i); 152 ClassEntry destChainClassEntry = destClassChain.get(i);
152 if (destMapping == null) { 153 if (destMapping == null) {
153 destMapping = newMappings.getClassByObf(destChainClassEntry); 154 destMapping = newMappings.getClassByObf(destChainClassEntry);
154 if (destMapping == null) { 155 if (destMapping == null) {
155 destMapping = new ClassMapping(destChainClassEntry.getName()); 156 destMapping = new ClassMapping(destChainClassEntry.getName());
156 newMappings.addClassMapping(destMapping); 157 newMappings.addClassMapping(destMapping);
157 } 158 }
158 } else { 159 } else {
159 destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); 160 destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName());
160 if (destMapping == null) { 161 if (destMapping == null) {
161 destMapping = new ClassMapping(destChainClassEntry.getName()); 162 destMapping = new ClassMapping(destChainClassEntry.getName());
162 destMapping.addInnerClassMapping(destMapping); 163 destMapping.addInnerClassMapping(destMapping);
163 } 164 }
164 } 165 }
165 } 166 }
166 destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); 167 destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true));
167 } 168 }
168 } 169 }
169 } 170 }
170 return newMappings; 171 return newMappings;
171 } 172 }
172 173
173 private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { 174 private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) {
174 175
175 ClassNameReplacer replacer = className -> 176 ClassNameReplacer replacer = className ->
176 { 177 {
177 ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); 178 ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className));
178 if (newClassEntry != null) { 179 if (newClassEntry != null) {
179 return newClassEntry.getName(); 180 return newClassEntry.getName();
180 } 181 }
181 return null; 182 return null;
182 }; 183 };
183 184
184 ClassMapping newClassMapping; 185 ClassMapping newClassMapping;
185 String deobfName = oldClassMapping.getDeobfName(); 186 String deobfName = oldClassMapping.getDeobfName();
186 if (deobfName != null) { 187 if (deobfName != null) {
187 if (useSimpleName) { 188 if (useSimpleName) {
188 deobfName = new ClassEntry(deobfName).getSimpleName(); 189 deobfName = new ClassEntry(deobfName).getSimpleName();
189 } 190 }
190 newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); 191 newClassMapping = new ClassMapping(newObfClass.getName(), deobfName);
191 } else { 192 } else {
192 newClassMapping = new ClassMapping(newObfClass.getName()); 193 newClassMapping = new ClassMapping(newObfClass.getName());
193 } 194 }
194 195
195 // migrate fields 196 // migrate fields
196 for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { 197 for (FieldMapping oldFieldMapping : oldClassMapping.fields()) {
197 if (canMigrate(oldFieldMapping.getObfType(), matches)) { 198 if (canMigrate(oldFieldMapping.getObfType(), matches)) {
198 newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); 199 newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer));
199 } else { 200 } else {
200 System.out.println(String.format("Can't map field, dropping: %s.%s %s", 201 System.out.println(String.format("Can't map field, dropping: %s.%s %s",
201 oldClassMapping.getDeobfName(), 202 oldClassMapping.getDeobfName(),
202 oldFieldMapping.getDeobfName(), 203 oldFieldMapping.getDeobfName(),
203 oldFieldMapping.getObfType() 204 oldFieldMapping.getObfType()
204 )); 205 ));
205 } 206 }
206 } 207 }
207 208
208 // migrate methods 209 // migrate methods
209 for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { 210 for (MethodMapping oldMethodMapping : oldClassMapping.methods()) {
210 if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { 211 if (canMigrate(oldMethodMapping.getObfSignature(), matches)) {
211 newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); 212 newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer));
212 } else { 213 } else {
213 System.out.println(String.format("Can't map method, dropping: %s.%s %s", 214 System.out.println(String.format("Can't map method, dropping: %s.%s %s",
214 oldClassMapping.getDeobfName(), 215 oldClassMapping.getDeobfName(),
215 oldMethodMapping.getDeobfName(), 216 oldMethodMapping.getDeobfName(),
216 oldMethodMapping.getObfSignature() 217 oldMethodMapping.getObfSignature()
217 )); 218 ));
218 } 219 }
219 } 220 }
220 221
221 return newClassMapping; 222 return newClassMapping;
222 } 223 }
223 224
224 private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { 225 private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) {
225 for (Type oldObfType : oldObfSignature.types()) { 226 for (Type oldObfType : oldObfSignature.types()) {
226 if (!canMigrate(oldObfType, classMatches)) { 227 if (!canMigrate(oldObfType, classMatches)) {
227 return false; 228 return false;
228 } 229 }
229 } 230 }
230 return true; 231 return true;
231 } 232 }
232 233
233 private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { 234 private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) {
234 235
235 // non classes can be migrated 236 // non classes can be migrated
236 if (!oldObfType.hasClass()) { 237 if (!oldObfType.hasClass()) {
237 return true; 238 return true;
238 } 239 }
239 240
240 // non obfuscated classes can be migrated 241 // non obfuscated classes can be migrated
241 ClassEntry classEntry = oldObfType.getClassEntry(); 242 ClassEntry classEntry = oldObfType.getClassEntry();
242 if (classEntry.getPackageName() != null) { 243 if (classEntry.getPackageName() != null) {
243 return true; 244 return true;
244 } 245 }
245 246
246 // obfuscated classes with mappings can be migrated 247 // obfuscated classes with mappings can be migrated
247 return classMatches.getUniqueMatches().containsKey(classEntry); 248 return classMatches.getUniqueMatches().containsKey(classEntry);
248 } 249 }
249 250
250 public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) { 251 public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) {
251 252
252 // sort the changes so classes are renamed in the correct order 253 // sort the changes so classes are renamed in the correct order
253 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b 254 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b
254 LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap(); 255 LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap();
255 int numChangesLeft = changes.size(); 256 int numChangesLeft = changes.size();
256 while (!changes.isEmpty()) { 257 while (!changes.isEmpty()) {
257 Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator(); 258 Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator();
258 while (iter.hasNext()) { 259 while (iter.hasNext()) {
259 Map.Entry<ClassEntry, ClassEntry> change = iter.next(); 260 Map.Entry<ClassEntry, ClassEntry> change = iter.next();
260 if (changes.containsKey(change.getValue())) { 261 if (changes.containsKey(change.getValue())) {
261 sortedChanges.put(change.getKey(), change.getValue()); 262 sortedChanges.put(change.getKey(), change.getValue());
262 iter.remove(); 263 iter.remove();
263 } 264 }
264 } 265 }
265 266
266 // did we remove any changes? 267 // did we remove any changes?
267 if (numChangesLeft - changes.size() > 0) { 268 if (numChangesLeft - changes.size() > 0) {
268 // keep going 269 // keep going
269 numChangesLeft = changes.size(); 270 numChangesLeft = changes.size();
270 } else { 271 } else {
271 // can't sort anymore. There must be a loop 272 // can't sort anymore. There must be a loop
272 break; 273 break;
273 } 274 }
274 } 275 }
275 if (!changes.isEmpty()) { 276 if (!changes.isEmpty()) {
276 throw new Error("Unable to sort class changes! There must be a cycle."); 277 throw new Error("Unable to sort class changes! There must be a cycle.");
277 } 278 }
278 279
279 // convert the mappings in the correct class order 280 // convert the mappings in the correct class order
280 for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) { 281 for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) {
281 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); 282 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName());
282 } 283 }
283 } 284 }
284 285
285 public interface Doer<T extends Entry> { 286 public static Doer<FieldEntry> getFieldDoer() {
286 Collection<T> getDroppedEntries(MappingsChecker checker); 287 return new Doer<FieldEntry>() {
287 288
288 Collection<T> getObfEntries(JarIndex jarIndex); 289 @Override
289 290 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) {
290 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping); 291 return checker.getDroppedFieldMappings().keySet();
291 292 }
292 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches); 293
293 294 @Override
294 void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry); 295 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) {
295 296 return jarIndex.getObfFieldEntries();
296 boolean hasObfMember(ClassMapping classMapping, T obfEntry); 297 }
297 298
298 void removeMemberByObf(ClassMapping classMapping, T obfEntry); 299 @Override
299 } 300 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) {
300 301 return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields();
301 public static Doer<FieldEntry> getFieldDoer() { 302 }
302 return new Doer<FieldEntry>() { 303
303 304 @Override
304 @Override 305 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) {
305 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) { 306 Set<FieldEntry> out = Sets.newHashSet();
306 return checker.getDroppedFieldMappings().keySet(); 307 for (FieldEntry obfDestField : obfDestFields) {
307 } 308 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
308 309 if (translatedDestType.equals(obfSourceField.getType())) {
309 @Override 310 out.add(obfDestField);
310 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) { 311 }
311 return jarIndex.getObfFieldEntries(); 312 }
312 } 313 return out;
313 314 }
314 @Override 315
315 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) { 316 @Override
316 return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields(); 317 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) {
317 } 318 FieldMapping fieldMapping = (FieldMapping) memberMapping;
318 319 classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType());
319 @Override 320 }
320 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { 321
321 Set<FieldEntry> out = Sets.newHashSet(); 322 @Override
322 for (FieldEntry obfDestField : obfDestFields) { 323 public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) {
323 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); 324 return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null;
324 if (translatedDestType.equals(obfSourceField.getType())) { 325 }
325 out.add(obfDestField); 326
326 } 327 @Override
327 } 328 public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) {
328 return out; 329 classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType()));
329 } 330 }
330 331 };
331 @Override 332 }
332 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) { 333
333 FieldMapping fieldMapping = (FieldMapping) memberMapping; 334 public static Doer<BehaviorEntry> getMethodDoer() {
334 classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); 335 return new Doer<BehaviorEntry>() {
335 } 336
336 337 @Override
337 @Override 338 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) {
338 public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { 339 return checker.getDroppedMethodMappings().keySet();
339 return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; 340 }
340 } 341
341 342 @Override
342 @Override 343 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) {
343 public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { 344 return jarIndex.getObfBehaviorEntries();
344 classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); 345 }
345 } 346
346 }; 347 @Override
347 } 348 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) {
348 349 return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods();
349 public static Doer<BehaviorEntry> getMethodDoer() { 350 }
350 return new Doer<BehaviorEntry>() { 351
351 352 @Override
352 @Override 353 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) {
353 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) { 354 Set<BehaviorEntry> out = Sets.newHashSet();
354 return checker.getDroppedMethodMappings().keySet(); 355 for (BehaviorEntry obfDestField : obfDestFields) {
355 } 356 // Try to translate the signature
356 357 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse());
357 @Override 358 if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature()))
358 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) { 359 out.add(obfDestField);
359 return jarIndex.getObfBehaviorEntries(); 360 }
360 } 361 return out;
361 362 }
362 @Override 363
363 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) { 364 @Override
364 return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods(); 365 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) {
365 } 366 MethodMapping methodMapping = (MethodMapping) memberMapping;
366 367 classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature());
367 @Override 368 }
368 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { 369
369 Set<BehaviorEntry> out = Sets.newHashSet(); 370 @Override
370 for (BehaviorEntry obfDestField : obfDestFields) { 371 public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) {
371 // Try to translate the signature 372 return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null;
372 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); 373 }
373 if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) 374
374 out.add(obfDestField); 375 @Override
375 } 376 public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) {
376 return out; 377 classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()));
377 } 378 }
378 379 };
379 @Override 380 }
380 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) { 381
381 MethodMapping methodMapping = (MethodMapping) memberMapping; 382 public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) {
382 classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); 383 int sourcePos = 0;
383 } 384 int destPos = 0;
384 385 while (sourceIt.hasNext() && destIt.hasNext()) {
385 @Override 386 try {
386 public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { 387 sourcePos = sourceIt.next();
387 return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; 388 destPos = destIt.next();
388 } 389 if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos))
389 390 return sourcePos;
390 @Override 391 } catch (BadBytecode badBytecode) {
391 public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { 392 // Ignore bad bytecode (it might be a little bit dangerous...)
392 classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); 393 }
393 } 394 }
394 }; 395 if (sourcePos < destPos)
395 } 396 return sourcePos;
396 397 else if (destPos < sourcePos)
397 public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) 398 return destPos;
398 { 399 return sourcePos;
399 int sourcePos = 0; 400 }
400 int destPos = 0; 401
401 while (sourceIt.hasNext() && destIt.hasNext()) 402 public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry,
402 { 403 Set<BehaviorEntry> obfDestEntries) {
403 try 404 try {
404 { 405 // Get the source method with Javassist
405 sourcePos = sourceIt.next(); 406 CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString());
406 destPos = destIt.next(); 407 CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute();
407 if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) 408
408 return sourcePos; 409 // Empty method body, ignore!
409 } catch (BadBytecode badBytecode) 410 if (sourceAttribute == null)
410 { 411 return null;
411 // Ignore bad bytecode (it might be a little bit dangerous...) 412 for (BehaviorEntry desEntry : obfDestEntries) {
412 } 413 try {
413 } 414 CtMethod destCtClassMethod = destCtClass
414 if (sourcePos < destPos) 415 .getMethod(desEntry.getName(), desEntry.getSignature().toString());
415 return sourcePos; 416 CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute();
416 else if (destPos < sourcePos) 417
417 return destPos; 418 // Ignore empty body methods
418 return sourcePos; 419 if (destAttribute == null)
419 } 420 continue;
420 421 CodeIterator destIterator = destAttribute.iterator();
421 public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, 422 int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator);
422 Set<BehaviorEntry> obfDestEntries) 423
423 { 424 // The bytecode is identical to the original method, assuming that the method is correct!
424 try 425 if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1)
425 { 426 return desEntry;
426 // Get the source method with Javassist 427 } catch (NotFoundException e) {
427 CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); 428 e.printStackTrace();
428 CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); 429 }
429 430 }
430 // Empty method body, ignore! 431 } catch (NotFoundException e) {
431 if (sourceAttribute == null) 432 e.printStackTrace();
432 return null; 433 return null;
433 for (BehaviorEntry desEntry : obfDestEntries) 434 }
434 { 435 return null;
435 try 436 }
436 { 437
437 CtMethod destCtClassMethod = destCtClass 438 public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator,
438 .getMethod(desEntry.getName(), desEntry.getSignature().toString()); 439 Mappings destMappings,
439 CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); 440 Deobfuscator sourceDeobfuscator,
440 441 Mappings sourceMappings,
441 // Ignore empty body methods 442 ClassMatches classMatches,
442 if (destAttribute == null) 443 Doer<BehaviorEntry> doer) {
443 continue; 444
444 CodeIterator destIterator = destAttribute.iterator(); 445 MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>();
445 int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); 446
446 447 // unmatched source fields are easy
447 // The bytecode is identical to the original method, assuming that the method is correct! 448 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
448 if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) 449 checker.dropBrokenMappings(destMappings);
449 return desEntry; 450 for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) {
450 } catch (NotFoundException e) 451 BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
451 { 452 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
452 e.printStackTrace(); 453 }
453 } 454
454 } 455 // get matched fields (anything that's left after the checks/drops is matched(
455 } catch (NotFoundException e) 456 for (ClassMapping classMapping : destMappings.classes())
456 { 457 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
457 e.printStackTrace(); 458
458 return null; 459 // get unmatched dest fields
459 } 460 doer.getObfEntries(destDeobfuscator.getJarIndex()).stream()
460 return null; 461 .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry))
461 } 462 .forEach(memberMatches::addUnmatchedDestEntry);
462 463
463 public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, Mappings destMappings, Deobfuscator sourceDeobfuscator, Mappings sourceMappings, ClassMatches classMatches, Doer<BehaviorEntry> doer) { 464 // Apply mappings to deobfuscator
464 465
465 MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>(); 466 // Create type loader
466 467 TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader();
467 // unmatched source fields are easy 468 TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader();
468 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); 469
469 checker.dropBrokenMappings(destMappings); 470 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
470 for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { 471
471 BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); 472 // go through the unmatched source fields and try to pick out the easy matches
472 memberMatches.addUnmatchedSourceEntry(srcObfEntry); 473 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
473 } 474 for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
474 475
475 // get matched fields (anything that's left after the checks/drops is matched( 476 // get the possible dest matches
476 for (ClassMapping classMapping : destMappings.classes()) 477 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
477 collectMatchedFields(memberMatches, classMapping, classMatches, doer); 478
478 479 // filter by type/signature
479 // get unmatched dest fields 480 Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
480 doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() 481
481 .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) 482 if (obfDestEntries.size() == 1) {
482 .forEach(memberMatches::addUnmatchedDestEntry); 483 // make the easy match
483 484 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
484 // Apply mappings to deobfuscator 485 } else if (obfDestEntries.isEmpty()) {
485 486 // no match is possible =(
486 // Create type loader 487 memberMatches.makeSourceUnmatchable(obfSourceEntry, null);
487 TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); 488 } else {
488 TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); 489 // Multiple matches! Scan methods instructions
489 490 CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName());
490 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); 491 CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName());
491 492 BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries);
492 // go through the unmatched source fields and try to pick out the easy matches 493 // the method match correctly, match it on the member mapping!
493 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { 494 if (match != null)
494 for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { 495 memberMatches.makeMatch(obfSourceEntry, match);
495 496 }
496 // get the possible dest matches 497 }
497 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); 498 }
498 499
499 // filter by type/signature 500 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
500 Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); 501 memberMatches.getUnmatchedSourceEntries().size(),
501 502 memberMatches.getUnmatchableSourceEntries().size()
502 if (obfDestEntries.size() == 1) { 503 ));
503 // make the easy match 504
504 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); 505 return memberMatches;
505 } else if (obfDestEntries.isEmpty()) { 506 }
506 // no match is possible =( 507
507 memberMatches.makeSourceUnmatchable(obfSourceEntry, null); 508 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) {
508 } else 509
509 { 510 MemberMatches<T> memberMatches = new MemberMatches<>();
510 // Multiple matches! Scan methods instructions 511
511 CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); 512 // unmatched source fields are easy
512 CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); 513 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
513 BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); 514 checker.dropBrokenMappings(destMappings);
514 // the method match correctly, match it on the member mapping! 515 for (T destObfEntry : doer.getDroppedEntries(checker)) {
515 if (match != null) 516 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
516 memberMatches.makeMatch(obfSourceEntry, match); 517 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
517 } 518 }
518 } 519
519 } 520 // get matched fields (anything that's left after the checks/drops is matched(
520 521 for (ClassMapping classMapping : destMappings.classes()) {
521 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", 522 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
522 memberMatches.getUnmatchedSourceEntries().size(), 523 }
523 memberMatches.getUnmatchableSourceEntries().size() 524
524 )); 525 // get unmatched dest fields
525 526 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) {
526 return memberMatches; 527 if (!memberMatches.isMatchedDestEntry(destEntry)) {
527 } 528 memberMatches.addUnmatchedDestEntry(destEntry);
528 529 }
529 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { 530 }
530 531
531 MemberMatches<T> memberMatches = new MemberMatches<>(); 532 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
532 533
533 // unmatched source fields are easy 534 // go through the unmatched source fields and try to pick out the easy matches
534 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); 535 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
535 checker.dropBrokenMappings(destMappings); 536 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
536 for (T destObfEntry : doer.getDroppedEntries(checker)) { 537
537 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); 538 // get the possible dest matches
538 memberMatches.addUnmatchedSourceEntry(srcObfEntry); 539 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
539 } 540
540 541 // filter by type/signature
541 // get matched fields (anything that's left after the checks/drops is matched( 542 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
542 for (ClassMapping classMapping : destMappings.classes()) { 543
543 collectMatchedFields(memberMatches, classMapping, classMatches, doer); 544 if (obfDestEntries.size() == 1) {
544 } 545 // make the easy match
545 546 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
546 // get unmatched dest fields 547 } else if (obfDestEntries.isEmpty()) {
547 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { 548 // no match is possible =(
548 if (!memberMatches.isMatchedDestEntry(destEntry)) { 549 memberMatches.makeSourceUnmatchable(obfSourceEntry, null);
549 memberMatches.addUnmatchedDestEntry(destEntry); 550 }
550 } 551 }
551 } 552 }
552 553
553 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); 554 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
554 555 memberMatches.getUnmatchedSourceEntries().size(),
555 // go through the unmatched source fields and try to pick out the easy matches 556 memberMatches.getUnmatchableSourceEntries().size()
556 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { 557 ));
557 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { 558
558 559 return memberMatches;
559 // get the possible dest matches 560 }
560 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); 561
561 562 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) {
562 // filter by type/signature 563
563 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); 564 // get the fields for this class
564 565 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) {
565 if (obfDestEntries.size() == 1) { 566 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry());
566 // make the easy match 567 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
567 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); 568 memberMatches.addMatch(srcObfField, destObfField);
568 } else if (obfDestEntries.isEmpty()) { 569 }
569 // no match is possible =( 570
570 memberMatches.makeSourceUnmatchable(obfSourceEntry, null); 571 // recurse
571 } 572 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
572 } 573 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer);
573 } 574 }
574 575 }
575 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", 576
576 memberMatches.getUnmatchedSourceEntries().size(), 577 @SuppressWarnings("unchecked")
577 memberMatches.getUnmatchableSourceEntries().size() 578 private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) {
578 )); 579 if (in instanceof FieldEntry) {
579 580 return (T) new FieldEntry(
580 return memberMatches; 581 map.get(in.getClassEntry()),
581 } 582 in.getName(),
582 583 translate(((FieldEntry) in).getType(), map)
583 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) { 584 );
584 585 } else if (in instanceof MethodEntry) {
585 // get the fields for this class 586 return (T) new MethodEntry(
586 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) { 587 map.get(in.getClassEntry()),
587 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); 588 in.getName(),
588 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); 589 translate(((MethodEntry) in).getSignature(), map)
589 memberMatches.addMatch(srcObfField, destObfField); 590 );
590 } 591 } else if (in instanceof ConstructorEntry) {
591 592 return (T) new ConstructorEntry(
592 // recurse 593 map.get(in.getClassEntry()),
593 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { 594 translate(((ConstructorEntry) in).getSignature(), map)
594 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); 595 );
595 } 596 }
596 } 597 throw new Error("Unhandled entry type: " + in.getClass());
597 598 }
598 @SuppressWarnings("unchecked") 599
599 private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) { 600 private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) {
600 if (in instanceof FieldEntry) { 601 return new Type(type, inClassName ->
601 return (T) new FieldEntry( 602 {
602 map.get(in.getClassEntry()), 603 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
603 in.getName(), 604 if (outClassEntry == null) {
604 translate(((FieldEntry) in).getType(), map) 605 return null;
605 ); 606 }
606 } else if (in instanceof MethodEntry) { 607 return outClassEntry.getName();
607 return (T) new MethodEntry( 608 });
608 map.get(in.getClassEntry()), 609 }
609 in.getName(), 610
610 translate(((MethodEntry) in).getSignature(), map) 611 private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) {
611 ); 612 if (signature == null) {
612 } else if (in instanceof ConstructorEntry) { 613 return null;
613 return (T) new ConstructorEntry( 614 }
614 map.get(in.getClassEntry()), 615 return new Signature(signature, inClassName ->
615 translate(((ConstructorEntry) in).getSignature(), map) 616 {
616 ); 617 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
617 } 618 if (outClassEntry == null) {
618 throw new Error("Unhandled entry type: " + in.getClass()); 619 return null;
619 } 620 }
620 621 return outClassEntry.getName();
621 private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) { 622 });
622 return new Type(type, inClassName -> 623 }
623 { 624
624 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); 625 public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
625 if (outClassEntry == null) { 626 for (ClassMapping classMapping : mappings.classes()) {
626 return null; 627 applyMemberMatches(classMapping, classMatches, memberMatches, doer);
627 } 628 }
628 return outClassEntry.getName(); 629 }
629 }); 630
630 } 631 private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
631 632
632 private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) { 633 // get the classes
633 if (signature == null) { 634 ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName());
634 return null; 635
635 } 636 // make a map of all the renames we need to make
636 return new Signature(signature, inClassName -> 637 Map<T, T> renames = Maps.newHashMap();
637 { 638 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
638 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); 639 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
639 if (outClassEntry == null) { 640 T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches);
640 return null; 641
641 } 642 // but drop the unmatchable things
642 return outClassEntry.getName(); 643 if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) {
643 }); 644 doer.removeMemberByObf(classMapping, obfOldDestEntry);
644 } 645 continue;
645 646 }
646 public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { 647
647 for (ClassMapping classMapping : mappings.classes()) { 648 T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry);
648 applyMemberMatches(classMapping, classMatches, memberMatches, doer); 649 if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) {
649 } 650 renames.put(obfOldDestEntry, obfNewDestEntry);
650 } 651 }
651 652 }
652 private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { 653
653 654 if (!renames.isEmpty()) {
654 // get the classes 655
655 ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); 656 // apply to this class (should never need more than n passes)
656 657 int numRenamesAppliedThisRound;
657 // make a map of all the renames we need to make 658 do {
658 Map<T, T> renames = Maps.newHashMap(); 659 numRenamesAppliedThisRound = 0;
659 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { 660
660 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); 661 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
661 T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); 662 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
662 663 T obfNewDestEntry = renames.get(obfOldDestEntry);
663 // but drop the unmatchable things 664 if (obfNewDestEntry != null) {
664 if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { 665 // make sure this rename won't cause a collision
665 doer.removeMemberByObf(classMapping, obfOldDestEntry); 666 // otherwise, save it for the next round and try again next time
666 continue; 667 if (!doer.hasObfMember(classMapping, obfNewDestEntry)) {
667 } 668 doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry);
668 669 renames.remove(obfOldDestEntry);
669 T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); 670 numRenamesAppliedThisRound++;
670 if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { 671 }
671 renames.put(obfOldDestEntry, obfNewDestEntry); 672 }
672 } 673 }
673 } 674 } while (numRenamesAppliedThisRound > 0);
674 675
675 if (!renames.isEmpty()) { 676 if (!renames.isEmpty()) {
676 677 System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.",
677 // apply to this class (should never need more than n passes) 678 classMapping.getObfFullName(), renames.size()
678 int numRenamesAppliedThisRound; 679 ));
679 do { 680 for (Map.Entry<T, T> entry : renames.entrySet()) {
680 numRenamesAppliedThisRound = 0; 681 System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName()));
681 682 }
682 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { 683 }
683 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); 684 }
684 T obfNewDestEntry = renames.get(obfOldDestEntry); 685
685 if (obfNewDestEntry != null) { 686 // recurse
686 // make sure this rename won't cause a collision 687 for (ClassMapping innerClassMapping : classMapping.innerClasses()) {
687 // otherwise, save it for the next round and try again next time 688 applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer);
688 if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { 689 }
689 doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); 690 }
690 renames.remove(obfOldDestEntry); 691
691 numRenamesAppliedThisRound++; 692 private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) {
692 } 693 return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse());
693 } 694 }
694 } 695
695 } while (numRenamesAppliedThisRound > 0); 696 public interface Doer<T extends Entry> {
696 697 Collection<T> getDroppedEntries(MappingsChecker checker);
697 if (!renames.isEmpty()) { 698
698 System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", 699 Collection<T> getObfEntries(JarIndex jarIndex);
699 classMapping.getObfFullName(), renames.size() 700
700 )); 701 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping);
701 for (Map.Entry<T, T> entry : renames.entrySet()) { 702
702 System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); 703 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches);
703 } 704
704 } 705 void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry);
705 } 706
706 707 boolean hasObfMember(ClassMapping classMapping, T obfEntry);
707 // recurse 708
708 for (ClassMapping innerClassMapping : classMapping.innerClasses()) { 709 void removeMemberByObf(ClassMapping classMapping, T obfEntry);
709 applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); 710 }
710 }
711 }
712
713 private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) {
714 return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse());
715 }
716} 711}