Skip to content

Commit

Permalink
Close #40: ClassProcessor API
Browse files Browse the repository at this point in the history
Add addPackage(s) methods to mapping writers
Small improvements
  • Loading branch information
XiaoPangxie732 committed Apr 28, 2022
1 parent cdd82b2 commit 245f524
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 108 deletions.
36 changes: 13 additions & 23 deletions src/main/java/cn/maxpixel/mcdecompiler/ClassifiedDeobfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@

package cn.maxpixel.mcdecompiler;

import cn.maxpixel.mcdecompiler.asm.*;
import cn.maxpixel.mcdecompiler.asm.ClassProcessor;
import cn.maxpixel.mcdecompiler.asm.ClassifiedMappingRemapper;
import cn.maxpixel.mcdecompiler.asm.ExtraClassesInformation;
import cn.maxpixel.mcdecompiler.mapping.Mapping;
import cn.maxpixel.mcdecompiler.mapping.NameGetter;
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
import cn.maxpixel.mcdecompiler.mapping.PairedMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassMapping;
Expand All @@ -31,10 +32,7 @@
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectLists;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -48,8 +46,6 @@
import java.util.logging.Logger;
import java.util.stream.Stream;

import static cn.maxpixel.mcdecompiler.decompiler.ForgeFlowerDecompiler.FERNFLOWER_ABSTRACT_PARAMETER_NAMES;

public class ClassifiedDeobfuscator {
private static final Logger LOGGER = Logging.getLogger("ClassifiedDeobfuscator");
private static final DeobfuscateOptions DEFAULT_OPTIONS = new DeobfuscateOptions() {
Expand All @@ -68,6 +64,11 @@ public boolean reverse() {
return false;
}
};

static {
ClassProcessor.fetchOptions();
}

private final Object2ObjectOpenHashMap<String, ? extends ClassMapping<? extends Mapping>> mappings;
private final ClassifiedMappingRemapper mappingRemapper;
private final DeobfuscateOptions options;
Expand Down Expand Up @@ -125,26 +126,15 @@ public ClassifiedDeobfuscator deobfuscate(Path source, Path target) throws IOExc
}
});
mappingRemapper.setExtraClassesInformation(info);
if(options.rvn()) VariableNameGenerator.startRecord();
ClassProcessor.beforeRunning(options, targetNamespace, mappingRemapper);
paths.forEach(path -> {
try {
String classKeyName = NamingUtil.asNativeName0(path.toString().substring(1));
if(mappings.containsKey(classKeyName)) {
ClassWriter writer = new ClassWriter(0);
ClassReader reader = new ClassReader(IOUtil.readAllBytes(path));
boolean isRecord = (reader.getAccess() & Opcodes.ACC_RECORD) != 0;
ClassWriter writer = new ClassWriter(0);
ClassMapping<? extends Mapping> cm = mappings.get(classKeyName);
ClassProcessor processor = new ClassProcessor(parent -> {
ClassVisitor cv = parent;
if(options.rvn()) cv = new VariableNameGenerator(cv);
if(isRecord) cv = new RecordNameRemapper(cv);
if(cm.mapping instanceof NameGetter.Namespaced ngn) {
ngn.setMappedNamespace(targetNamespace);
cv = new LVTRemapper(cv, (ClassMapping<NamespacedMapping>) cm, mappingRemapper);
}
return new RuntimeParameterAnnotationFixer(new ClassRemapper(cv, mappingRemapper));
}, writer);
reader.accept(processor.getVisitor(), 0);
reader.accept(ClassProcessor.getVisitor(writer, options, reader, cm, targetNamespace, mappingRemapper), 0);
try(OutputStream os = Files.newOutputStream(FileUtil.ensureFileExist(targetFs
.getPath(cm.mapping.getMappedName().concat(".class"))))) {
os.write(writer.toByteArray());
Expand All @@ -166,14 +156,14 @@ public ClassifiedDeobfuscator deobfuscate(Path source, Path target) throws IOExc
LOGGER.log(Level.WARNING, "Error when remapping classes or coping files", e);
}
});
if(options.rvn()) VariableNameGenerator.endRecord(Properties.TEMP_DIR.resolve(FERNFLOWER_ABSTRACT_PARAMETER_NAMES));
ClassProcessor.afterRunning(options, targetNamespace, mappingRemapper);
} catch(IOException e) {
LOGGER.log(Level.WARNING, "Error when deobfuscating", e);
}
return this;
}

