Skip to content

Commit

Permalink
Add JADX decompiler (#27)
Browse files Browse the repository at this point in the history
* Add JADX decompiler

* Use memory cache for JADX

Speeds up things significantly. And yes, the memory is never reclaimed, but once we move the cache to the disk instead, that won't be an issue anymore

* Much more efficient JADX implementation

Now only passes the requested class instead of all input files.

* Fix crash on second decompile

* Un-`synchronize` JADX decompiling

* New JADX implementation can decompile inner classes

Also fix checkstyle issues

* Fix JPMS issues

* Put JADX menu entry underneath Vineflower

* Small clean-up

* Reduce JADX log spam

* Remove JADX repackaging top-level classes workaround
  • Loading branch information
NebelNidas authored Mar 10, 2024
1 parent da3d2b9 commit 2cb3bbd
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 0 deletions.
11 changes: 11 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ dependencies {
implementation "net.fabricmc:cfr:${fabric_cfr_version}"
implementation "org.vineflower:vineflower:${vineflower_version}"
implementation "org.bitbucket.mstrobel:procyon-compilertools:${procyon_version}"
implementation ("io.github.skylot:jadx-core:${jadx_version}") {
exclude group: 'com.android.tools.build', module: 'aapt2-proto'
}
implementation ("io.github.skylot:jadx-java-input:${jadx_version}") {
exclude group: 'io.github.skylot', module: 'raung-disasm'
}
runtimeOnly "org.tinylog:tinylog-impl:${tinylog_version}"
runtimeOnly "org.tinylog:slf4j-tinylog:${tinylog_version}"

Expand Down Expand Up @@ -97,6 +103,11 @@ extraJavaModuleInfo {

// Procyon
automaticModule("org.bitbucket.mstrobel:procyon-compilertools", "procyon.compilertools")

// JADX
automaticModule("io.github.skylot:jadx-core", "jadx.core")
automaticModule("io.github.skylot:jadx-plugins-api", "jadx.plugins.api")
automaticModule("io.github.skylot:jadx-java-input", "jadx.plugins.java_input")
}

application {
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ asm_version = 9.6
fabric_cfr_version = 0.2.1
vineflower_version = 1.9.3
procyon_version = 0.6.0
jadx_version = 1.4.7
mappingio_version = 0.5.0
javaparser_version = 3.25.6
javafx_version = 21.0.1
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/matcher/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -48,6 +50,15 @@ public static <T> Set<T> copySet(Set<T> set) {
}
}

public static String getStackTrace(Throwable t) {
if (t == null) return null;

StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
return sw.toString();
}

public static FileSystem iterateJar(Path archive, boolean autoClose, Consumer<Path> handler) {
boolean existing = false;
FileSystem fs = null;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/matcher/srcprocess/BuiltinDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public enum BuiltinDecompiler {
CFR("CFR", Cfr::new),
VINEFLOWER("Vineflower", Vineflower::new),
JADX("JADX", Jadx::new),
PROCYON("Procyon", Procyon::new);

BuiltinDecompiler(String name, Supplier<? extends Decompiler> supplier) {
Expand Down
75 changes: 75 additions & 0 deletions src/main/java/matcher/srcprocess/Jadx.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package matcher.srcprocess;

import java.io.IOException;
import java.util.function.Consumer;

import jadx.api.CommentsLevel;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.impl.NoOpCodeCache;
import jadx.api.plugins.input.data.IClassData;
import jadx.api.plugins.input.data.ILoadResult;
import jadx.api.plugins.input.data.IResourceData;
import jadx.plugins.input.java.JavaClassReader;
import jadx.plugins.input.java.data.JavaClassData;

import matcher.NameType;
import matcher.Util;
import matcher.type.ClassFeatureExtractor;
import matcher.type.ClassInstance;

public class Jadx implements Decompiler {
@Override
public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) {
String errorMessage = null;
final String fullClassName = cls.getName(NameType.PLAIN, true);

try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
jadx.addCustomLoad(new ILoadResult() {
@Override
public void close() throws IOException { }

@Override
public void visitClasses(Consumer<IClassData> consumer) {
consumer.accept(new JavaClassData(new JavaClassReader(0,
fullClassName + ".class", cls.serialize(nameType))));
}

@Override
public void visitResources(Consumer<IResourceData> consumer) { }

@Override
public boolean isEmpty() {
return false;
}
});
jadx.load();

assert jadx.getClassesWithInners().size() == 1;
return jadx.getClassesWithInners().get(0).getCode();
} catch (Exception e) {
errorMessage = Util.getStackTrace(e);
}

throw new RuntimeException(errorMessage != null ? errorMessage : "JADX couldn't find the requested class");
}

private static final JadxArgs jadxArgs;

static {
jadxArgs = new JadxArgs() {
@Override
public void close() {
return;
}
};
jadxArgs.setCodeCache(NoOpCodeCache.INSTANCE);
jadxArgs.setShowInconsistentCode(true);
jadxArgs.setInlineAnonymousClasses(false);
jadxArgs.setInlineMethods(false);
jadxArgs.setSkipResources(true);
jadxArgs.setRenameValid(false);
jadxArgs.setRespectBytecodeAccModifiers(true);
jadxArgs.setCommentsLevel(CommentsLevel.INFO);
}
}
3 changes: 3 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
requires org.objectweb.asm.tree.analysis;
requires org.objectweb.asm.util;
requires procyon.compilertools;
requires jadx.core;
requires jadx.plugins.api;
requires jadx.plugins.java_input;
requires transitive net.fabricmc.mappingio;

uses matcher.Plugin;
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/tinylog-dev.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
writer = console
writer.format = {date: HH:mm:ss.SSS} [{thread}/{level}]: {message}
writer.level = debug

level@jadx = warn
2 changes: 2 additions & 0 deletions src/main/resources/tinylog.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
writer = console
writer.format = {date: HH:mm:ss} [{level}]: {message}
writer.level = info

level@jadx = error

0 comments on commit 2cb3bbd

Please sign in to comment.