From 8efb62490ec153246d467a1a72c513781db887bf Mon Sep 17 00:00:00 2001 From: 2xsaiko Date: Thu, 8 Jul 2021 16:30:39 +0200 Subject: Entry Changes (#364) * Initial refactor: Allow EntryMapping to have null targetName, add EntryChange in favor of entry changing methods in GuiController * Fix resetting name not actually removing it, and renaming a class causing name collisions with itself Closes #246. * Use name proposer for setting default deobf name Closes #314 * Make network protocol use EntryChange directly * Handle writing other data correctly when the deobf name is null * b * Add some new abstraction stuff * Use pattern matching instanceof * Move classes out of newabstraction package * Make EntryChange final * Regenerate equals and hashCode * Convert EntryMapping to record * Make TristateChange final * Safety guard null accessModifier initialization--- .../enigma/source/DecompiledClassSource.java | 2 +- .../cuchaz/enigma/source/cfr/EnigmaDumper.java | 12 +-- .../transformers/AddJavadocsAstTransform.java | 7 +- .../enigma/translation/mapping/EntryChange.java | 97 ++++++++++++++++++++++ .../enigma/translation/mapping/EntryMapping.java | 67 +++++---------- .../enigma/translation/mapping/EntryRemapper.java | 53 ++++++------ .../enigma/translation/mapping/EntryUtil.java | 42 ++++++++++ .../translation/mapping/MappingValidator.java | 13 ++- .../translation/mapping/MappingsChecker.java | 2 +- .../translation/mapping/serde/RawEntryMapping.java | 12 +-- .../mapping/serde/enigma/EnigmaMappingsReader.java | 56 +++++-------- .../mapping/serde/enigma/EnigmaMappingsWriter.java | 96 ++++++++++----------- .../mapping/serde/tiny/TinyMappingsWriter.java | 9 +- .../mapping/serde/tinyv2/TinyV2Writer.java | 46 ++++++---- .../translation/mapping/tree/HashEntryTree.java | 2 + .../enigma/translation/representation/Lambda.java | 6 +- .../representation/entry/ClassDefEntry.java | 11 +-- .../representation/entry/ClassEntry.java | 8 +- .../translation/representation/entry/Entry.java | 28 +++++-- .../representation/entry/FieldDefEntry.java | 14 ++-- .../representation/entry/FieldEntry.java | 10 +-- .../entry/LocalVariableDefEntry.java | 10 +-- .../representation/entry/LocalVariableEntry.java | 10 +-- .../representation/entry/MethodDefEntry.java | 12 +-- .../representation/entry/MethodEntry.java | 10 +-- .../representation/entry/ParentedEntry.java | 5 +- .../java/cuchaz/enigma/utils/TristateChange.java | 78 +++++++++++++++++ .../enigma/utils/validation/PrintValidatable.java | 10 ++- .../enigma/utils/validation/ValidationContext.java | 14 ++++ 29 files changed, 486 insertions(+), 256 deletions(-) create mode 100644 enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java create mode 100644 enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java create mode 100644 enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java (limited to 'enigma/src/main/java') diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java index 9ac611f..5f371a5 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java @@ -79,7 +79,7 @@ public class DecompiledClassSource { return null; } - private Optional proposeName(EnigmaProject project, Entry entry) { + public static Optional proposeName(EnigmaProject project, Entry entry) { EnigmaServices services = project.getEnigma().getServices(); return services.get(NameProposalService.TYPE).stream().flatMap(nameProposalService -> { diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java index b85851f..4e8940a 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java @@ -148,7 +148,7 @@ public class EnigmaDumper extends StringStreamDumper { continue; } - String javaDoc = mapping.getJavadoc(); + String javaDoc = mapping.javadoc(); if (javaDoc != null) { recordComponentDocs.add(String.format("@param %s %s", field.getFieldName(), javaDoc)); } @@ -159,7 +159,7 @@ public class EnigmaDumper extends StringStreamDumper { String javadoc = null; if (mapping != null) { - javadoc = mapping.getJavadoc(); + javadoc = mapping.javadoc(); } if (javadoc != null || !recordComponentDocs.isEmpty()) { @@ -191,7 +191,7 @@ public class EnigmaDumper extends StringStreamDumper { MethodEntry methodEntry = getMethodEntry(method); EntryMapping mapping = mapper.getDeobfMapping(methodEntry); if (mapping != null) { - String javadoc = mapping.getJavadoc(); + String javadoc = mapping.javadoc(); if (javadoc != null) { lines.addAll(Arrays.asList(javadoc.split("\\R"))); } @@ -204,9 +204,9 @@ public class EnigmaDumper extends StringStreamDumper { if (each instanceof LocalVariableEntry) { EntryMapping paramMapping = mapper.getDeobfMapping(each); if (paramMapping != null) { - String javadoc = paramMapping.getJavadoc(); + String javadoc = paramMapping.javadoc(); if (javadoc != null) { - lines.addAll(Arrays.asList(("@param " + paramMapping.getTargetName() + " " + javadoc).split("\\R"))); + lines.addAll(Arrays.asList(("@param " + paramMapping.targetName() + " " + javadoc).split("\\R"))); } } } @@ -230,7 +230,7 @@ public class EnigmaDumper extends StringStreamDumper { if (mapper != null && !recordComponent) { EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor())); if (mapping != null) { - String javadoc = mapping.getJavadoc(); + String javadoc = mapping.javadoc(); if (javadoc != null) { print("/**").newln(); for (String line : javadoc.split("\\R")) { diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java index 70fc8c6..1e5beb1 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java @@ -47,16 +47,17 @@ public final class AddJavadocsAstTransform implements IAstTransform { private Comment[] getComments(T node, Function> retriever) { final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node)); - final String docs = mapping == null ? null : Strings.emptyToNull(mapping.getJavadoc()); + final String docs = Strings.emptyToNull(mapping.javadoc()); return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st, CommentType.Documentation)).toArray(Comment[]::new); } private Comment[] getParameterComments(ParameterDeclaration node, Function> retriever) { - final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node)); + Entry entry = retriever.apply(node); + final EntryMapping mapping = remapper.getDeobfMapping(entry); final Comment[] ret = getComments(node, retriever); if (ret != null) { - final String paramPrefix = "@param " + mapping.getTargetName() + " "; + final String paramPrefix = "@param " + (mapping.targetName() != null ? mapping.targetName() : entry.getName()) + " "; final String indent = Strings.repeat(" ", paramPrefix.length()); ret[0].setContent(paramPrefix + ret[0].getContent()); for (int i = 1; i < ret.length; i++) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java new file mode 100644 index 0000000..b5ec855 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java @@ -0,0 +1,97 @@ +package cuchaz.enigma.translation.mapping; + +import java.util.Objects; +import java.util.Optional; + +import javax.annotation.Nullable; + +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.source.DecompiledClassSource; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.utils.TristateChange; + +public final class EntryChange> { + + private final E target; + private final TristateChange deobfName; + private final TristateChange javadoc; + private final TristateChange access; + + private EntryChange(E target, TristateChange deobfName, TristateChange javadoc, TristateChange access) { + this.target = target; + this.deobfName = deobfName; + this.javadoc = javadoc; + this.access = access; + } + + public static > EntryChange modify(E target) { + return new EntryChange<>(target, TristateChange.unchanged(), TristateChange.unchanged(), TristateChange.unchanged()); + } + + public EntryChange withDeobfName(String name) { + return new EntryChange<>(this.target, TristateChange.set(name), this.javadoc, this.access); + } + + public EntryChange withDefaultDeobfName(@Nullable EnigmaProject project) { + Optional proposed = project != null ? DecompiledClassSource.proposeName(project, this.target) : Optional.empty(); + return this.withDeobfName(proposed.orElse(this.target.getName())); + } + + public EntryChange clearDeobfName() { + return new EntryChange<>(this.target, TristateChange.reset(), this.javadoc, this.access); + } + + public EntryChange withJavadoc(String javadoc) { + return new EntryChange<>(this.target, this.deobfName, TristateChange.set(javadoc), this.access); + } + + public EntryChange clearJavadoc() { + return new EntryChange<>(this.target, this.deobfName, TristateChange.reset(), this.access); + } + + public EntryChange withAccess(AccessModifier access) { + return new EntryChange<>(this.target, this.deobfName, this.javadoc, TristateChange.set(access)); + } + + public EntryChange clearAccess() { + return new EntryChange<>(this.target, this.deobfName, this.javadoc, TristateChange.reset()); + } + + public TristateChange getDeobfName() { + return this.deobfName; + } + + public TristateChange getJavadoc() { + return this.javadoc; + } + + public TristateChange getAccess() { + return this.access; + } + + public E getTarget() { + return this.target; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EntryChange)) return false; + EntryChange that = (EntryChange) o; + return Objects.equals(this.target, that.target) && + Objects.equals(this.deobfName, that.deobfName) && + Objects.equals(this.javadoc, that.javadoc) && + Objects.equals(this.access, that.access); + } + + @Override + public int hashCode() { + return Objects.hash(this.target, this.deobfName, this.javadoc, this.access); + } + + @Override + public String toString() { + return String.format("EntryChange { target: %s, deobfName: %s, javadoc: %s, access: %s }", this.target, this.deobfName, this.javadoc, this.access); + } + +} diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java index c607817..e916bf3 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java @@ -1,49 +1,37 @@ package cuchaz.enigma.translation.mapping; +import java.util.Arrays; + import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class EntryMapping { - private final String targetName; - private final AccessModifier accessModifier; - private final @Nullable String javadoc; +public record EntryMapping( + @Nullable String targetName, + @Nonnull AccessModifier accessModifier, + @Nullable String javadoc +) { + public static final EntryMapping DEFAULT = new EntryMapping(null, AccessModifier.UNCHANGED, null); + + public EntryMapping { + if (accessModifier == null) { + accessModifier = AccessModifier.UNCHANGED; + System.err.println("EntryMapping initialized with 'null' accessModifier, assuming UNCHANGED. Please fix."); + Arrays.stream(new Exception().getStackTrace()).skip(1).map("\tat %s"::formatted).forEach(System.err::println); + } + } - public EntryMapping(@Nonnull String targetName) { + public EntryMapping(@Nullable String targetName) { this(targetName, AccessModifier.UNCHANGED); } - public EntryMapping(@Nonnull String targetName, @Nullable String javadoc) { + public EntryMapping(@Nullable String targetName, @Nullable String javadoc) { this(targetName, AccessModifier.UNCHANGED, javadoc); } - public EntryMapping(@Nonnull String targetName, AccessModifier accessModifier) { + public EntryMapping(@Nullable String targetName, AccessModifier accessModifier) { this(targetName, accessModifier, null); } - public EntryMapping(@Nonnull String targetName, AccessModifier accessModifier, @Nullable String javadoc) { - this.targetName = targetName; - this.accessModifier = accessModifier; - this.javadoc = javadoc; - } - - @Nonnull - public String getTargetName() { - return targetName; - } - - @Nonnull - public AccessModifier getAccessModifier() { - if (accessModifier == null) { - return AccessModifier.UNCHANGED; - } - return accessModifier; - } - - @Nullable - public String getJavadoc() { - return javadoc; - } - public EntryMapping withName(String newName) { return new EntryMapping(newName, accessModifier, javadoc); } @@ -55,21 +43,4 @@ public class EntryMapping { public EntryMapping withDocs(String newDocs) { return new EntryMapping(targetName, accessModifier, newDocs); } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - - if (obj instanceof EntryMapping) { - EntryMapping mapping = (EntryMapping) obj; - return mapping.targetName.equals(targetName) && mapping.accessModifier.equals(accessModifier); - } - - return false; - } - - @Override - public int hashCode() { - return targetName.hashCode() + accessModifier.hashCode() * 31; - } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java index 8b5490e..0977b74 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java @@ -1,10 +1,11 @@ package cuchaz.enigma.translation.mapping; import java.util.Collection; +import java.util.Objects; import java.util.List; import java.util.stream.Stream; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.translation.MappingTranslator; @@ -50,15 +51,15 @@ public class EntryRemapper { return new EntryRemapper(index, new HashEntryTree<>()); } - public > void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { - mapFromObf(vc, obfuscatedEntry, deobfMapping, true); + public void validatePutMapping(ValidationContext vc, Entry obfuscatedEntry, @Nonnull EntryMapping deobfMapping) { + doPutMapping(vc, obfuscatedEntry, deobfMapping, true); } - public > void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming) { - mapFromObf(vc, obfuscatedEntry, deobfMapping, renaming, false); + public void putMapping(ValidationContext vc, Entry obfuscatedEntry, @Nonnull EntryMapping deobfMapping) { + doPutMapping(vc, obfuscatedEntry, deobfMapping, false); } - public > void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming, boolean validateOnly) { + private void doPutMapping(ValidationContext vc, Entry obfuscatedEntry, @Nonnull EntryMapping deobfMapping, boolean validateOnly) { if (obfuscatedEntry instanceof FieldEntry) { FieldEntry fieldEntry = (FieldEntry) obfuscatedEntry; ClassEntry classEntry = fieldEntry.getParent(); @@ -66,25 +67,27 @@ public class EntryRemapper { mapRecordComponentGetter(vc, classEntry, fieldEntry, deobfMapping); } - Collection resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST); + boolean renaming = !Objects.equals(getDeobfMapping(obfuscatedEntry).targetName(), deobfMapping.targetName()); - if (renaming && deobfMapping != null) { - for (E resolvedEntry : resolvedEntries) { - validator.validateRename(vc, resolvedEntry, deobfMapping.getTargetName()); + Collection> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST); + + if (renaming && deobfMapping.targetName() != null) { + for (Entry resolvedEntry : resolvedEntries) { + validator.validateRename(vc, resolvedEntry, deobfMapping.targetName()); } } if (validateOnly || !vc.canProceed()) return; - for (E resolvedEntry : resolvedEntries) { - obfToDeobf.insert(resolvedEntry, deobfMapping); + for (Entry resolvedEntry : resolvedEntries) { + if (deobfMapping.equals(EntryMapping.DEFAULT)) { + obfToDeobf.insert(resolvedEntry, null); + } else { + obfToDeobf.insert(resolvedEntry, deobfMapping); + } } } - public void removeByObf(ValidationContext vc, Entry obfuscatedEntry) { - mapFromObf(vc, obfuscatedEntry, null); - } - // A little bit of a hack to also map the getter method for record fields/components. private void mapRecordComponentGetter(ValidationContext vc, ClassEntry classEntry, FieldEntry fieldEntry, EntryMapping fieldMapping) { if (!jarIndex.getEntryIndex().getClassAccess(classEntry).isRecord() || jarIndex.getEntryIndex().getFieldAccess(fieldEntry).isStatic()) { @@ -107,20 +110,17 @@ public class EntryRemapper { } if (methodEntry == null && fieldMapping != null) { - vc.raise(Message.UNKNOWN_RECORD_GETTER, fieldMapping.getTargetName()); + vc.raise(Message.UNKNOWN_RECORD_GETTER, fieldMapping.targetName()); return; } - mapFromObf(vc, methodEntry, fieldMapping != null ? new EntryMapping(fieldMapping.getTargetName()) : null); + putMapping(vc, methodEntry, fieldMapping != null ? new EntryMapping(fieldMapping.targetName()) : null); } - @Nullable + @Nonnull public EntryMapping getDeobfMapping(Entry entry) { - return obfToDeobf.get(entry); - } - - public boolean hasDeobfMapping(Entry obfEntry) { - return obfToDeobf.contains(obfEntry); + EntryMapping entryMapping = obfToDeobf.get(entry); + return entryMapping == null ? EntryMapping.DEFAULT : entryMapping; } public TranslateResult extendedDeobfuscate(T translatable) { @@ -158,4 +158,9 @@ public class EntryRemapper { public EntryResolver getObfResolver() { return obfResolver; } + + public MappingValidator getValidator() { + return validator; + } + } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java new file mode 100644 index 0000000..582076c --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java @@ -0,0 +1,42 @@ +package cuchaz.enigma.translation.mapping; + +import javax.annotation.Nonnull; + +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.utils.validation.ValidationContext; + +public class EntryUtil { + + public static EntryMapping applyChange(ValidationContext vc, EntryRemapper remapper, EntryChange change) { + Entry target = change.getTarget(); + EntryMapping prev = remapper.getDeobfMapping(target); + EntryMapping mapping = EntryUtil.applyChange(prev, change); + + remapper.putMapping(vc, target, mapping); + + return mapping; + } + + public static EntryMapping applyChange(@Nonnull EntryMapping self, EntryChange change) { + if (change.getDeobfName().isSet()) { + self = self.withName(change.getDeobfName().getNewValue()); + } else if (change.getDeobfName().isReset()) { + self = self.withName(null); + } + + if (change.getJavadoc().isSet()) { + self = self.withDocs(change.getJavadoc().getNewValue()); + } else if (change.getJavadoc().isReset()) { + self = self.withDocs(null); + } + + if (change.getAccess().isSet()) { + self = self.withModifier(change.getAccess().getNewValue()); + } else if (change.getAccess().isReset()) { + self = self.withModifier(AccessModifier.UNCHANGED); + } + + return self; + } + +} 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 1615912..065e5c3 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java @@ -25,18 +25,22 @@ public class MappingValidator { this.index = index; } - public void validateRename(ValidationContext vc, Entry entry, String name) { + public boolean validateRename(ValidationContext vc, Entry entry, String name) { Collection> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry); + boolean error = false; for (Entry equivalentEntry : equivalentEntries) { equivalentEntry.validateName(vc, name); - validateUnique(vc, equivalentEntry, name); + error |= validateUnique(vc, equivalentEntry, name); } + return error; } - private void validateUnique(ValidationContext vc, Entry entry, String name) { + private boolean validateUnique(ValidationContext vc, Entry entry, String name) { ClassEntry containingClass = entry.getContainingClass(); Collection relatedClasses = getRelatedClasses(containingClass); + boolean error = false; + for (ClassEntry relatedClass : relatedClasses) { Entry relatedEntry = entry.replaceAncestor(containingClass, relatedClass); Entry translatedEntry = deobfuscator.translate(relatedEntry); @@ -52,8 +56,11 @@ public class MappingValidator { } else { vc.raise(Message.NONUNIQUE_NAME, name); } + error = true; } } + + return error; } private Collection getRelatedClasses(ClassEntry classEntry) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java index 141a07c..392b1a6 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java @@ -75,7 +75,7 @@ public class MappingsChecker { private final Map, String> droppedMappings = new HashMap<>(); public void drop(Entry entry, EntryMapping mapping) { - droppedMappings.put(entry, mapping.getTargetName()); + droppedMappings.put(entry, mapping.targetName() != null ? mapping.targetName() : entry.getName()); } void apply(EntryTree mappings) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java index 79587a0..6465008 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java @@ -1,23 +1,23 @@ package cuchaz.enigma.translation.mapping.serde; -import cuchaz.enigma.translation.mapping.AccessModifier; -import cuchaz.enigma.translation.mapping.EntryMapping; - import java.util.ArrayList; import java.util.List; +import cuchaz.enigma.translation.mapping.AccessModifier; +import cuchaz.enigma.translation.mapping.EntryMapping; + public final class RawEntryMapping { private final String targetName; private final AccessModifier access; - private List javadocs = new ArrayList<>(); + private final List javadocs = new ArrayList<>(); public RawEntryMapping(String targetName) { - this(targetName, null); + this(targetName, AccessModifier.UNCHANGED); } public RawEntryMapping(String targetName, AccessModifier access) { this.access = access; - this.targetName = targetName; + this.targetName = targetName != null && !targetName.equals("-") ? targetName : null; } public void addJavadocLine(String line) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java index 27545c0..ccc25df 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java @@ -1,15 +1,21 @@ package cuchaz.enigma.translation.mapping.serde.enigma; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import javax.annotation.Nullable; + import com.google.common.base.Charsets; + import cuchaz.enigma.ProgressListener; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingPair; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; -import cuchaz.enigma.translation.mapping.serde.MappingHelper; -import cuchaz.enigma.translation.mapping.serde.MappingsReader; -import cuchaz.enigma.translation.mapping.serde.RawEntryMapping; +import cuchaz.enigma.translation.mapping.serde.*; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.representation.MethodDescriptor; @@ -17,18 +23,6 @@ import cuchaz.enigma.translation.representation.TypeDescriptor; import cuchaz.enigma.translation.representation.entry.*; import cuchaz.enigma.utils.I18n; -import javax.annotation.Nullable; -import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Deque; -import java.util.List; -import java.util.Locale; - public enum EnigmaMappingsReader implements MappingsReader { FILE { @Override @@ -200,12 +194,12 @@ public enum EnigmaMappingsReader implements MappingsReader { throw new RuntimeException("Unknown token '" + keyToken + "'"); } } - + private static void readJavadoc(MappingPair parent, String[] tokens) { if (parent == null) throw new IllegalStateException("Javadoc has no parent!"); // Empty string to concat - String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens,1,tokens.length)) : ""; + String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens, 1, tokens.length)) : ""; if (parent.getMapping() == null) { parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED)); } @@ -237,11 +231,7 @@ public enum EnigmaMappingsReader implements MappingsReader { modifier = parseModifier(tokens[3]); } - if (mapping != null) { - return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier)); - } else { - return new MappingPair<>(obfuscatedEntry); - } + return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier)); } private static MappingPair parseField(@Nullable Entry parent, String[] tokens) { @@ -252,7 +242,7 @@ public enum EnigmaMappingsReader implements MappingsReader { ClassEntry ownerEntry = (ClassEntry) parent; String obfuscatedName = tokens[1]; - String mapping = obfuscatedName; + String mapping = null; AccessModifier modifier = AccessModifier.UNCHANGED; TypeDescriptor descriptor; @@ -269,19 +259,15 @@ public enum EnigmaMappingsReader implements MappingsReader { descriptor = new TypeDescriptor(tokens[3]); } } else if (tokens.length == 5) { - descriptor = new TypeDescriptor(tokens[3]); mapping = tokens[2]; - modifier = parseModifier(tokens[4]); + modifier = parseModifier(tokens[3]); + descriptor = new TypeDescriptor(tokens[4]); } else { throw new RuntimeException("Invalid field declaration"); } FieldEntry obfuscatedEntry = new FieldEntry(ownerEntry, obfuscatedName, descriptor); - if (mapping != null) { - return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier)); - } else { - return new MappingPair<>(obfuscatedEntry); - } + return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier)); } private static MappingPair parseMethod(@Nullable Entry parent, String[] tokens) { @@ -317,11 +303,7 @@ public enum EnigmaMappingsReader implements MappingsReader { } MethodEntry obfuscatedEntry = new MethodEntry(ownerEntry, obfuscatedName, descriptor); - if (mapping != null) { - return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier)); - } else { - return new MappingPair<>(obfuscatedEntry); - } + return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier)); } private static MappingPair parseArgument(@Nullable Entry parent, String[] tokens) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java index d9f1470..5d7a789 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java @@ -15,12 +15,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.nio.file.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -28,25 +23,19 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; +import javax.annotation.Nonnull; + 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.serde.MappingFileNameFormat; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.VoidEntryResolver; -import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; -import cuchaz.enigma.translation.mapping.serde.MappingHelper; -import cuchaz.enigma.translation.mapping.serde.MappingsWriter; +import cuchaz.enigma.translation.mapping.serde.*; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.translation.representation.entry.Entry; -import cuchaz.enigma.translation.representation.entry.FieldEntry; -import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; -import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.translation.representation.entry.*; import cuchaz.enigma.utils.I18n; public enum EnigmaMappingsWriter implements MappingsWriter { @@ -184,19 +173,22 @@ public enum EnigmaMappingsWriter implements MappingsWriter { EntryMapping classEntryMapping = mappings.get(classEntry); + if (classEntryMapping == null) { + classEntryMapping = EntryMapping.DEFAULT; + } + writer.println(writeClass(classEntry, classEntryMapping).trim()); - if (classEntryMapping != null && classEntryMapping.getJavadoc() != null) { + if (classEntryMapping.javadoc() != null) { writeDocs(writer, classEntryMapping, 0); } for (Entry child : children) { writeEntry(writer, mappings, child, 1); } - } private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) { - String jd = mapping.getJavadoc(); + String jd = mapping.javadoc(); if (jd != null) { for (String line : jd.split("\\R")) { writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1)); @@ -212,20 +204,26 @@ public enum EnigmaMappingsWriter implements MappingsWriter { EntryMapping mapping = node.getValue(); - if (entry instanceof ClassEntry) { - String line = writeClass((ClassEntry) entry, mapping); - writer.println(indent(line, depth)); - } else if (entry instanceof MethodEntry) { - String line = writeMethod((MethodEntry) entry, mapping); - writer.println(indent(line, depth)); - } else if (entry instanceof FieldEntry) { - String line = writeField((FieldEntry) entry, mapping); - writer.println(indent(line, depth)); - } else if (entry instanceof LocalVariableEntry && mapping != null) { - String line = writeArgument((LocalVariableEntry) entry, mapping); + if (mapping == null) { + mapping = EntryMapping.DEFAULT; + } + + String line = null; + if (entry instanceof ClassEntry classEntry) { + line = writeClass(classEntry, mapping); + } else if (entry instanceof MethodEntry methodEntry) { + line = writeMethod(methodEntry, mapping); + } else if (entry instanceof FieldEntry fieldEntry) { + line = writeField(fieldEntry, mapping); + } else if (entry instanceof LocalVariableEntry varEntry && mapping.targetName() != null) { + line = writeArgument(varEntry, mapping); + } + + if (line != null) { writer.println(indent(line, depth)); } - if (mapping != null && mapping.getJavadoc() != null) { + + if (mapping.javadoc() != null) { writeDocs(writer, mapping, depth); } @@ -254,55 +252,53 @@ public enum EnigmaMappingsWriter implements MappingsWriter { .forEach(result::add); children.stream().filter(e -> e instanceof ClassEntry) - .map(e -> (ClassEntry) e) - .sorted() - .forEach(result::add); + .map(e -> (ClassEntry) e) + .sorted() + .forEach(result::add); return result; } - protected String writeClass(ClassEntry entry, EntryMapping mapping) { - StringBuilder builder = new StringBuilder(EnigmaFormat.CLASS +" "); + protected String writeClass(ClassEntry entry, @Nonnull EntryMapping mapping) { + StringBuilder builder = new StringBuilder(EnigmaFormat.CLASS + " "); builder.append(entry.getName()).append(' '); writeMapping(builder, mapping); return builder.toString(); } - protected String writeMethod(MethodEntry entry, EntryMapping mapping) { + protected String writeMethod(MethodEntry entry, @Nonnull EntryMapping mapping) { StringBuilder builder = new StringBuilder(EnigmaFormat.METHOD + " "); builder.append(entry.getName()).append(' '); - if (mapping != null && !mapping.getTargetName().equals(entry.getName())) { - writeMapping(builder, mapping); - } + writeMapping(builder, mapping); builder.append(entry.getDesc().toString()); return builder.toString(); } - protected String writeField(FieldEntry entry, EntryMapping mapping) { + protected String writeField(FieldEntry entry, @Nonnull EntryMapping mapping) { StringBuilder builder = new StringBuilder(EnigmaFormat.FIELD + " "); builder.append(entry.getName()).append(' '); - if (mapping != null && !mapping.getTargetName().equals(entry.getName())) { - writeMapping(builder, mapping); - } + writeMapping(builder, mapping); builder.append(entry.getDesc().toString()); return builder.toString(); } - protected String writeArgument(LocalVariableEntry entry, EntryMapping mapping) { - return EnigmaFormat.PARAMETER + " " + entry.getIndex() + ' ' + mapping.getTargetName(); + protected String writeArgument(LocalVariableEntry entry, @Nonnull EntryMapping mapping) { + return EnigmaFormat.PARAMETER + " " + entry.getIndex() + ' ' + mapping.targetName(); } private void writeMapping(StringBuilder builder, EntryMapping mapping) { - if (mapping != null) { - builder.append(mapping.getTargetName()).append(' '); - if (mapping.getAccessModifier() != AccessModifier.UNCHANGED) { - builder.append(mapping.getAccessModifier().getFormattedName()).append(' '); + if (mapping.targetName() != null) { + builder.append(mapping.targetName()).append(' '); + if (mapping.accessModifier() != AccessModifier.UNCHANGED) { + builder.append(mapping.accessModifier().getFormattedName()).append(' '); } + } else if (mapping.accessModifier() != AccessModifier.UNCHANGED) { + builder.append("- ").append(mapping.accessModifier().getFormattedName()).append(' '); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java index 4f78e6f..1f785e1 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java @@ -73,13 +73,16 @@ public class TinyMappingsWriter implements MappingsWriter { Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); EntryMapping mapping = mappings.get(entry); - if (mapping != null && !entry.getName().equals(mapping.getTargetName())) { + + // Do not write mappings without deobfuscated name since tiny v1 doesn't + // support comments anyway + if (mapping != null && mapping.targetName() != null) { if (entry instanceof ClassEntry) { writeClass(writer, (ClassEntry) entry, translator); } else if (entry instanceof FieldEntry) { - writeLine(writer, serializeEntry(entry, mapping.getTargetName())); + writeLine(writer, serializeEntry(entry, mapping.targetName())); } else if (entry instanceof MethodEntry) { - writeLine(writer, serializeEntry(entry, mapping.getTargetName())); + writeLine(writer, serializeEntry(entry, mapping.targetName())); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java index 5160eda..c400568 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java @@ -62,8 +62,8 @@ public final class TinyV2Writer implements MappingsWriter { Deque parts = new LinkedList<>(); do { EntryMapping mapping = tree.get(classEntry); - if (mapping != null) { - parts.addFirst(mapping.getTargetName()); + if (mapping != null && mapping.targetName() != null) { + parts.addFirst(mapping.targetName()); } else { parts.addFirst(classEntry.getName()); } @@ -81,7 +81,7 @@ public final class TinyV2Writer implements MappingsWriter { writeComment(writer, node.getValue(), 1); for (EntryTreeNode child : node.getChildNodes()) { - Entry entry = child.getEntry(); + Entry entry = child.getEntry(); if (entry instanceof FieldEntry) { writeField(writer, child); } else if (entry instanceof MethodEntry) { @@ -98,16 +98,21 @@ public final class TinyV2Writer implements MappingsWriter { writer.print(node.getEntry().getName()); writer.print("\t"); EntryMapping mapping = node.getValue(); + if (mapping == null) { - writer.println(node.getEntry().getName()); // todo fix v2 name inference - } else { - writer.println(mapping.getTargetName()); + mapping = EntryMapping.DEFAULT; + } - writeComment(writer, mapping, 2); + if (mapping.targetName() != null) { + writer.println(mapping.targetName()); + } else { + writer.println(node.getEntry().getName()); // todo fix v2 name inference } + writeComment(writer, mapping, 2); + for (EntryTreeNode child : node.getChildNodes()) { - Entry entry = child.getEntry(); + Entry entry = child.getEntry(); if (entry instanceof LocalVariableEntry) { writeParameter(writer, child); } @@ -116,7 +121,7 @@ public final class TinyV2Writer implements MappingsWriter { } private void writeField(PrintWriter writer, EntryTreeNode node) { - if (node.getValue() == null) + if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) return; // Shortcut writer.print(indent(1)); @@ -126,17 +131,22 @@ public final class TinyV2Writer implements MappingsWriter { writer.print(node.getEntry().getName()); writer.print("\t"); EntryMapping mapping = node.getValue(); + if (mapping == null) { - writer.println(node.getEntry().getName()); // todo fix v2 name inference - } else { - writer.println(mapping.getTargetName()); + mapping = EntryMapping.DEFAULT; + } - writeComment(writer, mapping, 2); + if (mapping.targetName() != null) { + writer.println(mapping.targetName()); + } else { + writer.println(node.getEntry().getName()); // todo fix v2 name inference } + + writeComment(writer, mapping, 2); } private void writeParameter(PrintWriter writer, EntryTreeNode node) { - if (node.getValue() == null) + if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) return; // Shortcut writer.print(indent(2)); @@ -146,20 +156,20 @@ public final class TinyV2Writer implements MappingsWriter { writer.print(node.getEntry().getName()); writer.print("\t"); EntryMapping mapping = node.getValue(); - if (mapping == null) { + if (mapping == null || mapping.targetName() == null) { writer.println(); // todo ??? } else { - writer.println(mapping.getTargetName()); + writer.println(mapping.targetName()); writeComment(writer, mapping, 3); } } private void writeComment(PrintWriter writer, EntryMapping mapping, int indent) { - if (mapping != null && mapping.getJavadoc() != null) { + if (mapping != null && mapping.javadoc() != null) { writer.print(indent(indent)); writer.print("c\t"); - writer.print(MappingHelper.escape(mapping.getJavadoc())); + writer.print(MappingHelper.escape(mapping.javadoc())); writer.println(); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java index 570941c..0992d34 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java @@ -6,6 +6,7 @@ import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.representation.entry.Entry; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.*; import java.util.function.Function; @@ -152,6 +153,7 @@ public class HashEntryTree implements EntryTree { } @Override + @Nonnull public Iterator> iterator() { Collection> nodes = new ArrayList<>(); for (EntryTreeNode node : root.values()) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java index a6aed73..13c7cd4 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java @@ -35,9 +35,9 @@ public class Lambda implements Translatable { EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod); return TranslateResult.of( - samMethodMapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + samMethodMapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new Lambda( - samMethodMapping != null ? samMethodMapping.getTargetName() : invokedName, + samMethodMapping.targetName() != null ? samMethodMapping.targetName() : invokedName, invokedType.extendedTranslate(translator, resolver, mappings).getValue(), samMethodType.extendedTranslate(translator, resolver, mappings).getValue(), implMethod.extendedTranslate(translator, resolver, mappings).getValue(), @@ -53,7 +53,7 @@ public class Lambda implements Translatable { return mapping; } } - return null; + return EntryMapping.DEFAULT; } public ClassEntry getInterface() { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java index b0fd286..237c93d 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java @@ -13,6 +13,7 @@ package cuchaz.enigma.translation.representation.entry; import java.util.Arrays; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.base.Preconditions; @@ -75,15 +76,15 @@ public class ClassDefEntry extends ClassEntry implements DefEntry { } @Override - public TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { + public TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { Signature translatedSignature = translator.translate(signature); - String translatedName = mapping != null ? mapping.getTargetName() : name; - AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access; + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + AccessFlags translatedAccess = mapping.accessModifier().transform(access); ClassEntry translatedSuper = translator.translate(superClass); ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new); - String docs = mapping != null ? mapping.getJavadoc() : null; + String docs = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java index fe56611..ec5294e 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java @@ -82,16 +82,16 @@ public class ClassEntry extends ParentedEntry implements Comparable< } @Override - public TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { + public TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { if (name.charAt(0) == '[') { TranslateResult translatedName = translator.extendedTranslate(new TypeDescriptor(name)); return translatedName.map(desc -> new ClassEntry(parent, desc.toString())); } - String translatedName = mapping != null ? mapping.getTargetName() : name; - String docs = mapping != null ? mapping.getJavadoc() : null; + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + String docs = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new ClassEntry(parent, translatedName, docs) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java index 8a81c54..956f32c 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java @@ -13,6 +13,7 @@ package cuchaz.enigma.translation.representation.entry; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.annotation.Nullable; @@ -107,16 +108,29 @@ public interface Entry

> extends Translatable { boolean canConflictWith(Entry entry); - @Nullable default ClassEntry getContainingClass() { - P parent = getParent(); - if (parent == null) { - return null; + ClassEntry last = null; + Entry current = this; + while (current != null) { + if (current instanceof ClassEntry) { + last = (ClassEntry) current; + break; + } + current = current.getParent(); } - if (parent instanceof ClassEntry) { - return (ClassEntry) parent; + return Objects.requireNonNull(last, () -> String.format("%s has no containing class?", this)); + } + + default ClassEntry getTopLevelClass() { + ClassEntry last = null; + Entry current = this; + while (current != null) { + if (current instanceof ClassEntry) { + last = (ClassEntry) current; + } + current = current.getParent(); } - return parent.getContainingClass(); + return Objects.requireNonNull(last, () -> String.format("%s has no top level class?", this)); } default List> getAncestry() { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java index b9da6cc..0efb6a9 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java @@ -11,8 +11,6 @@ package cuchaz.enigma.translation.representation.entry; -import javax.annotation.Nullable; - import com.google.common.base.Preconditions; import cuchaz.enigma.source.RenamableTokenType; @@ -23,6 +21,8 @@ import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.Signature; import cuchaz.enigma.translation.representation.TypeDescriptor; +import javax.annotation.Nonnull; + public class FieldDefEntry extends FieldEntry implements DefEntry { private final AccessFlags access; private final Signature signature; @@ -53,14 +53,14 @@ public class FieldDefEntry extends FieldEntry implements DefEntry { } @Override - protected TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { + protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { TypeDescriptor translatedDesc = translator.translate(desc); Signature translatedSignature = translator.translate(signature); - String translatedName = mapping != null ? mapping.getTargetName() : name; - AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access; - String docs = mapping != null ? mapping.getJavadoc() : null; + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + AccessFlags translatedAccess = mapping.accessModifier().transform(access); + String docs = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java index 5ddd33d..db94011 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java @@ -13,7 +13,7 @@ package cuchaz.enigma.translation.representation.entry; import java.util.Objects; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import com.google.common.base.Preconditions; @@ -63,11 +63,11 @@ public class FieldEntry extends ParentedEntry implements Comparable< } @Override - protected TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { - String translatedName = mapping != null ? mapping.getTargetName() : name; - String docs = mapping != null ? mapping.getJavadoc() : null; + protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + String docs = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new FieldEntry(parent, translatedName, translator.translate(desc), docs) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java index 2712c65..c151de4 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java @@ -1,6 +1,6 @@ package cuchaz.enigma.translation.representation.entry; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import com.google.common.base.Preconditions; @@ -30,12 +30,12 @@ public class LocalVariableDefEntry extends LocalVariableEntry { } @Override - protected TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { + protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { TypeDescriptor translatedDesc = translator.translate(desc); - String translatedName = mapping != null ? mapping.getTargetName() : name; - String javadoc = mapping != null ? mapping.getJavadoc() : javadocs; + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + String javadoc = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java index 154f11f..1cf1a83 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java @@ -2,7 +2,7 @@ package cuchaz.enigma.translation.representation.entry; import java.util.Objects; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import com.google.common.base.Preconditions; @@ -50,11 +50,11 @@ public class LocalVariableEntry extends ParentedEntry implements Co } @Override - protected TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { - String translatedName = mapping != null ? mapping.getTargetName() : name; - String javadoc = mapping != null ? mapping.getJavadoc() : null; + protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + String javadoc = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new LocalVariableEntry(parent, index, translatedName, parameter, javadoc) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java index cc326d6..30ef706 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java @@ -11,7 +11,7 @@ package cuchaz.enigma.translation.representation.entry; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import com.google.common.base.Preconditions; @@ -53,14 +53,14 @@ public class MethodDefEntry extends MethodEntry implements DefEntry } @Override - protected TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { + protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { MethodDescriptor translatedDesc = translator.translate(descriptor); Signature translatedSignature = translator.translate(signature); - String translatedName = mapping != null ? mapping.getTargetName() : name; - AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access; - String docs = mapping != null ? mapping.getJavadoc() : null; + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + AccessFlags translatedAccess = mapping.accessModifier().transform(access); + String docs = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java index 864a580..ab9c2d1 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java @@ -13,7 +13,7 @@ package cuchaz.enigma.translation.representation.entry; import java.util.Objects; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import com.google.common.base.Preconditions; @@ -58,11 +58,11 @@ public class MethodEntry extends ParentedEntry implements Comparable } @Override - protected TranslateResult extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { - String translatedName = mapping != null ? mapping.getTargetName() : name; - String docs = mapping != null ? mapping.getJavadoc() : null; + protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { + String translatedName = mapping.targetName() != null ? mapping.targetName() : name; + String docs = mapping.javadoc(); return TranslateResult.of( - mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new MethodEntry(parent, translatedName, translator.translate(descriptor), docs) ); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java index 5634891..267bc11 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java @@ -11,6 +11,7 @@ package cuchaz.enigma.translation.representation.entry; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.base.Preconditions; @@ -41,7 +42,7 @@ public abstract class ParentedEntry

> implements Entry

{ @Override public abstract ParentedEntry

withName(String name); - protected abstract TranslateResult> extendedTranslate(Translator translator, @Nullable EntryMapping mapping); + protected abstract TranslateResult> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping); @Override public String getName() { @@ -93,6 +94,6 @@ public abstract class ParentedEntry

> implements Entry

{ return mapping; } } - return null; + return EntryMapping.DEFAULT; } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java new file mode 100644 index 0000000..864154c --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java @@ -0,0 +1,78 @@ +package cuchaz.enigma.utils; + +import java.util.Objects; + +public final class TristateChange { + + private static final TristateChange UNCHANGED = new TristateChange<>(Type.UNCHANGED, null); + private static final TristateChange RESET = new TristateChange<>(Type.RESET, null); + + private final Type type; + private final T val; + + @SuppressWarnings("unchecked") + public static TristateChange unchanged() { + return (TristateChange) TristateChange.UNCHANGED; + } + + @SuppressWarnings("unchecked") + public static TristateChange reset() { + return (TristateChange) TristateChange.RESET; + } + + public static TristateChange set(T value) { + return new TristateChange<>(Type.SET, value); + } + + private TristateChange(Type type, T val) { + this.type = type; + this.val = val; + } + + public Type getType() { + return this.type; + } + + public boolean isUnchanged() { + return this.type == Type.UNCHANGED; + } + + public boolean isReset() { + return this.type == Type.RESET; + } + + public boolean isSet() { + return this.type == Type.SET; + } + + public T getNewValue() { + if (this.type != Type.SET) throw new IllegalStateException(String.format("No concrete value in %s", this)); + return this.val; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TristateChange that = (TristateChange) o; + return type == that.type && + Objects.equals(val, that.val); + } + + @Override + public int hashCode() { + return Objects.hash(type, val); + } + + @Override + public String toString() { + return String.format("TristateChange { type: %s, val: %s }", type, val); + } + + public enum Type { + UNCHANGED, + RESET, + SET, + } + +} diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java index 81b6c43..5067d7e 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java @@ -1,5 +1,6 @@ package cuchaz.enigma.utils.validation; +import java.io.PrintStream; import java.util.Arrays; public class PrintValidatable implements Validatable { @@ -8,6 +9,10 @@ public class PrintValidatable implements Validatable { @Override public void addMessage(ParameterizedMessage message) { + formatMessage(System.out, message); + } + + public static void formatMessage(PrintStream w, ParameterizedMessage message) { String text = message.getText(); String longText = message.getLongText(); String type = switch (message.message.type) { @@ -15,9 +20,10 @@ public class PrintValidatable implements Validatable { case WARNING -> "warning"; case ERROR -> "error"; }; - System.out.printf("%s: %s\n", type, text); + w.printf("%s: %s\n", type, text); + if (!longText.isEmpty()) { - Arrays.stream(longText.split("\n")).forEach(s -> System.out.printf(" %s\n", s)); + Arrays.stream(longText.split("\n")).forEach(s -> w.printf(" %s\n", s)); } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java index ee2b595..0ecb9fb 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java @@ -62,6 +62,20 @@ public class ValidationContext { return messages.stream().noneMatch(m -> m.message.type == Type.ERROR); } + /** + * If this validation context has at least one error, throw an exception. + * + * @throws IllegalStateException if errors are present + */ + public void throwOnError() { + if (!this.canProceed()) { + for (ParameterizedMessage message : this.messages) { + PrintValidatable.formatMessage(System.err, message); + } + throw new IllegalStateException("Errors encountered; cannot continue! Check error log for details."); + } + } + public List getMessages() { return Collections.unmodifiableList(messages); } -- cgit v1.2.3