interface DeobfuscateOptions {
public interface DeobfuscateOptions {
boolean includeOthers();

boolean rvn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package cn.maxpixel.mcdecompiler;

import cn.maxpixel.mcdecompiler.asm.ClassProcessor;
import cn.maxpixel.mcdecompiler.util.Logging;
import cn.maxpixel.mcdecompiler.util.Utils;
import joptsimple.*;
Expand Down Expand Up @@ -95,6 +96,7 @@ public URL convert(String value) {
ArgumentAcceptingOptionSpec<Path> extraJarsO = parser.accepts("extraJars", "Extra jars that will be used to get the " +
"class information").withRequiredArg().withValuesConvertedBy(new PathConverter(PathProperties.FILE_EXISTING));
AbstractOptionSpec<Void> help = parser.acceptsAll(of("h", "?", "help"), "For help").forHelp();
ClassProcessor.registerCommandLineOptions(parser);

if(args == null) {
printHelp(parser);
Expand Down Expand Up @@ -134,6 +136,7 @@ public URL convert(String value) {
options.valueOfOptional(outputO).ifPresent(builder::output);
options.valueOfOptional(outputDecompO).ifPresent(builder::outputDecomp);
builder.addExtraJars(options.valuesOf(extraJarsO));
ClassProcessor.acceptCommandLineValues(options);

MinecraftDecompiler md = new MinecraftDecompiler(builder.build());
md.deobfuscate();
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/cn/maxpixel/mcdecompiler/Properties.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package cn.maxpixel.mcdecompiler;

import org.jetbrains.annotations.NotNull;

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

Expand All @@ -29,8 +31,8 @@ public static Path getDownloadedProguardMappingPath(String version, Info.SideTyp
return DOWNLOAD_DIR.resolve(version).resolve(type + "_mappings.txt");
}

public static Path getDownloadedDecompilerPath(Info.DecompilerType type) {
if(Objects.requireNonNull(type) == Info.DecompilerType.USER_DEFINED) throw new UnsupportedOperationException();
public static Path getDownloadedDecompilerPath(@NotNull Info.DecompilerType type) {
if(type == Info.DecompilerType.USER_DEFINED) throw new UnsupportedOperationException();
return DOWNLOAD_DIR.resolve("decompiler").resolve(Objects.requireNonNull(type) + ".jar");
}

Expand Down
170 changes: 159 additions & 11 deletions src/main/java/cn/maxpixel/mcdecompiler/asm/ClassProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,184 @@

package cn.maxpixel.mcdecompiler.asm;

import cn.maxpixel.mcdecompiler.ClassifiedDeobfuscator;
import cn.maxpixel.mcdecompiler.Properties;
import cn.maxpixel.mcdecompiler.mapping.Mapping;
import cn.maxpixel.mcdecompiler.mapping.NameGetter;
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassMapping;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import joptsimple.OptionSpecBuilder;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;

import java.io.IOException;
import java.util.List;
import java.util.ServiceLoader;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

public class ClassProcessor {//TODO
private final ClassVisitor visitor;
import static cn.maxpixel.mcdecompiler.decompiler.ForgeFlowerDecompiler.FERNFLOWER_ABSTRACT_PARAMETER_NAMES;

public ClassProcessor(Function<ClassVisitor, ClassVisitor> visitor, ClassWriter writer) {
this.visitor = visitor.apply(writer);
@ApiStatus.Experimental
public final class ClassProcessor {
private static final ServiceLoader<Process> LOADER = ServiceLoader.load(Process.class);
private static final Process[] BEFORE = LOADER.stream().map(ServiceLoader.Provider::get)
.filter(pro -> pro.getState() == Process.State.BEFORE).toArray(Process[]::new);
private static final Process[] AFTER = LOADER.stream().map(ServiceLoader.Provider::get)
.filter(pro -> pro.getState() == Process.State.AFTER).toArray(Process[]::new);

private ClassProcessor() {
throw new AssertionError("No instances");
}

public static void registerCommandLineOptions(OptionParser parser) {
CoreProcess.INSTANCE.registerCommandLineOptions(parser::accepts, parser::accepts);
for(Process process : LOADER) {
process.registerCommandLineOptions((option) -> parser.accepts(process.getName() + '.' + option),
(option, description) -> parser.accepts(process.getName() + '.' + option, description));
}
}

public static void acceptCommandLineValues(OptionSet options) {
CoreProcess.INSTANCE.acceptCommandLineValues(options::has, options::hasArgument, options::valueOf, options::valuesOf);
for(Process process : LOADER) {
process.acceptCommandLineValues(options::has, options::hasArgument, options::valueOf, options::valuesOf);
}
}

public ClassVisitor getVisitor() {
return visitor;
public static void fetchOptions() {
for(Process process : LOADER) {
process.fetchOptions();
}
}

public static void beforeRunning(ClassifiedDeobfuscator.DeobfuscateOptions options, @Nullable String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) throws IOException {
CoreProcess.INSTANCE.beforeRunning(options, targetNamespace, mappingRemapper);
for(Process process : LOADER) {
process.beforeRunning(options, targetNamespace, mappingRemapper);
}
}

public static void afterRunning(ClassifiedDeobfuscator.DeobfuscateOptions options, @Nullable String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) throws IOException {
CoreProcess.INSTANCE.afterRunning(options, targetNamespace, mappingRemapper);
for(Process process : LOADER) {
process.afterRunning(options, targetNamespace, mappingRemapper);
}
}

public static ClassVisitor getVisitor(ClassWriter writer, ClassifiedDeobfuscator.DeobfuscateOptions options, ClassReader reader,
ClassMapping<? extends Mapping> mapping, String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) {
ClassVisitor cv = writer;
for(Process process : AFTER) {
cv = process.getVisitor(options, reader, mapping, targetNamespace, mappingRemapper).apply(cv);
}
cv = CoreProcess.INSTANCE.getVisitor(options, reader, mapping, targetNamespace, mappingRemapper).apply(cv);
for(Process process : BEFORE) {
cv = process.getVisitor(options, reader, mapping, targetNamespace, mappingRemapper).apply(cv);
}
return cv;
}

public interface Process {
enum State {
/**
* Run before the class is remapped
*/
BEFORE,
AFTER
/**
* Run after the class is remapped
*/
AFTER,
/**
* <b>THIS IS INTERNALLY USED BY {@link CoreProcess}</b><br>
* Others that use this state will be skipped
*/
@ApiStatus.Internal
CORE
}

String name();
String getName();

State getState();

void beforeRunning();
default void registerCommandLineOptions(Function<String, OptionSpecBuilder> accept,
BiFunction<String, String, OptionSpecBuilder> acceptWithDescription) {
}

default <V> void acceptCommandLineValues(Predicate<OptionSpec<?>> has, Predicate<OptionSpec<?>> hasArgument,
Function<OptionSpec<V>, V> valueOf, Function<OptionSpec<V>, List<V>> valuesOf) {
}

/**
* Your own logic to fetch options of your process
*/
default void fetchOptions() {
}

default void beforeRunning(ClassifiedDeobfuscator.DeobfuscateOptions options, @Nullable String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) throws IOException {
}

default void afterRunning(ClassifiedDeobfuscator.DeobfuscateOptions options, @Nullable String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) throws IOException {
}

Function<ClassVisitor, ClassVisitor> getVisitor(ClassifiedDeobfuscator.DeobfuscateOptions options, ClassReader reader,
ClassMapping<? extends Mapping> mapping, @Nullable String targetNamespace,
ClassifiedMappingRemapper mappingRemapper);
}

void afterRunning();
private enum CoreProcess implements Process {
INSTANCE;

Function<ClassVisitor, ClassVisitor> getVisitor();
@Override
public String getName() {
return "core";
}

@Override
public State getState() {
return State.CORE;
}

@Override
public void beforeRunning(ClassifiedDeobfuscator.DeobfuscateOptions options, String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) {
if(options.rvn()) VariableNameGenerator.startRecord();
}

@Override
public void afterRunning(ClassifiedDeobfuscator.DeobfuscateOptions options, String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) throws IOException {
if(options.rvn()) VariableNameGenerator.endRecord(Properties.TEMP_DIR.resolve(FERNFLOWER_ABSTRACT_PARAMETER_NAMES));
}

@Override
public Function<ClassVisitor, ClassVisitor> getVisitor(ClassifiedDeobfuscator.DeobfuscateOptions options, ClassReader reader,
ClassMapping<? extends Mapping> mapping, String targetNamespace,
ClassifiedMappingRemapper mappingRemapper) {
return parent -> {
ClassVisitor cv = parent;
if(options.rvn()) cv = new VariableNameGenerator(cv);
if((reader.getAccess() & Opcodes.ACC_RECORD) != 0) cv = new RecordNameRemapper(cv);
if(mapping.mapping instanceof NameGetter.Namespaced ngn) {
ngn.setMappedNamespace(targetNamespace);
cv = new LVTRemapper(cv, (ClassMapping<NamespacedMapping>) mapping, mappingRemapper);
}
return new RuntimeParameterAnnotationFixer(new ClassRemapper(cv, mappingRemapper));
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import cn.maxpixel.mcdecompiler.util.DownloadUtil;
import cn.maxpixel.mcdecompiler.util.Logging;
import cn.maxpixel.mcdecompiler.util.Utils;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.net.URI;
Expand All @@ -48,7 +49,7 @@ public SourceType getSourceType() {
}

@Override
public void decompile(Path source, Path target) throws IOException {
public void decompile(@NotNull Path source, @NotNull Path target) throws IOException {
checkArgs(source, target);
try {
if(cl == null) cl = new ExternalJarClassLoader(new URL[] {decompilerJarPath.toUri().toURL()}, getClass().getClassLoader());
Expand Down
Loading

0 comments on commit 245f524

Please sign in to comment.