From ba7a354efae7d49833c887cf147ac940c975a1fa Mon Sep 17 00:00:00 2001 From: Gegy Date: Wed, 30 Jan 2019 21:05:32 +0200 Subject: Remap sources (#106) * Source remapping beginnings * Fix navigation to remapped classes * Translate identifier info reference * Remap local variables with default names in source * Caching translator * Fix lack of highlighting for first opened class * Fix unicode variable names * Unicode checker shouldn't be checking just alphanumeric * Fix package tree being built from obf names * Don't index `this` as method call for method::reference * Apply proposed names * Fix source export issues * Replace unicode var names at bytecode level uniquely * Drop imports from editor source * Class selector fixes * Delta keep track of base mappings to enable lookup of old names * Optimize source remapping by remapping source with a StringBuffer instead of copying * Bump version --- .../enigma/translation/mapping/EntryRemapper.java | 125 ++------------------- .../enigma/translation/mapping/MappingDelta.java | 36 +++--- .../translation/mapping/MappingValidator.java | 14 ++- .../mapping/serde/EnigmaMappingsWriter.java | 19 ++-- .../translation/mapping/serde/MappingFormat.java | 2 +- .../translation/mapping/serde/MappingsWriter.java | 6 +- .../mapping/serde/SrgMappingsWriter.java | 2 +- .../mapping/tree/DeltaTrackingTree.java | 21 +++- .../enigma/translation/mapping/tree/EntryTree.java | 9 +- .../translation/mapping/tree/HashEntryTree.java | 22 ++++ 10 files changed, 101 insertions(+), 155 deletions(-) (limited to 'src/main/java/cuchaz/enigma/translation/mapping') diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java index b7d8d17..1203aba 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java @@ -6,7 +6,6 @@ import cuchaz.enigma.translation.Translatable; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.representation.entry.Entry; @@ -14,59 +13,25 @@ import javax.annotation.Nullable; import java.util.Collection; public class EntryRemapper { - private final EntryTree obfToDeobf; - private final DeltaTrackingTree deobfToObf; - - private final JarIndex obfIndex; + private final DeltaTrackingTree obfToDeobf; private final EntryResolver obfResolver; - private EntryResolver deobfResolver; - private final Translator deobfuscator; - private Translator obfuscator; private final MappingValidator validator; - private EntryRemapper(JarIndex jarIndex, EntryTree obfToDeobf, EntryTree deobfToObf) { - this.obfToDeobf = obfToDeobf; - this.deobfToObf = new DeltaTrackingTree<>(deobfToObf); + public EntryRemapper(JarIndex jarIndex, EntryTree obfToDeobf) { + this.obfToDeobf = new DeltaTrackingTree<>(obfToDeobf); - this.obfIndex = jarIndex; this.obfResolver = jarIndex.getEntryResolver(); this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver); - rebuildDeobfIndex(); - this.validator = new MappingValidator(this.deobfToObf, deobfuscator, obfResolver); + this.validator = new MappingValidator(obfToDeobf, deobfuscator, obfResolver); } public EntryRemapper(JarIndex jarIndex) { - this(jarIndex, new HashEntryTree<>(), new HashEntryTree<>()); - } - - public EntryRemapper(JarIndex jarIndex, EntryTree deobfuscationTrees) { - this(jarIndex, deobfuscationTrees, inverse(deobfuscationTrees)); - } - - private static EntryTree inverse(EntryTree tree) { - Translator translator = new MappingTranslator(tree, VoidEntryResolver.INSTANCE); - EntryTree inverse = new HashEntryTree<>(); - - // Naive approach, could operate on the nodes of the tree. However, this runs infrequently. - Collection> entries = tree.getAllEntries(); - for (Entry sourceEntry : entries) { - Entry targetEntry = translator.translate(sourceEntry); - inverse.insert(targetEntry, new EntryMapping(sourceEntry.getName())); - } - - return inverse; - } - - private void rebuildDeobfIndex() { - JarIndex deobfIndex = obfIndex.remapped(deobfuscator); - - this.deobfResolver = deobfIndex.getEntryResolver(); - this.obfuscator = new MappingTranslator(deobfToObf, deobfResolver); + this(jarIndex, new HashEntryTree<>()); } public > void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { @@ -76,84 +41,31 @@ public class EntryRemapper { validator.validateRename(resolvedEntry, deobfMapping.getTargetName()); } - setObfToDeobf(resolvedEntry, deobfMapping); + obfToDeobf.insert(obfuscatedEntry, deobfMapping); } - - // Temporary hack, not very performant - rebuildDeobfIndex(); - } - - public > void mapFromDeobf(E deobfuscatedEntry, @Nullable EntryMapping deobfMapping) { - E obfuscatedEntry = obfuscate(deobfuscatedEntry); - mapFromObf(obfuscatedEntry, deobfMapping); } public void removeByObf(Entry obfuscatedEntry) { mapFromObf(obfuscatedEntry, null); } - public void removeByDeobf(Entry deobfuscatedEntry) { - mapFromObf(obfuscate(deobfuscatedEntry), null); - } - - private > void setObfToDeobf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { - E prevDeobf = deobfuscate(obfuscatedEntry); - obfToDeobf.insert(obfuscatedEntry, deobfMapping); - - E newDeobf = deobfuscate(obfuscatedEntry); - - // Reconstruct the children of this node in the deobf -> obf tree with our new mapping - // We only need to do this for deobf -> obf because the obf tree is always consistent on the left hand side - // We lookup by obf, and the obf never changes. This is not the case for deobf so we need to update the tree. - - EntryTreeNode node = deobfToObf.findNode(prevDeobf); - if (node != null) { - for (EntryTreeNode child : node.getNodesRecursively()) { - Entry entry = child.getEntry(); - EntryMapping mapping = new EntryMapping(obfuscate(entry).getName()); - - deobfToObf.insert(entry.replaceAncestor(prevDeobf, newDeobf), mapping); - deobfToObf.remove(entry); - } - } else { - deobfToObf.insert(newDeobf, new EntryMapping(obfuscatedEntry.getName())); - } - } - @Nullable public EntryMapping getDeobfMapping(Entry entry) { return obfToDeobf.get(entry); } - @Nullable - public EntryMapping getObfMapping(Entry entry) { - return deobfToObf.get(entry); - } - public boolean hasDeobfMapping(Entry obfEntry) { return obfToDeobf.contains(obfEntry); } - public boolean hasObfMapping(Entry deobfEntry) { - return deobfToObf.contains(deobfEntry); - } - public T deobfuscate(T translatable) { return deobfuscator.translate(translatable); } - public T obfuscate(T translatable) { - return obfuscator.translate(translatable); - } - public Translator getDeobfuscator() { return deobfuscator; } - public Translator getObfuscator() { - return obfuscator; - } - public Collection> getObfEntries() { return obfToDeobf.getAllEntries(); } @@ -162,40 +74,23 @@ public class EntryRemapper { return obfToDeobf.getRootEntries(); } - public Collection> getDeobfEntries() { - return deobfToObf.getAllEntries(); - } - public Collection> getObfChildren(Entry obfuscatedEntry) { return obfToDeobf.getChildren(obfuscatedEntry); } - public Collection> getDeobfChildren(Entry deobfuscatedEntry) { - return deobfToObf.getChildren(deobfuscatedEntry); - } - - public EntryTree getObfToDeobf() { + public DeltaTrackingTree getObfToDeobf() { return obfToDeobf; } - public DeltaTrackingTree getDeobfToObf() { - return deobfToObf; - } - - public MappingDelta takeMappingDelta() { - MappingDelta delta = deobfToObf.takeDelta(); - return delta.translate(obfuscator, VoidEntryResolver.INSTANCE, deobfToObf); + public MappingDelta takeMappingDelta() { + return obfToDeobf.takeDelta(); } public boolean isDirty() { - return deobfToObf.isDirty(); + return obfToDeobf.isDirty(); } public EntryResolver getObfResolver() { return obfResolver; } - - public EntryResolver getDeobfResolver() { - return deobfResolver; - } } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java index 4fba49d..9f1f468 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java @@ -6,28 +6,35 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.representation.entry.Entry; -public class MappingDelta implements Translatable { +public class MappingDelta implements Translatable { public static final Object PLACEHOLDER = new Object(); + private final EntryTree baseMappings; + private final EntryTree additions; private final EntryTree deletions; - public MappingDelta(EntryTree additions, EntryTree deletions) { + public MappingDelta(EntryTree baseMappings, EntryTree additions, EntryTree deletions) { + this.baseMappings = baseMappings; this.additions = additions; this.deletions = deletions; } - public MappingDelta() { - this(new HashEntryTree<>(), new HashEntryTree<>()); + public MappingDelta(EntryTree baseMappings) { + this(baseMappings, new HashEntryTree<>(), new HashEntryTree<>()); } - public static MappingDelta added(EntryTree mappings) { + public static MappingDelta added(EntryTree mappings) { EntryTree additions = new HashEntryTree<>(); for (Entry entry : mappings.getAllEntries()) { additions.insert(entry, PLACEHOLDER); } - return new MappingDelta(additions, new HashEntryTree<>()); + return new MappingDelta<>(new HashEntryTree<>(), additions, new HashEntryTree<>()); + } + + public EntryTree getBaseMappings() { + return baseMappings; } public EntryTree getAdditions() { @@ -39,18 +46,11 @@ public class MappingDelta implements Translatable { } @Override - public MappingDelta translate(Translator translator, EntryResolver resolver, EntryMap mappings) { - return new MappingDelta( - translate(translator, additions), - translate(translator, deletions) + public MappingDelta translate(Translator translator, EntryResolver resolver, EntryMap mappings) { + return new MappingDelta<>( + translator.translate(baseMappings), + translator.translate(additions), + translator.translate(deletions) ); } - - private EntryTree translate(Translator translator, EntryTree tree) { - EntryTree translatedTree = new HashEntryTree<>(); - for (Entry entry : tree.getAllEntries()) { - translatedTree.insert(translator.translate(entry), PLACEHOLDER); - } - return translatedTree; - } } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java index 422bf38..9be48c3 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java @@ -6,14 +6,15 @@ import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.representation.entry.Entry; import java.util.Collection; +import java.util.stream.Collectors; public class MappingValidator { - private final EntryTree deobfToObf; + private final EntryTree obfToDeobf; private final Translator deobfuscator; private final EntryResolver entryResolver; - public MappingValidator(EntryTree deobfToObf, Translator deobfuscator, EntryResolver entryResolver) { - this.deobfToObf = deobfToObf; + public MappingValidator(EntryTree obfToDeobf, Translator deobfuscator, EntryResolver entryResolver) { + this.obfToDeobf = obfToDeobf; this.deobfuscator = deobfuscator; this.entryResolver = entryResolver; } @@ -28,8 +29,11 @@ public class MappingValidator { private void validateUnique(Entry entry, String name) { Entry translatedEntry = deobfuscator.translate(entry); - Collection> siblings = deobfToObf.getSiblings(translatedEntry); - if (!isUnique(translatedEntry, siblings, name)) { + Collection> translatedSiblings = obfToDeobf.getSiblings(entry).stream() + .map(deobfuscator::translate) + .collect(Collectors.toList()); + + if (!isUnique(translatedEntry, translatedSiblings, name)) { throw new IllegalNameException(name, "Name is not unique in " + translatedEntry.getParent() + "!"); } } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java index 5acb1da..1d44b6e 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java @@ -14,10 +14,7 @@ package cuchaz.enigma.translation.mapping.serde; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.MappingTranslator; import cuchaz.enigma.translation.Translator; -import cuchaz.enigma.translation.mapping.AccessModifier; -import cuchaz.enigma.translation.mapping.EntryMapping; -import cuchaz.enigma.translation.mapping.MappingDelta; -import cuchaz.enigma.translation.mapping.VoidEntryResolver; +import cuchaz.enigma.translation.mapping.*; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; import cuchaz.enigma.translation.representation.entry.*; @@ -37,7 +34,7 @@ import java.util.stream.Collectors; public enum EnigmaMappingsWriter implements MappingsWriter { FILE { @Override - public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress) { + public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress) { Collection classes = mappings.getRootEntries().stream() .filter(entry -> entry instanceof ClassEntry) .map(entry -> (ClassEntry) entry) @@ -58,8 +55,8 @@ public enum EnigmaMappingsWriter implements MappingsWriter { }, DIRECTORY { @Override - public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress) { - applyDeletions(delta.getDeletions(), path); + public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress) { + applyDeletions(delta.getBaseMappings(), delta.getDeletions(), path); Collection classes = delta.getAdditions().getRootEntries().stream() .filter(entry -> entry instanceof ClassEntry) @@ -76,8 +73,8 @@ public enum EnigmaMappingsWriter implements MappingsWriter { try { Path classPath = resolve(path, translator.translate(classEntry)); - Files.deleteIfExists(classPath); Files.createDirectories(classPath.getParent()); + Files.deleteIfExists(classPath); try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(classPath))) { writeRoot(writer, mappings, classEntry); @@ -89,10 +86,12 @@ public enum EnigmaMappingsWriter implements MappingsWriter { }); } - private void applyDeletions(EntryTree deletions, Path root) { + private void applyDeletions(EntryTree baseMappings, EntryTree deletions, Path root) { + Translator oldMappingTranslator = new MappingTranslator(baseMappings, VoidEntryResolver.INSTANCE); + Collection deletedClasses = deletions.getRootEntries().stream() .filter(e -> e instanceof ClassEntry) - .map(e -> (ClassEntry) e) + .map(e -> oldMappingTranslator.translate((ClassEntry) e)) .collect(Collectors.toList()); for (ClassEntry classEntry : deletedClasses) { diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java index 4db1645..622a0e1 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java @@ -28,7 +28,7 @@ public enum MappingFormat { write(mappings, MappingDelta.added(mappings), path, progressListener); } - public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progressListener) { + public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progressListener) { if (writer == null) { throw new IllegalStateException(name() + " does not support writing"); } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java index b519668..77f6ee0 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java @@ -8,5 +8,9 @@ import cuchaz.enigma.translation.mapping.tree.EntryTree; import java.nio.file.Path; public interface MappingsWriter { - void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress); + void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress); + + default void write(EntryTree mappings, Path path, ProgressListener progress) { + write(mappings, MappingDelta.added(mappings), path, progress); + } } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java index 5ff9141..40be136 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java @@ -29,7 +29,7 @@ public enum SrgMappingsWriter implements MappingsWriter { INSTANCE; @Override - public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress) { + public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress) { try { Files.deleteIfExists(path); Files.createFile(path); diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java index 98a01df..36be5e1 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java @@ -1,5 +1,9 @@ package cuchaz.enigma.translation.mapping.tree; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryMap; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.MappingDelta; import cuchaz.enigma.translation.representation.entry.Entry; @@ -10,11 +14,13 @@ import java.util.Iterator; public class DeltaTrackingTree implements EntryTree { private final EntryTree delegate; + private EntryTree deltaReference; private EntryTree additions = new HashEntryTree<>(); private EntryTree deletions = new HashEntryTree<>(); public DeltaTrackingTree(EntryTree delegate) { this.delegate = delegate; + this.deltaReference = new HashEntryTree<>(delegate); } public DeltaTrackingTree() { @@ -40,7 +46,7 @@ public class DeltaTrackingTree implements EntryTree { } public void trackAddition(Entry entry) { - deletions.remove(entry); + deletions.insert(entry, MappingDelta.PLACEHOLDER); additions.insert(entry, MappingDelta.PLACEHOLDER); } @@ -81,6 +87,14 @@ public class DeltaTrackingTree implements EntryTree { return delegate.getRootEntries(); } + @Override + public DeltaTrackingTree translate(Translator translator, EntryResolver resolver, EntryMap mappings) { + DeltaTrackingTree translatedTree = new DeltaTrackingTree<>(delegate.translate(translator, resolver, mappings)); + translatedTree.additions = additions.translate(translator, resolver, mappings); + translatedTree.deletions = deletions.translate(translator, resolver, mappings); + return translatedTree; + } + @Override public Collection> getAllEntries() { return delegate.getAllEntries(); @@ -96,13 +110,14 @@ public class DeltaTrackingTree implements EntryTree { return delegate.iterator(); } - public MappingDelta takeDelta() { - MappingDelta delta = new MappingDelta(additions, deletions); + public MappingDelta takeDelta() { + MappingDelta delta = new MappingDelta<>(deltaReference, additions, deletions); resetDelta(); return delta; } private void resetDelta() { + deltaReference = new HashEntryTree<>(delegate); additions = new HashEntryTree<>(); deletions = new HashEntryTree<>(); } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java index 73fe12d..4f341f4 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java @@ -1,12 +1,16 @@ package cuchaz.enigma.translation.mapping.tree; +import cuchaz.enigma.translation.Translatable; +import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.EntryMap; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.representation.entry.Entry; import javax.annotation.Nullable; import java.util.Collection; -public interface EntryTree extends EntryMap, Iterable> { +public interface EntryTree extends EntryMap, Iterable>, Translatable { Collection> getChildren(Entry entry); Collection> getSiblings(Entry entry); @@ -17,4 +21,7 @@ public interface EntryTree extends EntryMap, Iterable> { Collection> getAllNodes(); Collection> getRootEntries(); + + @Override + EntryTree translate(Translator translator, EntryResolver resolver, EntryMap mappings); } diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java index ff88bf9..551fb1c 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java @@ -1,5 +1,9 @@ package cuchaz.enigma.translation.mapping.tree; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryMap; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.representation.entry.Entry; import javax.annotation.Nullable; @@ -9,6 +13,15 @@ import java.util.stream.Collectors; public class HashEntryTree implements EntryTree { private final Map, HashTreeNode> root = new HashMap<>(); + public HashEntryTree() { + } + + public HashEntryTree(EntryTree tree) { + for (EntryTreeNode node : tree.getAllNodes()) { + insert(node.getEntry(), node.getValue()); + } + } + @Override public void insert(Entry entry, T value) { List> path = computePath(entry); @@ -156,4 +169,13 @@ public class HashEntryTree implements EntryTree { public boolean isEmpty() { return root.isEmpty(); } + + @Override + public HashEntryTree translate(Translator translator, EntryResolver resolver, EntryMap mappings) { + HashEntryTree translatedTree = new HashEntryTree<>(); + for (EntryTreeNode node : getAllNodes()) { + translatedTree.insert(translator.translate(node.getEntry()), node.getValue()); + } + return translatedTree; + } } -- cgit v1.2.3