diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java index 75ef225ae..3d20b2b5a 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java @@ -3,17 +3,21 @@ 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.command.checks.CheckFailureException; +import cuchaz.enigma.command.checks.CheckInvalidMappings; +import cuchaz.enigma.command.checks.CheckNamedSyntheticEntry; +import cuchaz.enigma.command.checks.MappingCheck; +import cuchaz.enigma.command.checks.CheckPackageVisibility; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingFormat; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import cuchaz.enigma.translation.representation.entry.ClassEntry; import java.nio.file.Path; +import java.util.HashSet; +import java.util.LinkedList; import java.util.Set; -import java.util.stream.Collectors; public class CheckMappingsCommand extends Command { @@ -50,29 +54,30 @@ public void run(String... args) throws Exception { EntryTree mappings = format.read(fileMappings, ProgressListener.none(), saveParameters); project.setMappings(mappings); - JarIndex idx = project.getJarIndex(); - - boolean error = false; - - for (Set partition : idx.getPackageVisibilityIndex().getPartitions()) { - long packages = partition.stream() - .map(project.getMapper()::deobfuscate) - .map(ClassEntry::getPackageName) - .distinct() - .count(); - if (packages > 1) { - error = true; - System.err.println("ERROR: Must be in one package:\n" + partition.stream() - .map(project.getMapper()::deobfuscate) - .map(ClassEntry::toString) - .sorted() - .collect(Collectors.joining("\n")) - ); - } + Set checks = new HashSet<>(); + checks.add(new CheckPackageVisibility()); + checks.add(new CheckInvalidMappings()); + checks.add(new CheckNamedSyntheticEntry()); + + LinkedList errors = new LinkedList<>(); + LinkedList warnings = new LinkedList<>(); + + for (MappingCheck check : checks) { + check.findErrors(project, check.failOnError() ? errors : warnings); } - if (error) { - throw new IllegalStateException("Errors in package visibility detected, see SysErr above"); + System.out.printf("%d warnings:\n", warnings.size()); + for (CheckFailureException warning : warnings) { + System.out.println(warning.getMessage()); + } + + System.out.printf("%d errors:\n", errors.size()); + if (!errors.isEmpty()) { + for (CheckFailureException error : errors) { + System.err.println(error.getMessage()); + } + + throw new IllegalStateException("Mappings check failed, see above output"); } } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckFailureException.java b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckFailureException.java new file mode 100644 index 000000000..b07ec05cf --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckFailureException.java @@ -0,0 +1,7 @@ +package cuchaz.enigma.command.checks; + +public class CheckFailureException extends RuntimeException { + public CheckFailureException(String message) { + super(message); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckInvalidMappings.java b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckInvalidMappings.java new file mode 100644 index 000000000..5be90aa2b --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckInvalidMappings.java @@ -0,0 +1,18 @@ +package cuchaz.enigma.command.checks; + +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.translation.representation.entry.Entry; + +import java.util.Collection; + +public class CheckInvalidMappings implements MappingCheck { + @Override + public void findErrors(EnigmaProject project, Collection errors) { + Collection> invalidEntries = project.dropMappings(ProgressListener.none()); + + for (Entry invalidEntry : invalidEntries) { + errors.add(new CheckFailureException("Found invalid mapping entry: " + invalidEntry.toString())); + } + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckNamedSyntheticEntry.java b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckNamedSyntheticEntry.java new file mode 100644 index 000000000..70be8d9e5 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckNamedSyntheticEntry.java @@ -0,0 +1,28 @@ +package cuchaz.enigma.command.checks; + +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.analysis.index.EntryIndex; +import cuchaz.enigma.translation.representation.entry.DefEntry; +import cuchaz.enigma.translation.representation.entry.Entry; + +import java.util.Collection; + +public class CheckNamedSyntheticEntry implements MappingCheck { + @Override + public void findErrors(EnigmaProject project, Collection errors) { + EntryIndex entryIndex = project.getJarIndex().getEntryIndex(); + + check(entryIndex.getClasses(), project, errors); + check(entryIndex.getMethods(), project, errors); + check(entryIndex.getFields(), project, errors); + } + + private void check(Collection> entries, EnigmaProject project, Collection errors) { + for (Entry entry : entries) { + DefEntry defEntry = (DefEntry) entry; + if (defEntry.getAccess().isSynthetic() && project.getMapper().hasDeobfMapping(entry)) { + errors.add(new CheckFailureException(String.format("Synthetic entry (%s) has a debof name (%s)", entry, project.getMapper().getDeobfMapping(entry).getTargetName()))); + } + } + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckPackageVisibility.java b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckPackageVisibility.java new file mode 100644 index 000000000..ff6df3059 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/CheckPackageVisibility.java @@ -0,0 +1,38 @@ +package cuchaz.enigma.command.checks; + +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.representation.entry.ClassEntry; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +public class CheckPackageVisibility implements MappingCheck { + @Override + public void findErrors(EnigmaProject project, Collection errors) { + JarIndex idx = project.getJarIndex(); + + for (Set partition : idx.getPackageVisibilityIndex().getPartitions()) { + long packages = partition.stream() + .map(project.getMapper()::deobfuscate) + .map(ClassEntry::getPackageName) + .distinct() + .count(); + + if (packages > 1) { + errors.add(new CheckFailureException("ERROR: Must be in one package:\n" + partition.stream() + .map(project.getMapper()::deobfuscate) + .map(ClassEntry::toString) + .sorted() + .collect(Collectors.joining("\n")) + )); + } + } + } + + @Override + public boolean failOnError() { + return false; + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/checks/MappingCheck.java b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/MappingCheck.java new file mode 100644 index 000000000..fbdee8dbe --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/checks/MappingCheck.java @@ -0,0 +1,13 @@ +package cuchaz.enigma.command.checks; + +import cuchaz.enigma.EnigmaProject; + +import java.util.Collection; + +public interface MappingCheck { + void findErrors(EnigmaProject project, Collection errors); + + default boolean failOnError() { + return true; + } +} diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index a01eca194..49051d681 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java @@ -97,13 +97,15 @@ public EntryRemapper getMapper() { return mapper; } - public void dropMappings(ProgressListener progress) { + public Collection> dropMappings(ProgressListener progress) { DeltaTrackingTree mappings = mapper.getObfToDeobf(); Collection> dropped = dropMappings(mappings, progress); for (Entry entry : dropped) { mappings.trackChange(entry); } + + return dropped; } private Collection> dropMappings(EntryTree mappings, ProgressListener progress) {