Skip to content

Commit

Permalink
add fernflower close #16
Browse files Browse the repository at this point in the history
  • Loading branch information
GraxCode committed May 15, 2020
1 parent 93f2ed5 commit 137e957
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 17 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ ext {

repositories {
mavenCentral()
flatDir {
dirs 'libs'
}
}

dependencies {
Expand All @@ -41,6 +44,7 @@ dependencies {
compile "org.ow2.asm:asm-util:${asm_ver}"

compile 'org.benf:cfr:0.149'
implementation name: 'fernflower-15-05-20'

compile 'com.fifesoft:rsyntaxtextarea:3.1.1'

Expand Down
Binary file added libs/fernflower-15-05-20.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import me.nov.threadtear.io.Conversion;

public class CFR {
public class CFRBridge implements IDecompilerBridge {

public static final HashMap<String, String> options = new HashMap<>();

Expand All @@ -20,16 +20,16 @@ public class CFR {
options.put("innerclasses", "false");
}

public static void setTopsort(boolean topsort) {
public void setAggressive(boolean topsort) {
options.put("forcetopsort", String.valueOf(topsort));
options.put("forcetopsortaggress", String.valueOf(topsort));
}

private static String decompiled;
private String result;

public static String decompile(String name, byte[] bytes) {
public String decompile(String name, byte[] bytes) {
try {
decompiled = null;
this.result = null;
OutputSinkFactory mySink = new OutputSinkFactory() {
@Override
public List<SinkClass> getSupportedSinks(SinkType sinkType, Collection<SinkClass> collection) {
Expand All @@ -44,7 +44,7 @@ public List<SinkClass> getSupportedSinks(SinkType sinkType, Collection<SinkClass
public <T> Sink<T> getSink(SinkType sinkType, SinkClass sinkClass) {
if (sinkType == SinkType.JAVA && sinkClass == SinkClass.DECOMPILED) {
return x -> {
decompiled = ((SinkReturns.Decompiled) x).getJava().substring(31);
result = ((SinkReturns.Decompiled) x).getJava().substring(31);
};
}
return ignore -> {
Expand Down Expand Up @@ -88,9 +88,9 @@ public Collection<String> addJar(String arg0) {
e.printStackTrace(pw);
return sw.toString();
}
if (decompiled == null || decompiled.trim().isEmpty()) {
decompiled = "No CFR output received";
if (result == null || result.trim().isEmpty()) {
result = "No CFR output received";
}
return decompiled;
return result;
}
}
113 changes: 113 additions & 0 deletions src/me/nov/threadtear/decompiler/FernflowerBridge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package me.nov.threadtear.decompiler;

import java.io.*;
import java.util.HashMap;
import java.util.jar.Manifest;

import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger;
import org.jetbrains.java.decompiler.main.extern.*;

import me.nov.threadtear.io.JarIO;

public class FernflowerBridge implements IDecompilerBridge, IBytecodeProvider, IResultSaver {

public static final HashMap<String, Object> options = new HashMap<>();
static {
options.put("rsy", "0");
options.put("rbr", "0");
options.put("din", "0");
options.put("dc4", "1");
options.put("das", "1");
options.put("hes", "1");
options.put("hdc", "1");
options.put("dgs", "0");
options.put("ner", "1");
options.put("den", "1");
options.put("rgn", "1");
options.put("lit", "0");
options.put("asc", "0");
options.put("bto", "1");
options.put("nns", "0");
options.put("uto", "1");
options.put("udv", "1");
options.put("rer", "1");
options.put("fdi", "1");
options.put("ren", "0");
options.put("inn", "1");
options.put("lac", "0");
options.put("mpm", "5");
}

@Override
public void setAggressive(boolean aggressive) {
options.put("das", aggressive ? "0" : "1");
options.put("fdi", aggressive ? "0" : "1");
options.put("rer", aggressive ? "0" : "1");
}

private byte[] bytes;

private String result;

public String decompile(String name, byte[] bytez) {
ByteArrayOutputStream log = new ByteArrayOutputStream();
try {
this.result = null;
this.bytes = bytez;
Fernflower f = new Fernflower(this, this, options, new PrintStreamLogger(new PrintStream(log)));
File temp = JarIO.writeTempJar(name, bytez);
f.addSource(temp);
f.decompileContext();
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
return pw.toString();
}
if (result == null || result.trim().isEmpty()) {
result = "No Fernflower output received\n\nOutput log:\n" + new String(log.toByteArray());
}
return result;
}

@Override
public byte[] getBytecode(String externalPath, String internalPath) throws IOException {
return this.bytes;
}

@Override
public void saveFolder(String path) {
}

@Override
public void copyFile(String source, String path, String entryName) {
}

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

@Override
public void createArchive(String path, String archiveName, Manifest manifest) {
}

@Override
public void saveDirEntry(String path, String archiveName, String entryName) {
}

@Override
public void copyEntry(String source, String path, String archiveName, String entry) {
}

@Override
public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) {
this.result = content;
}

@Override
public void closeArchive(String path, String archiveName) {
}

}
7 changes: 7 additions & 0 deletions src/me/nov/threadtear/decompiler/IDecompilerBridge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.nov.threadtear.decompiler;

public interface IDecompilerBridge {
public void setAggressive(boolean aggressive);

public String decompile(String name, byte[] bytes);
}
14 changes: 14 additions & 0 deletions src/me/nov/threadtear/io/JarIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ public static void saveAsJar(File original, File output, List<Clazz> classes, bo
}
}

public static File writeTempJar(String name, byte[] clazz) {
try {
File temp = File.createTempFile("temp-jar", ".jar");
JarOutputStream out = new JarOutputStream(new FileOutputStream(temp));
out.putNextEntry(new JarEntry(name + ".class"));
out.write(clazz);
out.closeEntry();
out.close();
return temp;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static boolean isClassFile(byte[] bytes) {
return bytes.length >= 4 && String.format("%02X%02X%02X%02X", bytes[0], bytes[1], bytes[2], bytes[3]).equals("CAFEBABE");
}
Expand Down
21 changes: 13 additions & 8 deletions src/me/nov/threadtear/swing/panel/DecompilerPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.github.weisj.darklaf.icons.IconLoader;
import com.github.weisj.darklaf.ui.text.DarkTextUI;

import me.nov.threadtear.decompiler.CFR;
import me.nov.threadtear.decompiler.*;
import me.nov.threadtear.execution.Clazz;
import me.nov.threadtear.io.*;
import me.nov.threadtear.swing.textarea.DecompilerTextArea;
Expand All @@ -37,31 +37,35 @@ public class DecompilerPanel extends JPanel implements ActionListener {
private Clazz clazz;

private RTextScrollPane scp;
private JComboBox<String> decompilerSelection;
private JComboBox<String> conversionMethod;
private JCheckBox ignoreTCB;
private JCheckBox ignoreMon;
private JCheckBox topsort;
private JCheckBox aggressive;
private IDecompilerBridge decompilerBridge;

public DecompilerPanel(Clazz cn) {
this.clazz = cn;
this.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
this.setLayout(new BorderLayout(4, 4));
JPanel leftActionPanel = new JPanel();
leftActionPanel.setLayout(new GridBagLayout());
leftActionPanel.add(new JLabel("<html><tt>CFR 0.149 "));
decompilerSelection = new JComboBox<>(new String[] { "CFR 0.149", "Fernflower 15-05-20" });
decompilerSelection.addActionListener(this);
leftActionPanel.add(decompilerSelection);
conversionMethod = new JComboBox<>(new String[] { "Source", "Transformed" });
conversionMethod.setSelectedIndex(1);
conversionMethod.addActionListener(this);
leftActionPanel.add(conversionMethod);
leftActionPanel.add(ignoreTCB = new JCheckBox("Ignore try catch blocks"));
leftActionPanel.add(ignoreMon = new JCheckBox("Ignore synchronized"));
leftActionPanel.add(topsort = new JCheckBox("Top sort"));
leftActionPanel.add(aggressive = new JCheckBox("Aggressive"));
ignoreTCB.setFocusable(false);
ignoreTCB.addActionListener(this);
ignoreMon.setFocusable(false);
ignoreMon.addActionListener(this);
topsort.addActionListener(this);
topsort.setFocusable(false);
aggressive.addActionListener(this);
aggressive.setFocusable(false);
JPanel rightActionPanel = new JPanel();
rightActionPanel.setLayout(new GridBagLayout());
JButton reload = new JButton(IconLoader.get().loadSVGIcon("res/refresh.svg", false));
Expand Down Expand Up @@ -187,8 +191,9 @@ public void update() {
.forEach(i -> m.instructions.set(i, new InsnNode(POP))));
}
bytes = Conversion.toBytecode0(copy);
CFR.setTopsort(topsort.isSelected());
String decompiled = CFR.decompile(clazz.node.name, bytes);
decompilerBridge = decompilerSelection.getSelectedIndex() == 0 ? new CFRBridge() : new FernflowerBridge();
decompilerBridge.setAggressive(aggressive.isSelected());
String decompiled = decompilerBridge.decompile(clazz.node.name, bytes);
this.textArea.setText(decompiled);
} catch (IOException e) {
e.printStackTrace();
Expand Down

0 comments on commit 137e957

Please sign in to comment.