diff options
Diffstat (limited to 'enigma/src/main/java')
| -rw-r--r-- | enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java | 116 | ||||
| -rw-r--r-- | enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java | 21 |
2 files changed, 26 insertions, 111 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 a84b0fb..f9f3b88 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java | |||
| @@ -1,19 +1,15 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | 1 | package cuchaz.enigma.translation.mapping; |
| 2 | 2 | ||
| 3 | import java.util.Collection; | 3 | import java.util.Collection; |
| 4 | import java.util.Objects; | 4 | import java.util.HashSet; |
| 5 | import java.util.Set; | ||
| 6 | import java.util.stream.Collectors; | 5 | import java.util.stream.Collectors; |
| 7 | 6 | ||
| 8 | import cuchaz.enigma.analysis.index.InheritanceIndex; | 7 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 9 | import cuchaz.enigma.analysis.index.JarIndex; | 8 | import cuchaz.enigma.analysis.index.JarIndex; |
| 10 | import cuchaz.enigma.translation.Translator; | 9 | import cuchaz.enigma.translation.Translator; |
| 11 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 10 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 12 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 11 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 14 | import cuchaz.enigma.translation.representation.entry.DefEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.Entry; | 12 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 16 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 17 | import cuchaz.enigma.utils.validation.Message; | 13 | import cuchaz.enigma.utils.validation.Message; |
| 18 | import cuchaz.enigma.utils.validation.ValidationContext; | 14 | import cuchaz.enigma.utils.validation.ValidationContext; |
| 19 | 15 | ||
| @@ -39,16 +35,18 @@ public class MappingValidator { | |||
| 39 | 35 | ||
| 40 | private void validateUnique(ValidationContext vc, Entry<?> entry, String name) { | 36 | private void validateUnique(ValidationContext vc, Entry<?> entry, String name) { |
| 41 | ClassEntry containingClass = entry.getContainingClass(); | 37 | ClassEntry containingClass = entry.getContainingClass(); |
| 42 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); | 38 | Collection<ClassEntry> relatedClasses = getRelatedClasses(containingClass); |
| 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()); | ||
| 43 | 47 | ||
| 44 | //Step 1, check it's unique within its own siblings | 48 | if (!isUnique(translatedEntry, translatedSiblings, name)) { |
| 45 | Collection<Entry<?>> directTranslatedSiblings = obfToDeobf.getSiblings(entry).stream() | 49 | Entry<?> parent = translatedEntry.getParent(); |
| 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(); | ||
| 52 | if (parent != null) { | 50 | if (parent != null) { |
| 53 | vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent); | 51 | vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent); |
| 54 | } else { | 52 | } else { |
| @@ -56,88 +54,26 @@ public class MappingValidator { | |||
| 56 | } | 54 | } |
| 57 | } | 55 | } |
| 58 | } | 56 | } |
| 59 | |||
| 60 | //Step 2, check ancestors, ignoring members invisible to children | ||
| 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 | } | ||
| 81 | |||
| 82 | //Step 3, if this entry is visible to children, see if it clashes with any of their names | ||
| 83 | if (isVisibleToChildren(entry)) { | ||
| 84 | Collection<ClassEntry> children = inheritanceIndex.getDescendants(containingClass); | ||
| 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 | } | 57 | } |
| 105 | 58 | ||
| 106 | private boolean isVisibleToChildren(Entry<?> entry) { | 59 | private Collection<ClassEntry> getRelatedClasses(ClassEntry classEntry) { |
| 107 | if (entry instanceof DefEntry) { | 60 | InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); |
| 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 | ||
| 115 | } | ||
| 116 | |||
| 117 | private boolean isAcceptableOverride(Entry<?> ancestor, Entry<?> descendent) { | ||
| 118 | if (ancestor instanceof FieldEntry && descendent instanceof FieldEntry){ | ||
| 119 | return true;//fields don't apply here | ||
| 120 | } | ||
| 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 | ||
| 127 | } | ||
| 128 | 61 | ||
| 129 | //bad == accessLevel < superAccessLevel | 62 | Collection<ClassEntry> relatedClasses = new HashSet<>(); |
| 130 | return !(descendentFlags.getAccessLevel() < ancestorFlags.getAccessLevel()); | 63 | relatedClasses.add(classEntry); |
| 131 | } | 64 | relatedClasses.addAll(inheritanceIndex.getChildren(classEntry)); |
| 65 | relatedClasses.addAll(inheritanceIndex.getAncestors(classEntry)); | ||
| 132 | 66 | ||
| 133 | private boolean isSynthetic(Entry<?> entry) { | 67 | return relatedClasses; |
| 134 | AccessFlags accessFlags = findAccessFlags(entry); | ||
| 135 | return accessFlags != null && accessFlags.isSynthetic(); | ||
| 136 | } | 68 | } |
| 137 | 69 | ||
| 138 | private AccessFlags findAccessFlags(Entry<?> entry) { | 70 | private boolean isUnique(Entry<?> entry, Collection<Entry<?>> siblings, String name) { |
| 139 | return (entry instanceof DefEntry) ? ((DefEntry<?>) entry).getAccess() : index.getEntryIndex() | 71 | for (Entry<?> sibling : siblings) { |
| 140 | .getEntryAccess(entry); | 72 | if (entry.canConflictWith(sibling) && sibling.getName().equals(name)) { |
| 73 | return false; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | return true; | ||
| 141 | } | 77 | } |
| 142 | 78 | ||
| 143 | } | 79 | } |
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 aa48a5b..b280eef 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java | |||
| @@ -8,10 +8,6 @@ import java.lang.reflect.Modifier; | |||
| 8 | public class AccessFlags { | 8 | public 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; | ||
| 15 | 11 | ||
| 16 | private int flags; | 12 | private int flags; |
| 17 | 13 | ||
| @@ -93,23 +89,6 @@ public class AccessFlags { | |||
| 93 | return this.flags; | 89 | return this.flags; |
| 94 | } | 90 | } |
| 95 | 91 | ||
| 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 | |||
| 113 | @Override | 92 | @Override |
| 114 | public boolean equals(Object obj) { | 93 | public boolean equals(Object obj) { |
| 115 | return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags; | 94 | return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags; |