Skip to content

Commit

Permalink
Add fill-class-mappings command
Browse files Browse the repository at this point in the history
  • Loading branch information
IotaBread committed Feb 26, 2023
1 parent b9edb8d commit 277e073
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package cuchaz.enigma.command;

import cuchaz.enigma.Enigma;
import cuchaz.enigma.EnigmaProject;
import cuchaz.enigma.ProgressListener;
import cuchaz.enigma.analysis.index.JarIndex;
import cuchaz.enigma.classprovider.ClasspathClassProvider;
import cuchaz.enigma.translation.mapping.EntryMapping;
import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
import cuchaz.enigma.translation.mapping.tree.EntryTree;
import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
import cuchaz.enigma.translation.representation.entry.ClassEntry;
import cuchaz.enigma.translation.representation.entry.ParentedEntry;
import cuchaz.enigma.utils.Utils;
import org.tinylog.Logger;

import java.nio.file.Path;
import java.util.List;

public class FillClassMappingsCommand extends Command {
public static final String NAME = "fill-class-mappings";

protected FillClassMappingsCommand() {
super(NAME);
}

@Override
public String getUsage() {
return "<in-jar> <source> <result> <result-format>";
}

@Override
public boolean isValidArgument(int length) {
return length == 4;
}

@Override
public void run(String... args) throws Exception {
Path inJar = getReadablePath(getArg(args, 0, "in-jar", true));
Path source = getReadablePath(getArg(args, 1, "source", true));
Path result = getWritablePath(getArg(args, 2, "result", true));
String resultFormat = getArg(args, 3, "result-format", true);

run(inJar, source, result, resultFormat);
}

public static void run(Path jar, Path source, Path result, String resultFormat) throws Exception {
boolean debug = shouldDebug(NAME);

Logger.info("Reading JAR...");
Enigma enigma = Enigma.create();
EnigmaProject project = enigma.openJar(jar, new ClasspathClassProvider(), ProgressListener.none());

Logger.info("Reading mappings...");
MappingSaveParameters saveParameters = enigma.getProfile().getMappingSaveParameters();
EntryTree<EntryMapping> mappings = readMappings(source, ProgressListener.none(), saveParameters);
project.setMappings(mappings);

if (debug) {
mappings = new DeltaTrackingTree<>(mappings);
}

Logger.info("Adding mappings...");
JarIndex index = project.getJarIndex();
List<ClassEntry> rootEntries = mappings.getRootNodes().map(EntryTreeNode::getEntry)
.filter(entry -> entry instanceof ClassEntry)
.map(entry -> (ClassEntry) entry)
.toList();
for (ClassEntry rootEntry : rootEntries) {
// These entries already have a mapping tree node
recursiveAddMappings(mappings, index, rootEntry, false);
}

Logger.info("Writing mappings...");
Utils.delete(result);
MappingCommandsUtil.write(mappings, resultFormat, result, saveParameters);

if (debug) {
writeDebugDelta((DeltaTrackingTree<EntryMapping>) mappings, result);
}
}

private static void recursiveAddMappings(EntryTree<EntryMapping> mappings, JarIndex index, ClassEntry entry, boolean addMapping) {
EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
boolean hasMapping = node != null && node.hasValue() && node.getValue().targetName() != null;

Logger.debug("Entry {} {} a mapping", entry, hasMapping ? "has" : "doesn't have");
if (!hasMapping && addMapping) {
Logger.debug("Adding mapping for {}", entry);
mappings.insert(entry, EntryMapping.DEFAULT);
}

List<ParentedEntry<?>> children = index.getChildrenByClass().get(entry);
for (ParentedEntry<?> child : children) {
if (child instanceof ClassEntry classChild) {
recursiveAddMappings(mappings, index, classChild, addMapping || hasMapping);
}
}
}
}
1 change: 1 addition & 0 deletions enigma-cli/src/main/java/cuchaz/enigma/command/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ private static void logEnigmaInfo() {
register(new MapSpecializedMethodsCommand());
register(new InsertProposedMappingsCommand());
register(new DropInvalidMappingsCommand());
register(new FillClassMappingsCommand());
}

private static final class CommandHelpException extends IllegalArgumentException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package cuchaz.enigma.command;

import cuchaz.enigma.ProgressListener;
import cuchaz.enigma.translation.mapping.EntryMapping;
import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
import cuchaz.enigma.translation.mapping.serde.MappingFormat;
import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
import cuchaz.enigma.translation.mapping.tree.EntryTree;
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.MethodEntry;
import org.junit.jupiter.api.Test;

import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

