summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/convert/MappingsConverter.java
diff options
context:
space:
mode:
authorGravatar lclc982016-06-30 00:49:21 +1000
committerGravatar GitHub2016-06-30 00:49:21 +1000
commit4be005617b3b8c3578cca07c5d085d12916f0d1d (patch)
treedb163431f38703e26da417ef05eaea2b27a498b9 /src/cuchaz/enigma/convert/MappingsConverter.java
parentSome small changes to fix idea importing (diff)
downloadenigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.tar.gz
enigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.tar.xz
enigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.zip
Json format (#2)
* Added new format * Fixed bug * Updated Version
Diffstat (limited to 'src/cuchaz/enigma/convert/MappingsConverter.java')
-rw-r--r--src/cuchaz/enigma/convert/MappingsConverter.java602
1 files changed, 0 insertions, 602 deletions
diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java
deleted file mode 100644
index 958a17c..0000000
--- a/src/cuchaz/enigma/convert/MappingsConverter.java
+++ /dev/null
@@ -1,602 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.Iterator;
17import java.util.LinkedHashMap;
18import java.util.List;
19import java.util.Map;
20import java.util.Set;
21import java.util.jar.JarFile;
22
23import com.google.common.collect.BiMap;
24import com.google.common.collect.HashMultimap;
25import com.google.common.collect.Lists;
26import com.google.common.collect.Maps;
27import com.google.common.collect.Multimap;
28import com.google.common.collect.Sets;
29
30import cuchaz.enigma.Constants;
31import cuchaz.enigma.Deobfuscator;
32import cuchaz.enigma.analysis.JarIndex;
33import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
34import cuchaz.enigma.mapping.BehaviorEntry;
35import cuchaz.enigma.mapping.ClassEntry;
36import cuchaz.enigma.mapping.ClassMapping;
37import cuchaz.enigma.mapping.ClassNameReplacer;
38import cuchaz.enigma.mapping.ConstructorEntry;
39import cuchaz.enigma.mapping.Entry;
40import cuchaz.enigma.mapping.FieldEntry;
41import cuchaz.enigma.mapping.FieldMapping;
42import cuchaz.enigma.mapping.Mappings;
43import cuchaz.enigma.mapping.MappingsChecker;
44import cuchaz.enigma.mapping.MemberMapping;
45import cuchaz.enigma.mapping.MethodEntry;
46import cuchaz.enigma.mapping.MethodMapping;
47import cuchaz.enigma.mapping.Signature;
48import cuchaz.enigma.mapping.Type;
49
50public class MappingsConverter {
51
52 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) {
53
54 // index jars
55 System.out.println("Indexing source jar...");
56 JarIndex sourceIndex = new JarIndex();
57 sourceIndex.indexJar(sourceJar, false);
58 System.out.println("Indexing dest jar...");
59 JarIndex destIndex = new JarIndex();
60 destIndex.indexJar(destJar, false);
61
62 // compute the matching
63 ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null);
64 return new ClassMatches(matching.matches());
65 }
66
67 public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry,ClassEntry> knownMatches) {
68
69 System.out.println("Iteratively matching classes");
70
71 ClassMatching lastMatching = null;
72 int round = 0;
73 SidedClassNamer sourceNamer = null;
74 SidedClassNamer destNamer = null;
75 for (boolean useReferences : Arrays.asList(false, true)) {
76
77 int numUniqueMatchesLastTime = 0;
78 if (lastMatching != null) {
79 numUniqueMatchesLastTime = lastMatching.uniqueMatches().size();
80 }
81
82 while (true) {
83
84 System.out.println("Round " + (++round) + "...");
85
86 // init the matching with identity settings
87 ClassMatching matching = new ClassMatching(
88 new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences),
89 new ClassIdentifier(destJar, destIndex, destNamer, useReferences)
90 );
91
92 if (knownMatches != null) {
93 matching.addKnownMatches(knownMatches);
94 }
95
96 if (lastMatching == null) {
97 // search all classes
98 matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries());
99 } else {
100 // we already know about these matches from last time
101 matching.addKnownMatches(lastMatching.uniqueMatches());
102
103 // search unmatched and ambiguously-matched classes
104 matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses());
105 for (ClassMatch match : lastMatching.ambiguousMatches()) {
106 matching.match(match.sourceClasses, match.destClasses);
107 }
108 }
109 System.out.println(matching);
110 BiMap<ClassEntry,ClassEntry> uniqueMatches = matching.uniqueMatches();
111
112 // did we match anything new this time?
113 if (uniqueMatches.size() > numUniqueMatchesLastTime) {
114 numUniqueMatchesLastTime = uniqueMatches.size();
115 lastMatching = matching;
116 } else {
117 break;
118 }
119
120 // update the namers
121 ClassNamer namer = new ClassNamer(uniqueMatches);
122 sourceNamer = namer.getSourceNamer();
123 destNamer = namer.getDestNamer();
124 }
125 }
126
127 return lastMatching;
128 }
129
130 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
131
132 // sort the unique matches by size of inner class chain
133 Multimap<Integer,java.util.Map.Entry<ClassEntry,ClassEntry>> matchesByDestChainSize = HashMultimap.create();
134 for (java.util.Map.Entry<ClassEntry,ClassEntry> match : matches.getUniqueMatches().entrySet()) {
135 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size();
136 matchesByDestChainSize.put(chainSize, match);
137 }
138
139 // build the mappings (in order of small-to-large inner chains)
140 Mappings newMappings = new Mappings();
141 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet());
142 Collections.sort(chainSizes);
143 for (int chainSize : chainSizes) {
144 for (java.util.Map.Entry<ClassEntry,ClassEntry> match : matchesByDestChainSize.get(chainSize)) {
145
146 // get class info
147 ClassEntry obfSourceClassEntry = match.getKey();
148 ClassEntry obfDestClassEntry = match.getValue();
149 List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry);
150
151 ClassMapping sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry);
152 if (sourceMapping == null) {
153 // if this class was never deobfuscated, don't try to match it
154 continue;
155 }
156
157 // find out where to make the dest class mapping
158 if (destClassChain.size() == 1) {
159 // not an inner class, add directly to mappings
160 newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false));
161 } else {
162 // inner class, find the outer class mapping
163 ClassMapping destMapping = null;
164 for (int i=0; i<destClassChain.size()-1; i++) {
165 ClassEntry destChainClassEntry = destClassChain.get(i);
166 if (destMapping == null) {
167 destMapping = newMappings.getClassByObf(destChainClassEntry);
168 if (destMapping == null) {
169 destMapping = new ClassMapping(destChainClassEntry.getName());
170 newMappings.addClassMapping(destMapping);
171 }
172 } else {
173 destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName());
174 if (destMapping == null) {
175 destMapping = new ClassMapping(destChainClassEntry.getName());
176 destMapping.addInnerClassMapping(destMapping);
177 }
178 }
179 }
180 destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true));
181 }
182 }
183 }
184 return newMappings;
185 }
186
187 private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) {
188
189 ClassNameReplacer replacer = new ClassNameReplacer() {
190 @Override
191 public String replace(String className) {
192 ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className));
193 if (newClassEntry != null) {
194 return newClassEntry.getName();
195 }
196 return null;
197 }
198 };
199
200 ClassMapping newClassMapping;
201 String deobfName = oldClassMapping.getDeobfName();
202 if (deobfName != null) {
203 if (useSimpleName) {
204 deobfName = new ClassEntry(deobfName).getSimpleName();
205 }
206 newClassMapping = new ClassMapping(newObfClass.getName(), deobfName);
207 } else {
208 newClassMapping = new ClassMapping(newObfClass.getName());
209 }
210
211 // migrate fields
212 for (FieldMapping oldFieldMapping : oldClassMapping.fields()) {
213 if (canMigrate(oldFieldMapping.getObfType(), matches)) {
214 newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer));
215 } else {
216 System.out.println(String.format("Can't map field, dropping: %s.%s %s",
217 oldClassMapping.getDeobfName(),
218 oldFieldMapping.getDeobfName(),
219 oldFieldMapping.getObfType()
220 ));
221 }
222 }
223
224 // migrate methods
225 for (MethodMapping oldMethodMapping : oldClassMapping.methods()) {
226 if (canMigrate(oldMethodMapping.getObfSignature(), matches)) {
227 newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer));
228 } else {
229 System.out.println(String.format("Can't map method, dropping: %s.%s %s",
230 oldClassMapping.getDeobfName(),
231 oldMethodMapping.getDeobfName(),
232 oldMethodMapping.getObfSignature()
233 ));
234 }
235 }
236
237 return newClassMapping;
238 }
239
240 private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) {
241 for (Type oldObfType : oldObfSignature.types()) {
242 if (!canMigrate(oldObfType, classMatches)) {
243 return false;
244 }
245 }
246 return true;
247 }
248
249 private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) {
250
251 // non classes can be migrated
252 if (!oldObfType.hasClass()) {
253 return true;
254 }
255
256 // non obfuscated classes can be migrated
257 ClassEntry classEntry = oldObfType.getClassEntry();
258 if (!classEntry.getPackageName().equals(Constants.NonePackage)) {
259 return true;
260 }
261
262 // obfuscated classes with mappings can be migrated
263 return classMatches.getUniqueMatches().containsKey(classEntry);
264 }
265
266 public static void convertMappings(Mappings mappings, BiMap<ClassEntry,ClassEntry> changes) {
267
268 // sort the changes so classes are renamed in the correct order
269 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b
270 LinkedHashMap<ClassEntry,ClassEntry> sortedChanges = Maps.newLinkedHashMap();
271 int numChangesLeft = changes.size();
272 while (!changes.isEmpty()) {
273 Iterator<Map.Entry<ClassEntry,ClassEntry>> iter = changes.entrySet().iterator();
274 while (iter.hasNext()) {
275 Map.Entry<ClassEntry,ClassEntry> change = iter.next();
276 if (changes.containsKey(change.getValue())) {
277 sortedChanges.put(change.getKey(), change.getValue());
278 iter.remove();
279 }
280 }
281
282 // did we remove any changes?
283 if (numChangesLeft - changes.size() > 0) {
284 // keep going
285 numChangesLeft = changes.size();
286 } else {
287 // can't sort anymore. There must be a loop
288 break;
289 }
290 }
291 if (!changes.isEmpty()) {
292 throw new Error("Unable to sort class changes! There must be a cycle.");
293 }
294
295 // convert the mappings in the correct class order
296 for (Map.Entry<ClassEntry,ClassEntry> entry : sortedChanges.entrySet()) {
297 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName());
298 }
299 }
300
301 public static interface Doer<T extends Entry> {
302 Collection<T> getDroppedEntries(MappingsChecker checker);
303 Collection<T> getObfEntries(JarIndex jarIndex);
304 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping);
305 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches);
306 void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry);
307 boolean hasObfMember(ClassMapping classMapping, T obfEntry);
308 void removeMemberByObf(ClassMapping classMapping, T obfEntry);
309 }
310
311 public static Doer<FieldEntry> getFieldDoer() {
312 return new Doer<FieldEntry>() {
313
314 @Override
315 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) {
316 return checker.getDroppedFieldMappings().keySet();
317 }
318
319 @Override
320 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) {
321 return jarIndex.getObfFieldEntries();
322 }
323
324 @Override
325 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) {
326 return (Collection<? extends MemberMapping<FieldEntry>>)destClassMapping.fields();
327 }
328
329 @Override
330 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) {
331 Set<FieldEntry> out = Sets.newHashSet();
332 for (FieldEntry obfDestField : obfDestFields) {
333 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
334 if (translatedDestType.equals(obfSourceField.getType())) {
335 out.add(obfDestField);
336 }
337 }
338 return out;
339 }
340
341 @Override
342 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) {
343 FieldMapping fieldMapping = (FieldMapping)memberMapping;
344 classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType());
345 }
346
347 @Override
348 public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) {
349 return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null;
350 }
351
352 @Override
353 public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) {
354 classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType()));
355 }
356 };
357 }
358
359 public static Doer<BehaviorEntry> getMethodDoer() {
360 return new Doer<BehaviorEntry>() {
361
362 @Override
363 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) {
364 return checker.getDroppedMethodMappings().keySet();
365 }
366
367 @Override
368 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) {
369 return jarIndex.getObfBehaviorEntries();
370 }
371
372 @Override
373 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) {
374 return (Collection<? extends MemberMapping<BehaviorEntry>>)destClassMapping.methods();
375 }
376
377 @Override
378 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) {
379 Set<BehaviorEntry> out = Sets.newHashSet();
380 for (BehaviorEntry obfDestField : obfDestFields) {
381 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse());
382 if (translatedDestSignature == null && obfSourceField.getSignature() == null) {
383 out.add(obfDestField);
384 } else if (translatedDestSignature == null || obfSourceField.getSignature() == null) {
385 // skip it
386 } else if (translatedDestSignature.equals(obfSourceField.getSignature())) {
387 out.add(obfDestField);
388 }
389 }
390 return out;
391 }
392
393 @Override
394 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) {
395 MethodMapping methodMapping = (MethodMapping)memberMapping;
396 classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature());
397 }
398
399 @Override
400 public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) {
401 return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null;
402 }
403
404 @Override
405 public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) {
406 classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()));
407 }
408 };
409 }
410
411 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) {
412
413 MemberMatches<T> memberMatches = new MemberMatches<T>();
414
415 // unmatched source fields are easy
416 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
417 checker.dropBrokenMappings(destMappings);
418 for (T destObfEntry : doer.getDroppedEntries(checker)) {
419 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
420 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
421 }
422
423 // get matched fields (anything that's left after the checks/drops is matched(
424 for (ClassMapping classMapping : destMappings.classes()) {
425 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
426 }
427
428 // get unmatched dest fields
429 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) {
430 if (!memberMatches.isMatchedDestEntry(destEntry)) {
431 memberMatches.addUnmatchedDestEntry(destEntry);
432 }
433 }
434
435 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
436
437 // go through the unmatched source fields and try to pick out the easy matches
438 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
439 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
440
441 // get the possible dest matches
442 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
443
444 // filter by type/signature
445 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
446
447 if (obfDestEntries.size() == 1) {
448 // make the easy match
449 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
450 } else if (obfDestEntries.isEmpty()) {
451 // no match is possible =(
452 memberMatches.makeSourceUnmatchable(obfSourceEntry);
453 }
454 }
455 }
456
457 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
458 memberMatches.getUnmatchedSourceEntries().size(),
459 memberMatches.getUnmatchableSourceEntries().size()
460 ));
461
462 return memberMatches;
463 }
464
465 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) {
466
467 // get the fields for this class
468 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) {
469 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry());
470 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
471 memberMatches.addMatch(srcObfField, destObfField);
472 }
473
474 // recurse
475 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
476 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer);
477 }
478 }
479
480 @SuppressWarnings("unchecked")
481 private static <T extends Entry> T translate(T in, BiMap<ClassEntry,ClassEntry> map) {
482 if (in instanceof FieldEntry) {
483 return (T)new FieldEntry(
484 map.get(in.getClassEntry()),
485 in.getName(),
486 translate(((FieldEntry)in).getType(), map)
487 );
488 } else if (in instanceof MethodEntry) {
489 return (T)new MethodEntry(
490 map.get(in.getClassEntry()),
491 in.getName(),
492 translate(((MethodEntry)in).getSignature(), map)
493 );
494 } else if (in instanceof ConstructorEntry) {
495 return (T)new ConstructorEntry(
496 map.get(in.getClassEntry()),
497 translate(((ConstructorEntry)in).getSignature(), map)
498 );
499 }
500 throw new Error("Unhandled entry type: " + in.getClass());
501 }
502
503 private static Type translate(Type type, final BiMap<ClassEntry,ClassEntry> map) {
504 return new Type(type, new ClassNameReplacer() {
505 @Override
506 public String replace(String inClassName) {
507 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
508 if (outClassEntry == null) {
509 return null;
510 }
511 return outClassEntry.getName();
512 }
513 });
514 }
515
516 private static Signature translate(Signature signature, final BiMap<ClassEntry,ClassEntry> map) {
517 if (signature == null) {
518 return null;
519 }
520 return new Signature(signature, new ClassNameReplacer() {
521 @Override
522 public String replace(String inClassName) {
523 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
524 if (outClassEntry == null) {
525 return null;
526 }
527 return outClassEntry.getName();
528 }
529 });
530 }
531
532 public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
533 for (ClassMapping classMapping : mappings.classes()) {
534 applyMemberMatches(classMapping, classMatches, memberMatches, doer);
535 }
536 }
537
538 private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
539
540 // get the classes
541 ClassEntry obfDestClass = classMapping.getObfEntry();
542
543 // make a map of all the renames we need to make
544 Map<T,T> renames = Maps.newHashMap();
545 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
546 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
547 T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches);
548
549 // but drop the unmatchable things
550 if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) {
551 doer.removeMemberByObf(classMapping, obfOldDestEntry);
552 continue;
553 }
554
555 T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry);
556 if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) {
557 renames.put(obfOldDestEntry, obfNewDestEntry);
558 }
559 }
560
561 if (!renames.isEmpty()) {
562
563 // apply to this class (should never need more than n passes)
564 int numRenamesAppliedThisRound;
565 do {
566 numRenamesAppliedThisRound = 0;
567
568 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
569 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
570 T obfNewDestEntry = renames.get(obfOldDestEntry);
571 if (obfNewDestEntry != null) {
572 // make sure this rename won't cause a collision
573 // otherwise, save it for the next round and try again next time
574 if (!doer.hasObfMember(classMapping, obfNewDestEntry)) {
575 doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry);
576 renames.remove(obfOldDestEntry);
577 numRenamesAppliedThisRound++;
578 }
579 }
580 }
581 } while(numRenamesAppliedThisRound > 0);
582
583 if (!renames.isEmpty()) {
584 System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.",
585 classMapping.getObfFullName(), renames.size()
586 ));
587 for (Map.Entry<T,T> entry : renames.entrySet()) {
588 System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName()));
589 }
590 }
591 }
592
593 // recurse
594 for (ClassMapping innerClassMapping : classMapping.innerClasses()) {
595 applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer);
596 }
597 }
598
599 private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) {
600 return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse());
601 }
602}