summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar Gegy2019-02-19 21:00:43 +0200
committerGravatar GitHub2019-02-19 21:00:43 +0200
commitc0b0388aae76d39d780c2b4ad2531feb9d633e62 (patch)
tree92ed3273d7961c2309c732d39cebd99e687a1532 /src/main/java/cuchaz/enigma/analysis
parentAdded Basic Search (#102) (diff)
downloadenigma-fork-c0b0388aae76d39d780c2b4ad2531feb9d633e62.tar.gz
enigma-fork-c0b0388aae76d39d780c2b4ad2531feb9d633e62.tar.xz
enigma-fork-c0b0388aae76d39d780c2b4ad2531feb9d633e62.zip
Bridge Method Fixes (#111)
* Detect synthetic bridges not marked as bridges, and add back flags to produced bytecode * Remove debug check * Remove more test code * Remove unneeded change to `TranslationClassVisitor`
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java89
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java36
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java4
3 files changed, 113 insertions, 16 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
index 8f6bd46..de2ba1e 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -1,22 +1,32 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import com.google.common.collect.Maps; 3import com.google.common.collect.Maps;
4import com.google.common.collect.Sets;
4import cuchaz.enigma.translation.mapping.EntryResolver; 5import cuchaz.enigma.translation.mapping.EntryResolver;
5import cuchaz.enigma.translation.representation.AccessFlags; 6import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.MethodDescriptor;
8import cuchaz.enigma.translation.representation.TypeDescriptor;
9import cuchaz.enigma.translation.representation.entry.ClassEntry;
10import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
6import cuchaz.enigma.translation.representation.entry.MethodEntry; 11import cuchaz.enigma.translation.representation.entry.MethodEntry;
7 12
8import javax.annotation.Nullable; 13import javax.annotation.Nullable;
9import java.util.Collection; 14import java.util.Collection;
15import java.util.List;
10import java.util.Map; 16import java.util.Map;
17import java.util.Set;
11 18
12public class BridgeMethodIndex implements JarIndexer { 19public class BridgeMethodIndex implements JarIndexer {
13 private final EntryIndex entryIndex; 20 private final EntryIndex entryIndex;
21 private final InheritanceIndex inheritanceIndex;
14 private final ReferenceIndex referenceIndex; 22 private final ReferenceIndex referenceIndex;
15 23
16 private Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap(); 24 private final Set<MethodEntry> bridgeMethods = Sets.newHashSet();
25 private final Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap();
17 26
18 public BridgeMethodIndex(EntryIndex entryIndex, ReferenceIndex referenceIndex) { 27 public BridgeMethodIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) {
19 this.entryIndex = entryIndex; 28 this.entryIndex = entryIndex;
29 this.inheritanceIndex = inheritanceIndex;
20 this.referenceIndex = referenceIndex; 30 this.referenceIndex = referenceIndex;
21 } 31 }
22 32
@@ -24,21 +34,26 @@ public class BridgeMethodIndex implements JarIndexer {
24 public void processIndex(EntryResolver resolver) { 34 public void processIndex(EntryResolver resolver) {
25 // look for access and bridged methods 35 // look for access and bridged methods
26 for (MethodEntry methodEntry : entryIndex.getMethods()) { 36 for (MethodEntry methodEntry : entryIndex.getMethods()) {
27 AccessFlags access = entryIndex.getMethodAccess(methodEntry); 37 MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry;
38
39 AccessFlags access = methodDefEntry.getAccess();
28 if (access == null || !access.isSynthetic()) { 40 if (access == null || !access.isSynthetic()) {
29 continue; 41 continue;
30 } 42 }
31 43
32 indexSyntheticMethod(methodEntry, access); 44 indexSyntheticMethod(methodDefEntry, access);
33 } 45 }
34 } 46 }
35 47
36 private void indexSyntheticMethod(MethodEntry syntheticMethod, AccessFlags access) { 48 private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) {
37 if (access.isBridge()) { 49 MethodEntry accessedMethod = findAccessMethod(syntheticMethod);
38 MethodEntry accessedMethod = findAccessMethod(syntheticMethod); 50 if (accessedMethod == null) {
39 if (accessedMethod != null) { 51 return;
40 accessedToBridge.put(accessedMethod, syntheticMethod); 52 }
41 } 53
54 if (access.isBridge() || isPotentialBridge(syntheticMethod, accessedMethod)) {
55 bridgeMethods.add(syntheticMethod);
56 accessedToBridge.put(accessedMethod, syntheticMethod);
42 } 57 }
43 } 58 }
44 59
@@ -56,6 +71,60 @@ public class BridgeMethodIndex implements JarIndexer {
56 return referencedMethods.stream().findFirst().orElse(null); 71 return referencedMethods.stream().findFirst().orElse(null);
57 } 72 }
58 73
74 private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry accessedMethod) {
75 // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited
76 AccessFlags bridgeAccess = bridgeMethod.getAccess();
77 if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) {
78 return false;
79 }
80
81 MethodDescriptor bridgeDesc = bridgeMethod.getDesc();
82 MethodDescriptor accessedDesc = accessedMethod.getDesc();
83 List<TypeDescriptor> bridgeArguments = bridgeDesc.getArgumentDescs();
84 List<TypeDescriptor> accessedArguments = accessedDesc.getArgumentDescs();
85
86 // A bridge method will always have the same number of arguments
87 if (bridgeArguments.size() != accessedArguments.size()) {
88 return false;
89 }
90
91 // Check that all argument types are bridge-compatible
92 for (int i = 0; i < bridgeArguments.size(); i++) {
93 if (!areTypesBridgeCompatible(bridgeArguments.get(i), accessedArguments.get(i))) {
94 return false;
95 }
96 }
97
98 // Check that the return type is bridge-compatible
99 return areTypesBridgeCompatible(bridgeDesc.getReturnDesc(), accessedDesc.getReturnDesc());
100 }
101
102 private boolean areTypesBridgeCompatible(TypeDescriptor bridgeDesc, TypeDescriptor accessedDesc) {
103 if (bridgeDesc.equals(accessedDesc)) {
104 return true;
105 }
106
107 // Either the descs will be equal, or they are both types and different through a generic
108 if (bridgeDesc.isType() && accessedDesc.isType()) {
109 ClassEntry bridgeType = bridgeDesc.getTypeEntry();
110 ClassEntry accessedType = accessedDesc.getTypeEntry();
111
112 // If the given types are completely unrelated to each other, this can't be bridge compatible
113 InheritanceIndex.Relation relation = inheritanceIndex.computeClassRelation(accessedType, bridgeType);
114 return relation != InheritanceIndex.Relation.UNRELATED;
115 }
116
117 return false;
118 }
119
120 public boolean isBridgeMethod(MethodEntry entry) {
121 return bridgeMethods.contains(entry);
122 }
123
124 public boolean isAccessedByBridge(MethodEntry entry) {
125 return accessedToBridge.containsKey(entry);
126 }
127
59 @Nullable 128 @Nullable
60 public MethodEntry getBridgeFromAccessed(MethodEntry entry) { 129 public MethodEntry getBridgeFromAccessed(MethodEntry entry) {
61 return accessedToBridge.get(entry); 130 return accessedToBridge.get(entry);
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
index 17bed54..1b8d9a8 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -22,13 +22,23 @@ import java.util.LinkedList;
22import java.util.Set; 22import java.util.Set;
23 23
24public class InheritanceIndex implements JarIndexer { 24public class InheritanceIndex implements JarIndexer {
25 private final EntryIndex entryIndex;
26
25 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); 27 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create();
26 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); 28 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create();
27 29
30 public InheritanceIndex(EntryIndex entryIndex) {
31 this.entryIndex = entryIndex;
32 }
33
28 @Override 34 @Override
29 public void indexClass(ClassDefEntry classEntry) { 35 public void indexClass(ClassDefEntry classEntry) {
36 if (classEntry.isJre()) {
37 return;
38 }
39
30 ClassEntry superClass = classEntry.getSuperClass(); 40 ClassEntry superClass = classEntry.getSuperClass();
31 if (superClass != null) { 41 if (superClass != null && !superClass.getName().equals("java/lang/Object")) {
32 indexParent(classEntry, superClass); 42 indexParent(classEntry, superClass);
33 } 43 }
34 44
@@ -38,9 +48,6 @@ public class InheritanceIndex implements JarIndexer {
38 } 48 }
39 49
40 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) { 50 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) {
41 if (childEntry.isJre() || parentEntry.isJre()) {
42 return;
43 }
44 classParents.put(childEntry, parentEntry); 51 classParents.put(childEntry, parentEntry);
45 classChildren.put(parentEntry, childEntry); 52 classChildren.put(parentEntry, childEntry);
46 } 53 }
@@ -70,6 +77,21 @@ public class InheritanceIndex implements JarIndexer {
70 return ancestors; 77 return ancestors;
71 } 78 }
72 79
80 public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) {
81 if (potentialAncestor.getName().equals("java/lang/Object")) return Relation.RELATED;
82 if (!entryIndex.hasClass(classEntry)) return Relation.UNKNOWN;
83
84 for (ClassEntry ancestor : getAncestors(classEntry)) {
85 if (potentialAncestor.equals(ancestor)) {
86 return Relation.RELATED;
87 } else if (!entryIndex.hasClass(ancestor)) {
88 return Relation.UNKNOWN;
89 }
90 }
91
92 return Relation.UNRELATED;
93 }
94
73 public boolean isParent(ClassEntry classEntry) { 95 public boolean isParent(ClassEntry classEntry) {
74 return classChildren.containsKey(classEntry); 96 return classChildren.containsKey(classEntry);
75 } 97 }
@@ -78,4 +100,10 @@ public class InheritanceIndex implements JarIndexer {
78 Collection<ClassEntry> parents = classParents.get(classEntry); 100 Collection<ClassEntry> parents = classParents.get(classEntry);
79 return parents != null && !parents.isEmpty(); 101 return parents != null && !parents.isEmpty();
80 } 102 }
103
104 public enum Relation {
105 RELATED,
106 UNRELATED,
107 UNKNOWN
108 }
81} 109}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
index 9b21cba..cb58fce 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -46,9 +46,9 @@ public class JarIndex implements JarIndexer {
46 46
47 public static JarIndex empty() { 47 public static JarIndex empty() {
48 EntryIndex entryIndex = new EntryIndex(); 48 EntryIndex entryIndex = new EntryIndex();
49 InheritanceIndex inheritanceIndex = new InheritanceIndex(); 49 InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex);
50 ReferenceIndex referenceIndex = new ReferenceIndex(); 50 ReferenceIndex referenceIndex = new ReferenceIndex();
51 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, referenceIndex); 51 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex);
52 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); 52 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
53 } 53 }
54 54