public class FillClassMappingsCommandTest {
private static final Path JAR = Path.of("../enigma/build/test-obf/innerClasses.jar");
private static final Path MAPPINGS;

private static final ClassEntry A = new ClassEntry("a");
private static final MethodEntry A_METHOD = MethodEntry.parse("a", "a", "()V");
private static final ClassEntry A_ANONYMOUS = new ClassEntry("a$1");
private static final ClassEntry B = new ClassEntry("b");
private static final MethodEntry B_METHOD = MethodEntry.parse("b", "a", "(Ld;)V");
private static final ClassEntry B_ANONYMOUS = new ClassEntry("b$1");
private static final ClassEntry C = new ClassEntry("c");
private static final FieldEntry C_FIELD = FieldEntry.parse("c", "a", "c$a");
private static final ClassEntry C_INNER = new ClassEntry("c$a");
private static final FieldEntry C_INNER_FIELD = FieldEntry.parse("c$a", "a", "I");
private static final ClassEntry D = new ClassEntry("d");
private static final ClassEntry D_INNER = new ClassEntry("d$a");
private static final ClassEntry E = new ClassEntry("e");
private static final MethodEntry E_METHOD_1 = MethodEntry.parse("e", "a", "()Ljava/lang/Object;");
private static final MethodEntry E_METHOD_2 = MethodEntry.parse("e", "a", "()Ljava/lang/String;");
private static final ClassEntry E_ANONYMOUS = new ClassEntry("e$1");
private static final ClassEntry F = new ClassEntry("f");
private static final ClassEntry F_LEVEL_1 = new ClassEntry("f$a");
private static final FieldEntry F_LEVEL_1_FIELD = FieldEntry.parse("f$a", "a", "I");
private static final ClassEntry F_LEVEL_2 = new ClassEntry("f$a$a");
private static final FieldEntry F_LEVEL_2_FIELD = FieldEntry.parse("f$a$a", "a", "I");
private static final ClassEntry F_LEVEL_3 = new ClassEntry("f$a$a$a");
private static final FieldEntry F_LEVEL_3_FIELD = FieldEntry.parse("f$a$a$a", "a", "I");

@Test
public void test() throws Exception {
Path resultFile = Files.createTempFile("fillClassMappings", ".mappings");
FillClassMappingsCommand.run(JAR, MAPPINGS, resultFile, MappingFormat.ENIGMA_FILE.name());

EntryTree<EntryMapping> result = MappingFormat.ENIGMA_FILE.read(resultFile, ProgressListener.none(), new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF));

assertEquals("A_Anonymous", getName(result, A));
assertNotNull(result.findNode(A_ANONYMOUS));
assertNull(getName(result, A_METHOD));
assertNull(getName(result, A_ANONYMOUS));

assertNotNull(result.findNode(B));
assertNull(getName(result, B));
assertEquals("foo", getName(result, B_METHOD));
assertNull(result.findNode(B_ANONYMOUS));

assertNotNull(result.findNode(C));
assertNull(getName(result, C));
assertNull(getName(result, C_FIELD));
assertNotNull(result.findNode(C_INNER));
assertNull(getName(result, C_INNER));
assertEquals("a", getName(result, C_INNER_FIELD));

assertNotNull(result.findNode(D));
assertNull(getName(result, D));
assertEquals("Inner", getName(result, D_INNER));

assertNotNull(result.findNode(E));
assertNull(getName(result, E));
assertNull(getName(result, E_METHOD_1));
assertEquals("outerMethod", getName(result, E_METHOD_2));
assertNull(result.findNode(E_ANONYMOUS));

assertNotNull(result.findNode(F));
assertNull(getName(result, F));
assertNotNull(result.findNode(F_LEVEL_1));
assertNull(getName(result, F_LEVEL_1));
assertNull(getName(result, F_LEVEL_1_FIELD));
assertNotNull(result.findNode(F_LEVEL_2));
assertEquals("Level2", getName(result, F_LEVEL_2));
assertNull(getName(result, F_LEVEL_2_FIELD));
assertNotNull(result.findNode(F_LEVEL_3));
assertNull(getName(result, F_LEVEL_3));
assertNull(getName(result, F_LEVEL_3_FIELD));
}

private static String getName(EntryTree<EntryMapping> mappings, Entry<?> entry) {
if (!mappings.contains(entry)) {
return null;
}

EntryMapping mapping = mappings.get(entry);
return mapping != null ? mapping.targetName() : null;
}

static {
try {
MAPPINGS = Path.of(FillClassMappingsCommandTest.class.getResource("/fillClassMappings/").toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CLASS a A_Anonymous
2 changes: 2 additions & 0 deletions enigma-cli/src/test/resources/fillClassMappings/b.mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CLASS b
METHOD a foo (Ld;)V
3 changes: 3 additions & 0 deletions enigma-cli/src/test/resources/fillClassMappings/c.mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CLASS c
CLASS a
FIELD a a I
2 changes: 2 additions & 0 deletions enigma-cli/src/test/resources/fillClassMappings/d.mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CLASS d
CLASS a Inner
2 changes: 2 additions & 0 deletions enigma-cli/src/test/resources/fillClassMappings/e.mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CLASS e
METHOD a outerMethod ()Ljava/lang/String;
3 changes: 3 additions & 0 deletions enigma-cli/src/test/resources/fillClassMappings/f.mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CLASS f
CLASS a
CLASS a Level2
5 changes: 5 additions & 0 deletions enigma-cli/src/test/resources/tinylog-test.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
writingthread = true

writerConsole = console
writerConsole.format = {date: HH:mm:ss.SSS} [{level}]: {message}
writerConsole.level = debug
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@

package cuchaz.enigma.inputs.innerClasses;

@SuppressWarnings("Convert2Lambda")
public class A_Anonymous {
public void foo() {
Runnable runnable = () -> {
// don't care
Runnable runnable = new Runnable() {
@Override
public void run() {
// don't care
}
};
runnable.run();
}
Expand Down

0 comments on commit 277e073

Please sign in to comment.