summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Thiakil2020-07-19 16:42:17 +0800
committerGravatar Thiakil2020-07-19 17:03:26 +0800
commit08ed439aa1deaeefe4145d5c00a0f79e15f62751 (patch)
tree8a6c0db8b81d871517fd2ffa22a32dc12c9c9c2c
parentadd MappingValidator based test (diff)
downloadenigma-08ed439aa1deaeefe4145d5c00a0f79e15f62751.tar.gz
enigma-08ed439aa1deaeefe4145d5c00a0f79e15f62751.tar.xz
enigma-08ed439aa1deaeefe4145d5c00a0f79e15f62751.zip
allow name clashes to pass where javac would accept them
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java116
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java21
2 files changed, 111 insertions, 26 deletions
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
index f9f3b88e..134c05ce 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
@@ -1,15 +1,19 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.Collection; 3import java.util.Collection;
4import java.util.HashSet; 4import java.util.Objects;
5import java.util.Set;
5import java.util.stream.Collectors; 6import java.util.stream.Collectors;
6 7
7import cuchaz.enigma.analysis.index.InheritanceIndex; 8import cuchaz.enigma.analysis.index.InheritanceIndex;
8import cuchaz.enigma.analysis.index.JarIndex; 9import cuchaz.enigma.analysis.index.JarIndex;
9import cuchaz.enigma.translation.Translator; 10import cuchaz.enigma.translation.Translator;
10import cuchaz.enigma.translation.mapping.tree.EntryTree; 11import cuchaz.enigma.translation.mapping.tree.EntryTree;
12import cuchaz.enigma.translation.representation.AccessFlags;
11import cuchaz.enigma.translation.representation.entry.ClassEntry; 13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.DefEntry;
12import cuchaz.enigma.translation.representation.entry.Entry; 15import cuchaz.enigma.translation.representation.entry.Entry;
16import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.utils.validation.Message; 17import cuchaz.enigma.utils.validation.Message;
14import cuchaz.enigma.utils.validation.ValidationContext; 18import cuchaz.enigma.utils.validation.ValidationContext;
15 19
@@ -35,18 +39,16 @@ public class MappingValidator {
35 39
36 private void validateUnique(ValidationContext vc, Entry<?> entry, String name) { 40 private void validateUnique(ValidationContext vc, Entry<?> entry, String name) {
37 ClassEntry containingClass = entry.getContainingClass(); 41 ClassEntry containingClass = entry.getContainingClass();
38 Collection<ClassEntry> relatedClasses = getRelatedClasses(containingClass); 42 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
39
40 for (ClassEntry relatedClass : relatedClasses) {
41 Entry<?> relatedEntry = entry.replaceAncestor(containingClass, relatedClass);
42 Entry<?> translatedEntry = deobfuscator.translate(relatedEntry);
43
44 Collection<Entry<?>> translatedSiblings = obfToDeobf.getSiblings(relatedEntry).stream()
45 .map(deobfuscator::translate)
46 .collect(Collectors.toList());
47 43
48 if (!isUnique(translatedEntry, translatedSiblings, name)) { 44 //Step 1, check it's unique within its own siblings
49 Entry<?> parent = translatedEntry.getParent(); 45 Collection<Entry<?>> directTranslatedSiblings = obfToDeobf.getSiblings(entry).stream()
46 .map(deobfuscator::translate)
47 .collect(Collectors.toList());
48 for (Entry<?> sibling : directTranslatedSiblings) {
49 if (entry.canConflictWith(sibling) && sibling.getName().equals(name) && isSynthetic(entry) == isSynthetic(sibling)) {
50 // allow clash if one is synthetic and the other is not
51 Entry<?> parent = entry.getParent();
50 if (parent != null) { 52 if (parent != null) {
51 vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent); 53 vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent);
52 } else { 54 } else {
@@ -54,26 +56,88 @@ public class MappingValidator {
54 } 56 }
55 } 57 }
56 } 58 }
57 }
58 59
59 private Collection<ClassEntry> getRelatedClasses(ClassEntry classEntry) { 60 //Step 2, check ancestors, ignoring members invisible to children
60 InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); 61 Set<ClassEntry> ancestors = inheritanceIndex.getAncestors(containingClass);
62 for (ClassEntry ancestor : ancestors) {
63 Entry<?> reparentedEntry = entry.replaceAncestor(containingClass, ancestor);
64 Entry<?> translatedEntry = Objects.requireNonNull(deobfuscator.translate(reparentedEntry), "Translation failed");
65 Collection<Entry<?>> translatedSiblings = obfToDeobf.getSiblings(reparentedEntry).stream()
66 .filter(it->!entry.equals(it))//e.g. for root classes, ensure we dont match the name against itself
67 .filter(this::isVisibleToChildren)
68 .collect(Collectors.toList());
69 for (Entry<?> parentSibling : translatedSiblings) {
70 Entry<?> parentSiblingTranslated = Objects.requireNonNull(deobfuscator.translate(parentSibling), "Translation failed");
71 if (translatedEntry.canConflictWith(parentSiblingTranslated) && parentSiblingTranslated.getName().equals(name) && !isAcceptableOverride(parentSibling, entry)) {
72 Entry<?> parent = translatedEntry.getParent();
73 if (parent != null) {
74 vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent);
75 } else {
76 vc.raise(Message.NONUNIQUE_NAME, name);
77 }
78 }
79 }
80 }
61 81
62 Collection<ClassEntry> relatedClasses = new HashSet<>(); 82 //Step 3, if this entry is visible to children, see if it clashes with any of their names
63 relatedClasses.add(classEntry); 83 if (isVisibleToChildren(entry)) {
64 relatedClasses.addAll(inheritanceIndex.getChildren(classEntry)); 84 Collection<ClassEntry> children = inheritanceIndex.getDescendants(containingClass);
65 relatedClasses.addAll(inheritanceIndex.getAncestors(classEntry)); 85 for (ClassEntry child : children) {
86 Entry<?> reparentedEntry = entry.replaceAncestor(containingClass, child);
87 Entry<?> translatedEntry = Objects.requireNonNull(deobfuscator.translate(reparentedEntry), "Translation failed");
88 Collection<Entry<?>> siblings = obfToDeobf.getSiblings(reparentedEntry).stream()
89 .filter(it->!entry.equals(it))//e.g. for root classes, ensure we dont match the name against itself
90 .collect(Collectors.toList());
91 for (Entry<?> childSibling : siblings) {
92 Entry<?> childSiblingTranslated = Objects.requireNonNull(deobfuscator.translate(childSibling), "Translation failed");
93 if (translatedEntry.canConflictWith(childSiblingTranslated) && childSiblingTranslated.getName().equals(name) && !isAcceptableOverride(entry, childSibling)) {
94 Entry<?> parent = translatedEntry.getParent();
95 if (parent != null) {
96 vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent);
97 } else {
98 vc.raise(Message.NONUNIQUE_NAME, name);
99 }
100 }
101 }
102 }
103 }
104 }
66 105
67 return relatedClasses; 106 private boolean isVisibleToChildren(Entry<?> entry) {
107 if (entry instanceof DefEntry) {
108 return !((DefEntry<?>) entry).getAccess().isPrivate();
109 }
110 AccessFlags accessFlags = index.getEntryIndex().getEntryAccess(entry);
111 if (accessFlags != null) {
112 return !accessFlags.isPrivate();
113 }
114 return true;//unknown, assume yes
68 } 115 }
69 116
70 private boolean isUnique(Entry<?> entry, Collection<Entry<?>> siblings, String name) { 117 private boolean isAcceptableOverride(Entry<?> ancestor, Entry<?> descendent) {
71 for (Entry<?> sibling : siblings) { 118 if (ancestor instanceof FieldEntry && descendent instanceof FieldEntry){
72 if (entry.canConflictWith(sibling) && sibling.getName().equals(name)) { 119 return true;//fields don't apply here
73 return false; 120 }
74 } 121
122 AccessFlags ancestorFlags = findAccessFlags(ancestor);
123 AccessFlags descendentFlags = findAccessFlags(descendent);
124
125 if (ancestorFlags == null || descendentFlags == null) {
126 return false;//we can't make any assumptions
75 } 127 }
76 return true; 128
129 //bad == accessLevel < superAccessLevel
130 return !(descendentFlags.getAccessLevel() < ancestorFlags.getAccessLevel());
131 }
132
133 private boolean isSynthetic(Entry<?> entry) {
134 AccessFlags accessFlags = findAccessFlags(entry);
135 return accessFlags != null && accessFlags.isSynthetic();
136 }
137
138 private AccessFlags findAccessFlags(Entry<?> entry) {
139 return (entry instanceof DefEntry) ? ((DefEntry<?>) entry).getAccess() : index.getEntryIndex()
140 .getEntryAccess(entry);
77 } 141 }
78 142
79} 143}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
index b280eef2..aa48a5b2 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
@@ -8,6 +8,10 @@ import java.lang.reflect.Modifier;
8public class AccessFlags { 8public class AccessFlags {
9 public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE); 9 public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE);
10 public static final AccessFlags PUBLIC = new AccessFlags(Opcodes.ACC_PUBLIC); 10 public static final AccessFlags PUBLIC = new AccessFlags(Opcodes.ACC_PUBLIC);
11 public static final int ACCESS_LEVEL_PUBLIC = 4;
12 public static final int ACCESS_LEVEL_PROTECTED = 3;
13 public static final int ACCESS_LEVEL_PACKAGE_LOCAL = 2;
14 public static final int ACCESS_LEVEL_PRIVATE = 1;
11 15
12 private int flags; 16 private int flags;
13 17
@@ -89,6 +93,23 @@ public class AccessFlags {
89 return this.flags; 93 return this.flags;
90 } 94 }
91 95
96 /**
97 * Adapted from https://github.com/JetBrains/intellij-community/blob/6472c347db91d11bbf02895a767198f9d884b119/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java#L389
98 * @return visibility access level on a 'weakness scale'
99 */
100 public int getAccessLevel() {
101 if (isPrivate()) {
102 return ACCESS_LEVEL_PRIVATE;
103 }
104 if (isProtected()) {
105 return ACCESS_LEVEL_PROTECTED;
106 }
107 if (isPublic()) {
108 return ACCESS_LEVEL_PUBLIC;
109 }
110 return ACCESS_LEVEL_PACKAGE_LOCAL;
111 }
112
92 @Override 113 @Override
93 public boolean equals(Object obj) { 114 public boolean equals(Object obj) {
94 return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags; 115 return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags;