Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make vineflower, fernflower, and forgeflower coexist #870

Closed
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ subprojects {
google()
maven { url 'https://maven.quiltmc.org/repository/release/' }
maven { url 'https://jitpack.io' }
maven { url 'https://www.jetbrains.com/intellij-repository/releases' }
maven { url 'https://maven.minecraftforge.net' }
}

// ======================= DEPENDENCIES ========================
Expand Down
9 changes: 9 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ regex = "0.1.16"
richtextfx = "0.11.3"
treemapfx = "1.1.0"
vineflower = "1.10.1"
fernflower = "242.23726.103"
forgeflower = "2.0.674.2"
annotations = "24.0.0"
wordwrap = "0.1.12"
benmanes-versions = "0.42.0"
gradle-coverage-report-aggregator = "1.3.0"
Expand Down Expand Up @@ -128,6 +131,12 @@ treemapfx = { module = "software.coley:treemap-fx", version.ref = "treemapfx" }

vineflower = { module = "org.vineflower:vineflower", version.ref = "vineflower" }

fernflower = { module = "com.jetbrains.intellij.java:java-decompiler-engine", version.ref = "fernflower" }

forgeflower = { module = "net.minecraftforge:forgeflower", version.ref = "forgeflower" }

annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" }

wordwrap = { module = "com.github.davidmoten:word-wrap", version.ref = "wordwrap" }

