Skip to content

Commit

Permalink
fix: pull in processing decompile phase from Forgeflower threading im…
Browse files Browse the repository at this point in the history
…pl (#303)

* fix: pull in processing decompile phase from Forgeflower threading impl

This resolves a race condition when decompiling non-static inner classes

* Propagate exceptions from pre-processing step

---------

Co-authored-by: Jasmine Karthikeyan <[email protected]>
  • Loading branch information
zml2008 and jaskarth committed Sep 4, 2023
1 parent 1a3e458 commit 0af9247
Show file tree
Hide file tree
Showing 14 changed files with 227 additions and 117 deletions.
28 changes: 16 additions & 12 deletions src/org/jetbrains/java/decompiler/main/ClassWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,25 +115,29 @@ private static boolean invokeProcessors(TextBuffer buffer, ClassNode node) {
DecompilerContext.getLogger().writeMessage("Class " + node.simpleName + " couldn't be written.",
IFernflowerLogger.Severity.WARN,
t);
buffer.append("// $VF: Couldn't be decompiled");
buffer.appendLineSeparator();
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_EXCEPTION_ON_ERROR)) {
List<String> lines = new ArrayList<>();
lines.addAll(ClassWriter.getErrorComment());
collectErrorLines(t, lines);
for (String line : lines) {
buffer.append("//");
if (!line.isEmpty()) buffer.append(' ').append(line);
buffer.appendLineSeparator();
}
}
writeException(buffer, t);

return false;
}

return true;
}

public static void writeException(TextBuffer buffer, Throwable t) {
buffer.append("// $VF: Couldn't be decompiled");
buffer.appendLineSeparator();
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_EXCEPTION_ON_ERROR)) {
List<String> lines = new ArrayList<>();
lines.addAll(ClassWriter.getErrorComment());
collectErrorLines(t, lines);
for (String line : lines) {
buffer.append("//");
if (!line.isEmpty()) buffer.append(' ').append(line);
buffer.appendLineSeparator();
}
}
}

public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent) {
ClassWrapper wrapper = node.getWrapper();
if (wrapper == null) {
Expand Down
93 changes: 37 additions & 56 deletions src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,38 @@ else if (cl.superClass == null) { // neither interface nor super class defined
return true;
}

public void processClass(StructClass cl) throws IOException {
ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.Type.ROOT) {
return;
}

boolean packageInfo = cl.isSynthetic() && "package-info".equals(root.simpleName);
boolean moduleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);

DecompilerContext.getLogger().startProcessingClass(cl.qualifiedName);
ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.startClass(importCollector);
try {
if (!packageInfo && !moduleInfo) {
new LambdaProcessor().processClass(root);

// add simple class names to implicit import
addClassNameToImport(root, importCollector);

// build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root);

// Java specific last minute processing
new NestedClassProcessor().processClass(root, root);

new NestedMemberAccess().propagateMemberAccess(root);
}
} finally {
DecompilerContext.getLogger().endProcessingClass();
}
}

