summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz
diff options
context:
space:
mode:
authorGravatar gegy10002018-06-24 12:24:48 +0200
committerGravatar gegy10002018-06-24 12:24:48 +0200
commit8a0e350a04e570074557ff0a53d67e82d54d3005 (patch)
tree621fb8ab321525eadfd95fdd743cfcfee935d4d8 /src/main/java/cuchaz
parentFix array translation in method calls (diff)
downloadenigma-8a0e350a04e570074557ff0a53d67e82d54d3005.tar.gz
enigma-8a0e350a04e570074557ff0a53d67e82d54d3005.tar.xz
enigma-8a0e350a04e570074557ff0a53d67e82d54d3005.zip
Fix method reference and bridge detection
Diffstat (limited to 'src/main/java/cuchaz')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java7
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java102
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java2
4 files changed, 77 insertions, 36 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
index e37c1d0c..9be8378e 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -138,6 +138,13 @@ public class EntryRenamer {
138 renameClassesInThing(renames, methodEntry.getSignature()), 138 renameClassesInThing(renames, methodEntry.getSignature()),
139 methodEntry.getAccess() 139 methodEntry.getAccess()
140 ); 140 );
141 } else if (thing instanceof MethodEntry) {
142 MethodEntry methodEntry = (MethodEntry) thing;
143 return (T) new MethodEntry(
144 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
145 methodEntry.getName(),
146 renameClassesInThing(renames, methodEntry.getDesc())
147 );
141 } else if (thing instanceof LocalVariableEntry) { 148 } else if (thing instanceof LocalVariableEntry) {
142 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing; 149 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing;
143 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName()); 150 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName());
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 3d0e48b4..f63b779a 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -63,7 +63,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
63 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 63 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
64 } 64 }
65 } else { 65 } else {
66 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodReferences(this.reference.context)) { 66 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.reference.context)) {
67 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); 67 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context)));
68 } 68 }
69 } 69 }
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
index 8172deaa..27a8e07b 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -29,11 +29,13 @@ public class JarIndex {
29 private Multimap<ClassEntry, FieldDefEntry> fields; 29 private Multimap<ClassEntry, FieldDefEntry> fields;
30 private Multimap<ClassEntry, MethodDefEntry> methods; 30 private Multimap<ClassEntry, MethodDefEntry> methods;
31 private Multimap<String, MethodDefEntry> methodImplementations; 31 private Multimap<String, MethodDefEntry> methodImplementations;
32 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodReferences; 32 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodsReferencing;
33 private Multimap<MethodEntry, MethodEntry> methodReferences;
33 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences; 34 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences;
34 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; 35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
35 private Map<ClassEntry, ClassEntry> outerClassesByInner; 36 private Map<ClassEntry, ClassEntry> outerClassesByInner;
36 private Map<MethodEntry, MethodEntry> bridgedMethods; 37 private Map<MethodEntry, MethodEntry> bridgedMethods;
38 private Map<MethodEntry, MethodEntry> accessMethods;
37 private Set<MethodEntry> syntheticMethods; 39 private Set<MethodEntry> syntheticMethods;
38 40
39 public JarIndex(ReferencedEntryPool entryPool) { 41 public JarIndex(ReferencedEntryPool entryPool) {
@@ -44,11 +46,13 @@ public class JarIndex {
44 this.fields = HashMultimap.create(); 46 this.fields = HashMultimap.create();
45 this.methods = HashMultimap.create(); 47 this.methods = HashMultimap.create();
46 this.methodImplementations = HashMultimap.create(); 48 this.methodImplementations = HashMultimap.create();
49 this.methodsReferencing = HashMultimap.create();
47 this.methodReferences = HashMultimap.create(); 50 this.methodReferences = HashMultimap.create();
48 this.fieldReferences = HashMultimap.create(); 51 this.fieldReferences = HashMultimap.create();
49 this.innerClassesByOuter = HashMultimap.create(); 52 this.innerClassesByOuter = HashMultimap.create();
50 this.outerClassesByInner = Maps.newHashMap(); 53 this.outerClassesByInner = Maps.newHashMap();
51 this.bridgedMethods = Maps.newHashMap(); 54 this.bridgedMethods = Maps.newHashMap();
55 this.accessMethods = Maps.newHashMap();
52 this.syntheticMethods = Sets.newHashSet(); 56 this.syntheticMethods = Sets.newHashSet();
53 } 57 }
54 58
@@ -63,12 +67,15 @@ public class JarIndex {
63 // step 3: index field, method, constructor references 67 // step 3: index field, method, constructor references
64 jar.visit(node -> node.accept(new IndexReferenceVisitor(this, Opcodes.ASM5))); 68 jar.visit(node -> node.accept(new IndexReferenceVisitor(this, Opcodes.ASM5)));
65 69
66 // step 4: index bridged methods 70 // step 4: index access and bridged methods
67 for (MethodDefEntry methodEntry : methods.values()) { 71 for (MethodDefEntry methodEntry : methods.values()) {
68 // look for bridge and bridged methods 72 // look for access and bridged methods
69 MethodEntry bridgedMethod = findBridgedMethod(methodEntry); 73 MethodEntry accessedMethod = findAccessMethod(methodEntry);
70 if (bridgedMethod != null) { 74 if (accessedMethod != null) {
71 this.bridgedMethods.put(methodEntry, bridgedMethod); 75 this.accessMethods.put(methodEntry, accessedMethod);
76 if (isBridgedMethod(accessedMethod, methodEntry)) {
77 this.bridgedMethods.put(methodEntry, accessedMethod);
78 }
72 } 79 }
73 } 80 }
74 81
@@ -89,6 +96,7 @@ public class JarIndex {
89 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); 96 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
90 this.translationIndex.renameClasses(renames); 97 this.translationIndex.renameClasses(renames);
91 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); 98 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
99 EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing);
92 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences); 100 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences);
93 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); 101 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
94 EntryRenamer.renameClassesInMap(renames, this.access); 102 EntryRenamer.renameClassesInMap(renames, this.access);
@@ -134,7 +142,8 @@ public class JarIndex {
134 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) { 142 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) {
135 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry); 143 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry);
136 } 144 }
137 methodReferences.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry)); 145 methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry));
146 methodReferences.put(callerEntry, referencedMethod);
138 } 147 }
139 148
140 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) { 149 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) {
@@ -151,26 +160,54 @@ public class JarIndex {
151 this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry); 160 this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry);
152 } 161 }
153 162
154 private MethodEntry findBridgedMethod(MethodDefEntry method) { 163 private MethodEntry findAccessMethod(MethodDefEntry method) {
155 164
156 // bridge methods just call another method, cast it to the return desc, and return the result 165 // we want to find all compiler-added methods that directly call another with no processing
157 // let's see if we can detect this scenario
158 166
159 // skip non-synthetic methods 167 // skip non-synthetic methods
160 if (!method.getAccess().isSynthetic()) { 168 if (!method.getAccess().isSynthetic()) {
161 return null; 169 return null;
162 } 170 }
163 171
164 // get all the called methods 172 // get all the methods that we call
165 final Collection<EntryReference<MethodEntry, MethodDefEntry>> referencedMethods = methodReferences.get(method); 173 final Collection<MethodEntry> referencedMethods = methodReferences.get(method);
166 174
167 // is there just one? 175 // is there just one?
168 if (referencedMethods.size() != 1) { 176 if (referencedMethods.size() != 1) {
169 return null; 177 return null;
170 } 178 }
171 179
172 // we have a bridge method! 180 return referencedMethods.stream().findFirst().orElse(null);
173 return referencedMethods.stream().map(ref -> ref.context).filter(Objects::nonNull).findFirst().orElse(null); 181 }
182
183 private boolean isBridgedMethod(MethodEntry called, MethodEntry access) {
184 // Bridged methods will always have the same name as the method they are calling
185 // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed)
186 if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) {
187 return false;
188 }
189
190 TypeDescriptor accessReturn = access.getDesc().getReturnDesc();
191 TypeDescriptor calledReturn = called.getDesc().getReturnDesc();
192 if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) {
193 return false;
194 }
195
196 // Bridged methods will never have the same type as what they are calling
197 if (accessReturn.equals(calledReturn)) {
198 return false;
199 }
200
201 String accessType = accessReturn.toString();
202
203 // If we're casting down from generic type to type-erased Object we're a bridge method
204 if (accessType.equals("Ljava/lang/Object;")) {
205 return true;
206 }
207
208 // Now we need to detect cases where we are being casted down to a higher type bound
209 List<ClassEntry> calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry());
210 return calledAncestry.contains(accessReturn.getTypeEntry());
174 } 211 }
175 212
176 public Set<ClassEntry> getObfClassEntries() { 213 public Set<ClassEntry> getObfClassEntries() {
@@ -302,11 +339,11 @@ public class JarIndex {
302 methodEntries.add(methodEntry); 339 methodEntries.add(methodEntry);
303 } 340 }
304 341
305 // look at bridged methods! 342 // look at access methods!
306 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 343 MethodEntry accessMethod = getAccessMethod(methodEntry);
307 while (bridgedEntry != null) { 344 while (accessMethod != null) {
308 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 345 methodEntries.addAll(getRelatedMethodImplementations(accessMethod));
309 bridgedEntry = getBridgedMethod(bridgedEntry); 346 accessMethod = getAccessMethod(accessMethod);
310 } 347 }
311 348
312 // look at interface methods too 349 // look at interface methods too
@@ -327,11 +364,11 @@ public class JarIndex {
327 methodEntries.add(methodEntry); 364 methodEntries.add(methodEntry);
328 } 365 }
329 366
330 // look at bridged methods! 367 // look at access methods!
331 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 368 MethodEntry accessMethod = getAccessMethod(methodEntry);
332 while (bridgedEntry != null) { 369 while (accessMethod != null) {
333 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 370 methodEntries.addAll(getRelatedMethodImplementations(accessMethod));
334 bridgedEntry = getBridgedMethod(bridgedEntry); 371 accessMethod = getAccessMethod(accessMethod);
335 } 372 }
336 373
337 // recurse 374 // recurse
@@ -355,19 +392,12 @@ public class JarIndex {
355 return fieldEntries; 392 return fieldEntries;
356 } 393 }
357 394
358 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodReferences(MethodEntry methodEntry) { 395 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry) {
359 return this.methodReferences.get(methodEntry); 396 return this.methodsReferencing.get(methodEntry);
360 } 397 }
361 398
362 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) { 399 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) {
363 // linear search is fast enough for now 400 return this.methodReferences.get(methodEntry);
364 Set<MethodEntry> behaviorEntries = Sets.newHashSet();
365 for (EntryReference<MethodEntry, MethodDefEntry> reference : this.methodReferences.values()) {
366 if (reference.context == methodEntry) {
367 behaviorEntries.add(reference.entry);
368 }
369 }
370 return behaviorEntries;
371 } 401 }
372 402
373 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { 403 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) {
@@ -461,6 +491,10 @@ public class JarIndex {
461 return this.bridgedMethods.get(bridgeMethodEntry); 491 return this.bridgedMethods.get(bridgeMethodEntry);
462 } 492 }
463 493
494 public MethodEntry getAccessMethod(MethodEntry bridgeMethodEntry) {
495 return this.accessMethods.get(bridgeMethodEntry);
496 }
497
464 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { 498 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) {
465 499
466 // build class chain in inner-to-outer order 500 // build class chain in inner-to-outer order
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
index 15ae5153..76c73c15 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -64,7 +64,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
64 64
65 public void load(JarIndex index, boolean recurse) { 65 public void load(JarIndex index, boolean recurse) {
66 // get all the child nodes 66 // get all the child nodes
67 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodReferences(this.entry)) { 67 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry)) {
68 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 68 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
69 } 69 }
70 70