[bundles]
Expand Down
3 changes: 2 additions & 1 deletion recaf-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ dependencies {
api(libs.openrewrite)
api(libs.regex)
api(libs.bundles.jasm)
api(libs.vineflower)
// api(libs.vineflower)
api( project(':recaf-relocation'))
api(libs.wordwrap)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package software.coley.recaf.services.decompile;

import java.io.File;

/**
* FakeFile
*
* @author meiMingle
*/
public class FakeFile extends File {

String absolutePath;


public FakeFile(String path) {
super(path);
this.absolutePath = path;
}

@Override
public boolean isDirectory() {
return false;
}

@Override
public String getAbsolutePath() {
return absolutePath;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package software.coley.recaf.services.decompile.fernflower;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.slf4j.event.Level;
import software.coley.observables.ObservableObject;
import software.coley.recaf.services.decompile.BaseDecompilerConfig;

/**
* FernflowerConfig
*
* @author meiMingle
*/
@ApplicationScoped
public class FernflowerConfig extends BaseDecompilerConfig {

@Inject
public FernflowerConfig() {
super("decompiler-fernflower" + CONFIG_SUFFIX);
registerConfigValuesHashUpdates();
}

public ObservableObject<Level> getLoggingLevel() {
return new ObservableObject<>(Level.WARN);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package software.coley.recaf.services.decompile.fernflower;

import jakarta.annotation.Nonnull;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import recaf.relocation.libs.fernflower.org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler;
import recaf.relocation.libs.fernflower.org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import software.coley.recaf.info.InnerClassInfo;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.services.decompile.AbstractJvmDecompiler;
import software.coley.recaf.services.decompile.DecompileResult;
import software.coley.recaf.services.decompile.FakeFile;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.resource.BasicWorkspaceFileResource;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Fernflower decompiler implementation.
*
* @author meiMingle
*/
@ApplicationScoped
public class FernflowerDecompiler extends AbstractJvmDecompiler {
public static final String NAME = "Fernflower";
private final FernflowerConfig config;
private final IFernflowerLogger logger;

/**
* New Fernflower decompiler instance.
*
* @param config Decompiler configuration.
*/
@Inject
public FernflowerDecompiler(@Nonnull FernflowerConfig config) {
// Change this version to be dynamic when / if the Fernflower authors make a function that returns the version...
super(NAME, "242.23726.103", config);
this.config = config;
logger = new FernflowerLogger(config);
}

@Nonnull
@Override
public DecompileResult decompileInternal(@Nonnull Workspace workspace, @Nonnull JvmClassInfo info) {


Map<String, Object> options = new HashMap<>();

options.put("hdc", "0");
options.put("dgs", "1");
options.put("rsy", "1");
options.put("rbr", "1");
options.put("nls", "1");
options.put("ban", "//Recreated by Recaf (powered by FernFlower decompiler)\n\n");
options.put("mpm", 60);
options.put("ind", " ");
options.put("iib", "1");
options.put("vac", "1");
options.put("cps", "1");
options.put("crp", "1");

options.put("bsm", "1");// "decompiler.use.line.mapping"
options.put("__dump_original_lines__", "1");// "decompiler.dump.original.lines"

MyResultSaver saver = new MyResultSaver();
MyBytecodeProvider provider = new MyBytecodeProvider(workspace);

BaseDecompiler decompiler = new BaseDecompiler(
provider,
saver,
options,
logger
);

try {
String path = ((BasicWorkspaceFileResource) workspace.getPrimaryResource()).getFileInfo().getName() + "!" + info.getName() + ".class";
decompiler.addSource(new FakeFile(path));
List<InnerClassInfo> innerClasses = info.getInnerClasses();
innerClasses.forEach(inner -> decompiler.addSource(new FakeFile(((BasicWorkspaceFileResource) workspace.getPrimaryResource()).getFileInfo().getName() + "!" + inner.getName() + ".class")));
decompiler.decompileContext();
if (saver.getResult() == null || saver.getResult().isEmpty()) {
return new DecompileResult(new IllegalStateException("Missing decompilation output"), 0);
}

return new DecompileResult(saver.getResult(), 0);
} catch (Exception e) {
return new DecompileResult(e, 0);
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package software.coley.recaf.services.decompile.fernflower;

import jakarta.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.event.Level;
import recaf.relocation.libs.fernflower.org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import software.coley.observables.ObservableObject;
import software.coley.recaf.analytics.logging.Logging;

/**
* Logger for Fernflower
*
* @author meiMingle
*/
public class FernflowerLogger extends IFernflowerLogger {
private static final Logger logger = Logging.get(FernflowerLogger.class);
private static final String VF_PREFIX = "FF: ";
private final ObservableObject<Level> level;

public FernflowerLogger(@Nonnull FernflowerConfig config) {
this.level = config.getLoggingLevel();
}

@Override
public void writeMessage(String message, Severity severity) {
switch (severity) {
case TRACE -> {
if (level.getValue().compareTo(Level.TRACE) >= 0) logger.trace(VF_PREFIX + message);
}
case INFO -> {
if (level.getValue().compareTo(Level.INFO) >= 0) logger.info(VF_PREFIX + message);
}
case WARN -> {
if (level.getValue().compareTo(Level.WARN) >= 0) logger.warn(VF_PREFIX + message);
}
case ERROR -> logger.error(VF_PREFIX + message);
}
}

@Override
public void writeMessage(String message, Severity severity, Throwable throwable) {
logger.error(VF_PREFIX + message, throwable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package software.coley.recaf.services.decompile.fernflower;

import recaf.relocation.libs.fernflower.org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import software.coley.recaf.path.ClassPathNode;
import software.coley.recaf.workspace.model.Workspace;


/**
* MyBytecodeProvider
*
* @author meiMingle
*/
public class MyBytecodeProvider implements IBytecodeProvider {
Workspace workspace;

public MyBytecodeProvider(Workspace workspace) {
this.workspace = workspace;
}

@Override
public byte[] getBytecode(String absolutePath, String internalPath) {
String[] split = absolutePath.split("!");
String path = split[0];
String name = split[1];

ClassPathNode aClass = workspace.findClass(name.substring(0, name.lastIndexOf('.')));
return aClass.getValue().asJvmClass().getBytecode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package software.coley.recaf.services.decompile.fernflower;


import recaf.relocation.libs.fernflower.org.jetbrains.java.decompiler.main.extern.IResultSaver;

import java.util.jar.Manifest;

/**
* MyResultSaver
*
* @author meiMingle
*/
public class MyResultSaver implements IResultSaver {
private String result = "";
private int[] mapping;

public final String getResult() {
return this.result;
}

public final void setResult(String string) {
this.result = string;
}

public final int[] getMapping() {
return this.mapping;
}

public final void setMapping(int[] nArray) {
this.mapping = nArray;
}

@Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
if (((CharSequence) this.result).length() == 0) {
this.result = content;
this.mapping = mapping;
}
}

@Override
public void saveFolder(String path) {
// no-op
}

@Override
public void copyFile(String source, String path, String entryName) {
// no-op
}

@Override
public void createArchive(String path, String archiveName, Manifest manifest) {
// no-op
}

@Override
public void saveDirEntry(String s, String s1, String s2) {
// no-op
}

@Override
public void copyEntry(String s, String s1, String s2, String s3) {
// no-op
}

@Override
public void saveClassEntry(String s, String s1, String s2, String s3, String s4) {
// no-op
}

@Override
public void closeArchive(String s, String s1) {
// no-op
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package software.coley.recaf.services.decompile.forgeflower;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.slf4j.event.Level;
import software.coley.observables.ObservableObject;
import software.coley.recaf.services.decompile.BaseDecompilerConfig;

/**
* ForgeflowerConfig
*
* @author meiMingle
*/
@ApplicationScoped
public class ForgeflowerConfig extends BaseDecompilerConfig {

@Inject
public ForgeflowerConfig() {
super("decompiler-forgeflower" + CONFIG_SUFFIX);
registerConfigValuesHashUpdates();
}

public ObservableObject<Level> getLoggingLevel() {
return new ObservableObject<>(Level.WARN);
}
}
Loading
Loading