From 941b7728ca51660a35fa50269d81b4787d1e2a34 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Sat, 16 Jan 2021 20:24:23 +0000 Subject: [PATCH 1/6] 0.5.7: Begin dev cycle --- build.gradle.kts | 2 +- changelogs/0.5.7.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/0.5.7.md diff --git a/build.gradle.kts b/build.gradle.kts index a65ae90..2518a56 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ val isSnapshot = version.toString().endsWith("-SNAPSHOT") allprojects { group = "org.cadixdev" - version = "0.5.6" + version = "0.5.7-SNAPSHOT" } subprojects { diff --git a/changelogs/0.5.7.md b/changelogs/0.5.7.md new file mode 100644 index 0000000..f94b792 --- /dev/null +++ b/changelogs/0.5.7.md @@ -0,0 +1,4 @@ +Lorenz 0.5.7 +=== + + From b8f24f475b854461ee6e0f0d39330eaba68c7b32 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Sat, 16 Jan 2021 20:41:25 +0000 Subject: [PATCH 2/6] Ignore package mappings in TSRG and CSRG mappings --- changelogs/0.5.7.md | 3 +++ .../cadixdev/lorenz/io/TextMappingsReader.java | 10 ++++++++++ .../cadixdev/lorenz/io/srg/csrg/CSrgReader.java | 16 ++++++++++++---- .../cadixdev/lorenz/io/srg/tsrg/TSrgReader.java | 16 ++++++++++++---- .../lorenz/test/io/srg/CSrgReaderTest.java | 11 +++++++++++ .../lorenz/test/io/srg/TSrgReaderTest.java | 11 +++++++++++ 6 files changed, 59 insertions(+), 8 deletions(-) diff --git a/changelogs/0.5.7.md b/changelogs/0.5.7.md index f94b792..d87af12 100644 --- a/changelogs/0.5.7.md +++ b/changelogs/0.5.7.md @@ -1,4 +1,7 @@ Lorenz 0.5.7 === +## Fixes +- CSRG and TSRG now ignore package mappings, rather than erroneously reading them + as class mappings. diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/io/TextMappingsReader.java b/lorenz/src/main/java/org/cadixdev/lorenz/io/TextMappingsReader.java index de06e03..c916021 100644 --- a/lorenz/src/main/java/org/cadixdev/lorenz/io/TextMappingsReader.java +++ b/lorenz/src/main/java/org/cadixdev/lorenz/io/TextMappingsReader.java @@ -107,6 +107,16 @@ protected Processor(final MappingSet mappings) { this.mappings = mappings; } + /** + * Gets the mapping set being read into by the processor. + * + * @return The mappings + * @since 0.5.7 + */ + public MappingSet getMappings() { + return this.mappings; + } + } } diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/csrg/CSrgReader.java b/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/csrg/CSrgReader.java index 36fd40d..8ab2141 100644 --- a/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/csrg/CSrgReader.java +++ b/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/csrg/CSrgReader.java @@ -87,14 +87,22 @@ public void accept(final String rawLine) { final String[] split = SPACE.split(line); final int len = split.length; - // Process class mappings + // Process class/package mappings if (len == CLASS_MAPPING_ELEMENT_COUNT) { final String obfuscatedName = split[0]; final String deobfuscatedName = split[1]; - // Get mapping, and set de-obfuscated name - this.mappings.getOrCreateClassMapping(obfuscatedName) - .setDeobfuscatedName(deobfuscatedName); + // Package mappings + if (obfuscatedName.endsWith("/")) { + // Lorenz doesn't currently support package mappings, though they are an SRG feature. + // For now, Lorenz will just silently ignore those mappings. + } + // Class mappings + else { + // Get mapping, and set de-obfuscated name + this.mappings.getOrCreateClassMapping(obfuscatedName) + .setDeobfuscatedName(deobfuscatedName); + } } // Process field mapping else if (len == FIELD_MAPPING_ELEMENT_COUNT) { diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/tsrg/TSrgReader.java b/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/tsrg/TSrgReader.java index 7eb9675..2b96cb4 100644 --- a/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/tsrg/TSrgReader.java +++ b/lorenz/src/main/java/org/cadixdev/lorenz/io/srg/tsrg/TSrgReader.java @@ -90,14 +90,22 @@ public void accept(final String rawLine) { final String[] split = SPACE.split(line); final int len = split.length; - // Process class mappings + // Process class/package mappings if (!split[0].startsWith("\t") && len == CLASS_MAPPING_ELEMENT_COUNT) { final String obfuscatedName = split[0]; final String deobfuscatedName = split[1]; - // Get mapping, and set de-obfuscated name - this.currentClass = this.mappings.getOrCreateClassMapping(obfuscatedName); - this.currentClass.setDeobfuscatedName(deobfuscatedName); + // Package mappings + if (obfuscatedName.endsWith("/")) { + // Lorenz doesn't currently support package mappings, though they are an SRG feature. + // For now, Lorenz will just silently ignore those mappings. + } + // Class mappings + else { + // Get mapping, and set de-obfuscated name + this.currentClass = this.mappings.getOrCreateClassMapping(obfuscatedName); + this.currentClass.setDeobfuscatedName(deobfuscatedName); + } } else if (split[0].startsWith("\t") && this.currentClass != null) { final String obfuscatedName = split[0].replace("\t", ""); diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/CSrgReaderTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/CSrgReaderTest.java index 7a837e6..7cfd03f 100644 --- a/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/CSrgReaderTest.java +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/CSrgReaderTest.java @@ -25,6 +25,7 @@ package org.cadixdev.lorenz.test.io.srg; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.cadixdev.lorenz.io.MappingFormats; @@ -39,6 +40,16 @@ public CSrgReaderTest() throws Exception { super(MappingFormats.CSRG, "/test.csrg"); } + @Test + public void ignoresPackages() throws IOException { + // This test ensures that package mappings won't erroneously be read as + // class mappings. No exceptions should be thrown either. + final CSrgReader.Processor parser = new CSrgReader.Processor(); + parser.accept("abc/ uk/jamierocks/Example"); + + assertEquals(0, parser.getMappings().getTopLevelClassMappings().size()); + } + @Test public void tooLongInput() throws IOException { // This test should set off the first case where IllegalArgumentException diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/TSrgReaderTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/TSrgReaderTest.java index 82297e4..51450ee 100644 --- a/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/TSrgReaderTest.java +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/TSrgReaderTest.java @@ -25,6 +25,7 @@ package org.cadixdev.lorenz.test.io.srg; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.cadixdev.lorenz.io.MappingFormats; @@ -39,6 +40,16 @@ public TSrgReaderTest() throws Exception { super(MappingFormats.TSRG, "/test.tsrg"); } + @Test + public void ignoresPackages() throws IOException { + // This test ensures that package mappings won't erroneously be read as + // class mappings. No exceptions should be thrown either. + final TSrgReader.Processor parser = new TSrgReader.Processor(); + parser.accept("abc/ uk/jamierocks/Example"); + + assertEquals(0, parser.getMappings().getTopLevelClassMappings().size()); + } + @Test public void tooLongInput() throws IOException { // This test should set off the first case where IllegalArgumentException From 4c86227ffc96ec3b1ba74e562a419ca56d6e31f9 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Mon, 18 Jan 2021 19:45:50 +0000 Subject: [PATCH 3/6] De-obfuscate unmapped inner classes --- changelogs/0.5.7.md | 3 + .../java/org/cadixdev/lorenz/MappingSet.java | 25 +++++- .../org/cadixdev/lorenz/util/BinaryTool.java | 71 ++++++++++++++++ .../cadixdev/lorenz/test/MappingSetTest.java | 82 +++++++++++++++++++ .../lorenz/test/util/BinaryToolTest.java | 65 +++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java create mode 100644 lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java create mode 100644 lorenz/src/test/java/org/cadixdev/lorenz/test/util/BinaryToolTest.java diff --git a/changelogs/0.5.7.md b/changelogs/0.5.7.md index d87af12..a4862c2 100644 --- a/changelogs/0.5.7.md +++ b/changelogs/0.5.7.md @@ -5,3 +5,6 @@ Lorenz 0.5.7 - CSRG and TSRG now ignore package mappings, rather than erroneously reading them as class mappings. +- `MappingSet#deobfuscate(FieldType)` will now correctly de-obfuscate object types + of inner classes, where the parent class has a mapping - but the inner class + does not. diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/MappingSet.java b/lorenz/src/main/java/org/cadixdev/lorenz/MappingSet.java index bf6f40b..6284733 100644 --- a/lorenz/src/main/java/org/cadixdev/lorenz/MappingSet.java +++ b/lorenz/src/main/java/org/cadixdev/lorenz/MappingSet.java @@ -36,6 +36,7 @@ import org.cadixdev.lorenz.model.TopLevelClassMapping; import org.cadixdev.lorenz.model.jar.CascadingFieldTypeProvider; import org.cadixdev.lorenz.model.jar.FieldTypeProvider; +import org.cadixdev.lorenz.util.BinaryTool; import org.cadixdev.lorenz.util.Reversible; import java.util.Collection; @@ -265,9 +266,27 @@ default FieldType deobfuscate(final FieldType type) { } else if (type instanceof ObjectType) { final ObjectType obj = (ObjectType) type; - return this.getClassMapping(obj.getClassName()) - .map(m -> new ObjectType(m.getFullDeobfuscatedName())) - .orElse(obj); + + final String[] name = BinaryTool.from(obj.getClassName()); + + ClassMapping currentClass = this.getClassMapping(name[0]).orElse(null); + if (currentClass == null) { + return type; + } + + for (int i = 1; i < name.length; i++) { + final ClassMapping thisClass = currentClass.getInnerClassMapping(name[i]).orElse(null); + if (thisClass == null) { + final String[] result = new String[name.length - i + 1]; + result[0] = currentClass.getFullDeobfuscatedName(); + System.arraycopy(name, i, result, 1, name.length - i); + + return new ObjectType(BinaryTool.to(result)); + } + currentClass = thisClass; + } + + return new ObjectType(currentClass.getFullDeobfuscatedName()); } return type; } diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java b/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java new file mode 100644 index 0000000..ef506fa --- /dev/null +++ b/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java @@ -0,0 +1,71 @@ +/* + * This file is part of Lorenz, licensed under the MIT License (MIT). + * + * Copyright (c) Jamie Mansfield + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.cadixdev.lorenz.util; + +/** + * A utility for working with binary names in Lorenz. + * + * @author Jamie Mansfield + * @since 0.5.7 + */ +public final class BinaryTool { + + /** + * Gets the split hierarchy of a binary name. + *

+ * For example, calling {@code from("a$b$c"}} would produce + * {@code ["a", "b", "c"]}. + * + * @return The name hierarchy + */ + public static String[] from(final String binaryName) { + return binaryName.split("\\$"); + } + + /** + * Gets the binary name for a split hierarchy. + * + * @param name The hierarchy + * @return The binary name + */ + public static String to(final String[] name) { + final StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < name.length; i++) { + builder.append(name[i]); + + if (i != name.length - 1) { + builder.append('$'); + } + } + + return builder.toString(); + } + + private BinaryTool() { + } + +} diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java new file mode 100644 index 0000000..0bbd19d --- /dev/null +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java @@ -0,0 +1,82 @@ +/* + * This file is part of Lorenz, licensed under the MIT License (MIT). + * + * Copyright (c) Jamie Mansfield + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.cadixdev.lorenz.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.cadixdev.bombe.type.ObjectType; +import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.lorenz.model.TopLevelClassMapping; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public final class MappingSetTest { + + @Test + @DisplayName("de-obfuscate object type") + public void deobfObjectType() { + final MappingSet mappings = mappings(); + + final ObjectType obfIn = new ObjectType("a"); + final ObjectType deobfOut = new ObjectType("Demo"); + + assertEquals(deobfOut, mappings.deobfuscate(obfIn)); + } + + @Test + @DisplayName("de-obfuscate object type of mapped inner class") + public void deobfObjectTypeOfMappedInnerClass() { + final MappingSet mappings = mappings(); + + final ObjectType obfIn = new ObjectType("a$a"); + final ObjectType deobfOut = new ObjectType("Demo$Inner"); + + assertEquals(deobfOut, mappings.deobfuscate(obfIn)); + } + + @Test + @DisplayName("de-obfuscate object type of unmapped inner class") + public void deobfObjectTypeOfUnmappedInnerClass() { + final MappingSet mappings = mappings(); + + final ObjectType obfIn = new ObjectType("a$b"); + final ObjectType deobfOut = new ObjectType("Demo$b"); + + assertEquals(deobfOut, mappings.deobfuscate(obfIn)); + } + + private static MappingSet mappings() { + final MappingSet mappings = MappingSet.create(); + + final TopLevelClassMapping a = mappings.getOrCreateTopLevelClassMapping("a") + .setDeobfuscatedName("Demo"); + a.getOrCreateInnerClassMapping("a") + .setDeobfuscatedName("Inner"); + + return mappings; + } + +} diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/util/BinaryToolTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/util/BinaryToolTest.java new file mode 100644 index 0000000..d3c41e8 --- /dev/null +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/util/BinaryToolTest.java @@ -0,0 +1,65 @@ +/* + * This file is part of Lorenz, licensed under the MIT License (MIT). + * + * Copyright (c) Jamie Mansfield + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.cadixdev.lorenz.test.util; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.cadixdev.lorenz.util.BinaryTool; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public final class BinaryToolTest { + + @Test + @DisplayName("reads top level class binary name") + public void readsTopLevelClass() { + final String[] result = BinaryTool.from("a"); + + assertArrayEquals(new String[]{ + "a" + }, result); + } + + @Test + @DisplayName("reads inner class binary name") + public void readsInnerClass() { + final String[] result = BinaryTool.from("a$b$c"); + + assertArrayEquals(new String[]{ + "a", "b", "c" + }, result); + } + + @Test + @DisplayName("writes binary name") + public void writeBinaryName() { + assertEquals("a$b", BinaryTool.to(new String[] { + "a", "b" + })); + } + +} From 3006543c61ef11db7ff9d7985cf9418399f5eb5f Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Tue, 19 Jan 2021 15:09:50 +0000 Subject: [PATCH 4/6] Further unit tests for descriptor de-obfuscation Some of these are superflous, granted - however, with de-obfuscating descriptors being as important as it is, I would rather not make mistakes again. --- .../cadixdev/lorenz/test/MappingSetTest.java | 85 +++++++++++++++++++ .../lorenz/test/io/srg/XSrgReaderTest.java | 8 +- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java index 0bbd19d..913b208 100644 --- a/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/MappingSetTest.java @@ -27,6 +27,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import org.cadixdev.bombe.type.ArrayType; +import org.cadixdev.bombe.type.MethodDescriptor; import org.cadixdev.bombe.type.ObjectType; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.lorenz.model.TopLevelClassMapping; @@ -68,6 +70,89 @@ public void deobfObjectTypeOfUnmappedInnerClass() { assertEquals(deobfOut, mappings.deobfuscate(obfIn)); } + @Test + @DisplayName("de-obfuscate array of object type") + public void deobfArrayOfObjectType() { + final MappingSet mappings = mappings(); + + final ArrayType in = new ArrayType(2, + new ObjectType("a")); + final ArrayType out = new ArrayType(2, + new ObjectType("Demo")); + + assertEquals(out, mappings.deobfuscate(in)); + } + + @Test + @DisplayName("de-obfuscate array of object type of mapped inner class") + public void deobfArrayOfObjectTypeOfMappedInnerClass() { + final MappingSet mappings = mappings(); + + final ArrayType in = new ArrayType(2, + new ObjectType("a$a")); + final ArrayType out = new ArrayType(2, + new ObjectType("Demo$Inner")); + + assertEquals(out, mappings.deobfuscate(in)); + } + + @Test + @DisplayName("de-obfuscate array of object type of unmapped inner class") + public void deobfArrayOfObjectTypeOfUnmappedInnerClass() { + final MappingSet mappings = mappings(); + + final ArrayType in = new ArrayType(2, + new ObjectType("a$b")); + final ArrayType out = new ArrayType(2, + new ObjectType("Demo$b")); + + assertEquals(out, mappings.deobfuscate(in)); + } + + @Test + @DisplayName("de-obfuscate method descriptor single object param") + public void deobfMethodDescriptorSingleObjectParam() { + final MappingSet mappings = mappings(); + + final MethodDescriptor obf = MethodDescriptor.of("(La;)V"); + final MethodDescriptor deobf = MethodDescriptor.of("(LDemo;)V"); + + assertEquals(deobf, mappings.deobfuscate(obf)); + } + + @Test + @DisplayName("de-obfuscate method descriptor multiple object param") + public void deobfMethodDescriptorMultipleObjectParam() { + final MappingSet mappings = mappings(); + + final MethodDescriptor obf = MethodDescriptor.of("(La;La;)V"); + final MethodDescriptor deobf = MethodDescriptor.of("(LDemo;LDemo;)V"); + + assertEquals(deobf, mappings.deobfuscate(obf)); + } + + @Test + @DisplayName("de-obfuscate method descriptor object return") + public void deobfMethodDescriptorObjectReturn() { + final MappingSet mappings = mappings(); + + final MethodDescriptor obf = MethodDescriptor.of("()La;"); + final MethodDescriptor deobf = MethodDescriptor.of("()LDemo;"); + + assertEquals(deobf, mappings.deobfuscate(obf)); + } + + @Test + @DisplayName("de-obfuscate method descriptor object return and params") + public void deobfMethodDescriptorObjectReturnAndParams() { + final MappingSet mappings = mappings(); + + final MethodDescriptor obf = MethodDescriptor.of("(La;La;)La;"); + final MethodDescriptor deobf = MethodDescriptor.of("(LDemo;LDemo;)LDemo;"); + + assertEquals(deobf, mappings.deobfuscate(obf)); + } + private static MappingSet mappings() { final MappingSet mappings = MappingSet.create(); diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/XSrgReaderTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/XSrgReaderTest.java index 93b4d6c..82f8896 100644 --- a/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/XSrgReaderTest.java +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/io/srg/XSrgReaderTest.java @@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.cadixdev.lorenz.io.MappingFormats; -import org.cadixdev.lorenz.io.srg.SrgReader; +import org.cadixdev.lorenz.io.srg.xsrg.XSrgReader; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -43,7 +43,7 @@ public XSrgReaderTest() throws Exception { public void ignoresPackages() throws IOException { // This test ensures that package mappings won't set off any exceptions // as they are valid input - even though Lorenz won't parse them :p - final SrgReader.Processor parser = new SrgReader.Processor(); + final XSrgReader.Processor parser = new XSrgReader.Processor(); parser.accept("PK: abc uk/jamierocks/Example"); } @@ -51,7 +51,7 @@ public void ignoresPackages() throws IOException { public void tooLongInput() throws IOException { // This test should set off the first case where IllegalArgumentException // is thrown - final SrgReader.Processor parser = new SrgReader.Processor(); + final XSrgReader.Processor parser = new XSrgReader.Processor(); assertThrows(IllegalArgumentException.class, () -> { parser.accept("this is a faulty mapping because it is too long"); }); @@ -61,7 +61,7 @@ public void tooLongInput() throws IOException { public void invalidInput() throws IOException { // This test should set off the first case where IllegalArgumentException // is thrown - final SrgReader.Processor parser = new SrgReader.Processor(); + final XSrgReader.Processor parser = new XSrgReader.Processor(); assertThrows(IllegalArgumentException.class, () -> { parser.accept("PK: TooShort"); }); From 576d28d7985dd6fcabe0f77c4d1a7d8f3d424c14 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Tue, 19 Jan 2021 17:11:43 +0000 Subject: [PATCH 5/6] Don't inherit field mappings where they conflict with a local one Fixes GH-29. --- changelogs/0.5.7.md | 4 + .../impl/model/AbstractClassMappingImpl.java | 6 + .../org/cadixdev/lorenz/util/BinaryTool.java | 1 + .../cadixdev/lorenz/test/CompletionTest.java | 182 ++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 lorenz/src/test/java/org/cadixdev/lorenz/test/CompletionTest.java diff --git a/changelogs/0.5.7.md b/changelogs/0.5.7.md index a4862c2..fe2867c 100644 --- a/changelogs/0.5.7.md +++ b/changelogs/0.5.7.md @@ -8,3 +8,7 @@ Lorenz 0.5.7 - `MappingSet#deobfuscate(FieldType)` will now correctly de-obfuscate object types of inner classes, where the parent class has a mapping - but the inner class does not. +- [GH-29]\: Avoid inheriting field mappings from a parent where the child class has + a field of its own, of the same signature. + +[GH-29]: https://github.com/CadixDev/Lorenz/issues/29 diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/impl/model/AbstractClassMappingImpl.java b/lorenz/src/main/java/org/cadixdev/lorenz/impl/model/AbstractClassMappingImpl.java index e908424..e8d0f5c 100644 --- a/lorenz/src/main/java/org/cadixdev/lorenz/impl/model/AbstractClassMappingImpl.java +++ b/lorenz/src/main/java/org/cadixdev/lorenz/impl/model/AbstractClassMappingImpl.java @@ -226,6 +226,12 @@ public void complete(final InheritanceProvider provider, final InheritanceProvid parentMappings.complete(provider, parent); for (final FieldMapping mapping : parentMappings.getFieldMappings()) { + // If the class has its own field that satisfies the parent's signature, + // then we shouldn't inherit the mapping + if (this.computeFieldMapping(mapping.getSignature()).isPresent()) { + continue; + } + if (parent.canInherit(info, mapping.getSignature())) { this.fields.putIfAbsent(mapping.getSignature(), mapping); } diff --git a/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java b/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java index ef506fa..bd2a5b8 100644 --- a/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java +++ b/lorenz/src/main/java/org/cadixdev/lorenz/util/BinaryTool.java @@ -39,6 +39,7 @@ public final class BinaryTool { * For example, calling {@code from("a$b$c"}} would produce * {@code ["a", "b", "c"]}. * + * @param binaryName The binary name * @return The name hierarchy */ public static String[] from(final String binaryName) { diff --git a/lorenz/src/test/java/org/cadixdev/lorenz/test/CompletionTest.java b/lorenz/src/test/java/org/cadixdev/lorenz/test/CompletionTest.java new file mode 100644 index 0000000..35c1a51 --- /dev/null +++ b/lorenz/src/test/java/org/cadixdev/lorenz/test/CompletionTest.java @@ -0,0 +1,182 @@ +/* + * This file is part of Lorenz, licensed under the MIT License (MIT). + * + * Copyright (c) Jamie Mansfield + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.cadixdev.lorenz.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.cadixdev.bombe.analysis.InheritanceProvider; +import org.cadixdev.bombe.analysis.InheritanceType; +import org.cadixdev.bombe.type.BaseType; +import org.cadixdev.bombe.type.signature.FieldSignature; +import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.lorenz.model.FieldMapping; +import org.cadixdev.lorenz.model.TopLevelClassMapping; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +public final class CompletionTest { + + @Test + @DisplayName("inherits field mapping") + public void inheritsFieldMapping() { + // given: + final MappingSet mappings = mappings(); + final TopLevelClassMapping Parent = mappings.getOrCreateTopLevelClassMapping("Parent"); + final TopLevelClassMapping Child2 = mappings.getOrCreateTopLevelClassMapping("Child2"); + final InheritanceProvider inheritanceProvider = inheritanceProvider(); + + // when: + Parent.complete(inheritanceProvider); + Parent.computeFieldMapping( + new FieldSignature("a", BaseType.INT) + ); + Child2.complete(inheritanceProvider); + + // then: + final Optional mapping = Child2.computeFieldMapping( + new FieldSignature("a", BaseType.INT) + ); + assertTrue(mapping.isPresent(), "Child2 hasn't inherited Parent/a"); + assertEquals("parentField", mapping.get().getDeobfuscatedName()); + } + + @Test + @DisplayName("use correct field mapping") + public void useCorrectFieldMapping() { + // given: + final MappingSet mappings = mappings(); + final TopLevelClassMapping Parent = mappings.getOrCreateTopLevelClassMapping("Parent"); + final TopLevelClassMapping Child1 = mappings.getOrCreateTopLevelClassMapping("Child1"); + final InheritanceProvider inheritanceProvider = inheritanceProvider(); + + // when: + Parent.complete(inheritanceProvider); + Parent.computeFieldMapping( + new FieldSignature("a", BaseType.INT) + ); + Child1.complete(inheritanceProvider); + + // then: + final Optional mapping = Child1.computeFieldMapping( + new FieldSignature("a", BaseType.INT) + ); + assertTrue(mapping.isPresent()); + assertEquals("childField", mapping.get().getDeobfuscatedName()); + } + + /* + This is representing the following: + + Parent Parent + a parentField + + Child1 Child1 + a childField + */ + private static MappingSet mappings() { + final MappingSet mappings = MappingSet.create(); + + final TopLevelClassMapping Parent = + mappings.getOrCreateTopLevelClassMapping("Parent"); + Parent.getOrCreateFieldMapping("a") + .setDeobfuscatedName("parentField"); + + final TopLevelClassMapping Child1 = + mappings.getOrCreateTopLevelClassMapping("Child1"); + Child1.getOrCreateFieldMapping("a") + .setDeobfuscatedName("childField"); + + final TopLevelClassMapping Child2 = + mappings.getOrCreateTopLevelClassMapping("Child2"); + + return mappings; + } + + /* + This is representing the following: + + class Parent { + int a; + } + class Child1 extends Parent { + int a; + } + class Child2 extends Parent { + } + */ + private static InheritanceProvider inheritanceProvider() { + return klass -> { + switch (klass) { + case "Parent": { + final Map fields = Collections.singletonMap( + new FieldSignature("a", BaseType.INT), InheritanceType.PACKAGE_PRIVATE + ); + final Map fieldsByName = Collections.singletonMap( + "a", InheritanceType.PACKAGE_PRIVATE + ); + + return Optional.of(new InheritanceProvider.ClassInfo.Impl( + "Parent", false, + "java/lang/Object", Collections.emptyList(), + fields, fieldsByName, + Collections.emptyMap() + )); + } + case "Child1": { + final Map fields = Collections.singletonMap( + new FieldSignature("a", BaseType.INT), InheritanceType.PACKAGE_PRIVATE + ); + final Map fieldsByName = Collections.singletonMap( + "a", InheritanceType.PACKAGE_PRIVATE + ); + + return Optional.of(new InheritanceProvider.ClassInfo.Impl( + "Child1", false, + "Parent", Collections.emptyList(), + fields, fieldsByName, + Collections.emptyMap() + )); + } + case "Child2": { + return Optional.of(new InheritanceProvider.ClassInfo.Impl( + "Child2", false, + "Parent", Collections.emptyList(), + Collections.emptyMap(), Collections.emptyMap(), + Collections.emptyMap() + )); + } + } + + return Optional.empty(); + }; + } + +} From f15fbeb28507720842069bc32559b169bc47e055 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Mon, 1 Feb 2021 15:07:00 +0000 Subject: [PATCH 6/6] 0.5.7: Release Time --- README.md | 4 ++-- build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 404f9fd..6db6d8e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Lorenz releases can be obtained through Maven Central: ### Gradle ```groovy -implementation 'org.cadixdev:lorenz:0.5.6' +implementation 'org.cadixdev:lorenz:0.5.7' ``` ### Maven @@ -40,7 +40,7 @@ implementation 'org.cadixdev:lorenz:0.5.6' org.cadixdev lorenz - 0.5.6 + 0.5.7 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 2518a56..b841210 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ val isSnapshot = version.toString().endsWith("-SNAPSHOT") allprojects { group = "org.cadixdev" - version = "0.5.7-SNAPSHOT" + version = "0.5.7" } subprojects {