public void writeClass(StructClass cl, TextBuffer buffer) throws IOException {
ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.Type.ROOT) {
Expand All @@ -409,76 +441,25 @@ public void writeClass(StructClass cl, TextBuffer buffer) throws IOException {

DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
try {
ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.startClass(importCollector);

if (packageInfo) {
ClassWriter.packageInfoToJava(cl, buffer);

importCollector.writeImports(buffer, false);
DecompilerContext.getImportCollector().writeImports(buffer, false);
}
else if (moduleInfo) {
TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
ClassWriter.moduleInfoToJava(cl, moduleBuffer);

importCollector.writeImports(buffer, true);
DecompilerContext.getImportCollector().writeImports(buffer, true);

buffer.append(moduleBuffer);
}
else {
try {
new LambdaProcessor().processClass(root);
} catch (Throwable t) {
DecompilerContext.getLogger().writeMessage("Class " + root.simpleName + " couldn't be written.",
IFernflowerLogger.Severity.WARN,
t);
buffer.append("// $VF: Couldn't be decompiled");
buffer.appendLineSeparator();
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_EXCEPTION_ON_ERROR)) {
List<String> lines = new ArrayList<>();
lines.addAll(ClassWriter.getErrorComment());
ClassWriter.collectErrorLines(t, lines);
for (String line : lines) {
buffer.append("//");
if (!line.isEmpty()) buffer.append(' ').append(line);
buffer.appendLineSeparator();
}
}
return;
}

// add simple class names to implicit import
addClassNameToImport(root, importCollector);

// build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root);

try {
new NestedClassProcessor().processClass(root, root);

new NestedMemberAccess().propagateMemberAccess(root);
} catch (Throwable t) {
DecompilerContext.getLogger().writeMessage("Class " + root.simpleName + " couldn't be written.",
IFernflowerLogger.Severity.WARN,
t);
buffer.append("// $VF: Couldn't be decompiled");
buffer.appendLineSeparator();
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_EXCEPTION_ON_ERROR)) {
List<String> lines = new ArrayList<>();
lines.addAll(ClassWriter.getErrorComment());
ClassWriter.collectErrorLines(t, lines);
for (String line : lines) {
buffer.append("//");
if (!line.isEmpty()) buffer.append(' ').append(line);
buffer.appendLineSeparator();
}
}
return;
}

TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);

new ClassWriter().classToJava(root, classBuffer, 0);
classBuffer.reformat();

classBuffer.getTracers().forEach((classAndMethod, tracer) -> {
// get the class by name
StructClass clazz = DecompilerContext.getStructContext().getClass(classAndMethod.a);
Expand All @@ -500,7 +481,7 @@ else if (moduleInfo) {
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
}

importCollector.writeImports(buffer, true);
DecompilerContext.getImportCollector().writeImports(buffer, true);

int offsetLines = buffer.countLines();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ public static DecompilerContext getCurrentContext() {
}

public static void setCurrentContext(DecompilerContext context) {
currentContext.set(context);
if (context == null) {
currentContext.remove();
} else {
currentContext.set(context);
}
}

public static void setProperty(String key, Object value) {
Expand Down
12 changes: 9 additions & 3 deletions src/org/jetbrains/java/decompiler/main/Fernflower.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.jetbrains.java.decompiler.util.TextBuffer;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -168,15 +169,20 @@ else if (converter != null) {
}
}

@Override
public void processClass(final StructClass cl) throws IOException {
classProcessor.processClass(cl); // unhandled exceptions handled later on
}

@Override
public String getClassContent(StructClass cl) {
TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);
try {
TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);
buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString());
classProcessor.writeClass(cl, buffer);
String res = buffer.convertToStringAndAllowDataDiscard();
if (res == null) {
return "$ FF: Unable to decompile class " + cl.qualifiedName;
return "$ VF: Unable to decompile class " + cl.qualifiedName;
}

return res;
Expand All @@ -190,7 +196,7 @@ public String getClassContent(StructClass cl) {
lines.addAll(ClassWriter.getErrorComment());
ClassWriter.collectErrorLines(t, lines);
lines.add("*/");
return String.join("\n", lines);
return String.join(DecompilerContext.getNewLineSeparator(), lines);
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ public void writeMessage(String message, Severity severity, Throwable t) {
}
}

public void startProcessingClass(String className) {
if (accepts(Severity.INFO)) {
writeMessage("Preprocessing class " + className, Severity.INFO);
indent.get().incrementAndGet();
}
}

@Override
public void endProcessingClass() {
if (accepts(Severity.INFO)) {
indent.get().decrementAndGet();
writeMessage("... done", Severity.INFO);
}
}

@Override
public void startReadingClass(String className) {
if (accepts(Severity.INFO)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public void writeMessage(String message, Throwable t) {
writeMessage(message, Severity.ERROR, t);
}

public void startProcessingClass(String className) { }

public void endProcessingClass() { }

public void startReadingClass(String className) { }

public void endReadingClass() { }
Expand Down
Loading

0 comments on commit 0af9247

Please sign in to comment.