Skip to content

Commit

Permalink
switch statement decompilation to when
Browse files Browse the repository at this point in the history
  • Loading branch information
sschr15 committed Aug 31, 2024
1 parent 97f22d5 commit e3f36fb
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.vineflower.kotlin.expr;

import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchHeadExprent;
import org.jetbrains.java.decompiler.util.TextBuffer;

public class KSwitchHeadExprent extends SwitchHeadExprent implements KExprent {
public KSwitchHeadExprent(SwitchHeadExprent ex) {
super(ex.getValue(), ex.bytecode);
setCaseValues(ex.getCaseValues());
}

@Override
public TextBuffer toJava(int indent) {
TextBuffer buf = new TextBuffer();

buf.append(getValue().toJava(indent)).enclose("when (", ")");
buf.addStartBytecodeMapping(bytecode);

return buf;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement;
import org.vineflower.kotlin.stat.KDoStatement;
import org.vineflower.kotlin.stat.KSequenceStatement;
import org.vineflower.kotlin.stat.KSwitchStatement;

public class ReplaceStatsPass implements Pass {
@Override
Expand All @@ -26,6 +28,9 @@ private static boolean replace(Statement stat) {
} else if (st instanceof DoStatement) {
stat.getStats().set(i, new KDoStatement((DoStatement) st));
res = true;
} else if (st instanceof SwitchStatement) {
stat.getStats().set(i, new KSwitchStatement((SwitchStatement) st));
res = true;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package org.vineflower.kotlin.stat;

import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.TextBuffer;
import org.vineflower.kotlin.util.KExprProcessor;

import java.util.List;

public class KSwitchStatement extends SwitchStatement {
public KSwitchStatement(SwitchStatement statement) {
super();

getCaseStatements().addAll(statement.getCaseStatements());
getCaseEdges().addAll(statement.getCaseEdges());
getCaseValues().addAll(statement.getCaseValues());
getCaseGuards().addAll(statement.getCaseGuards());
//TODO: check if scoped case statements are needed?
getHeadexprentList().remove(0); // remove added null
getHeadexprentList().addAll(statement.getHeadexprentList());

setFirst(statement.getFirst());
setPhantom(statement.isPhantom()); // Only type of statement that can be phantom
stats.addAllWithKey(statement.getStats(), statement.getStats().getLstKeys());
parent = statement.getParent();
first = statement.getFirst();
exprents = statement.getExprents();
labelEdges.addAll(statement.getLabelEdges());
varDefinitions.addAll(statement.getVarDefinitions());
post = statement.getPost();
lastBasicType = statement.getLastBasicType();

isMonitorEnter = statement.isMonitorEnter();
containsMonitorExit = statement.containsMonitorExit();
isLastAthrow = statement.containsMonitorExitOrAthrow() && !containsMonitorExit;

continueSet = statement.getContinueSet();

// Set up default edge
initSimpleCopy();
}

@Override
public TextBuffer toJava(int indent) {
TextBuffer buf = new TextBuffer();
buf.append(ExprProcessor.listToJava(varDefinitions, indent));

if (isLabeled()) {
buf.appendIndent(indent++)
.append("run label")
.append(id)
.append("@{")
.appendLineSeparator();
}

buf.appendIndent(indent).append(first.toJava());

boolean showIfHidden = DecompilerContext.getOption(IFernflowerPreferences.SHOW_HIDDEN_STATEMENTS);

if (isPhantom()) {
if (!showIfHidden) {
return buf;
} else {
buf.appendIndent(indent)
.append("/*")
.appendLineSeparator();
}
}

buf.append(getHeadexprent().toJava(indent))
.append(" {")
.appendLineSeparator();

VarType switchType = getHeadexprent().getExprType();

for (int i = 0; i < getCaseStatements().size(); i++) {
Statement stat = getCaseStatements().get(i);
List<StatEdge> edges = getCaseEdges().get(i);
List<Exprent> values = getCaseValues().get(i);
// Exprent guard = getCaseGuards().size() > i ? getCaseGuards().get(i) : null;

boolean anyNonDefault = false;
for (int j = 0; j < edges.size(); j++) {
if (edges.get(j) == getDefaultEdge()) {
continue; // Default / "else" edges must show up alone
}

anyNonDefault = true;

Exprent value = values.get(j);

if (j == 0) {
buf.appendIndent(indent + 1);
} else {
buf.append(", ");
}

if (value instanceof ConstExprent && !VarType.VARTYPE_NULL.equals(value.getExprType())) {
ConstExprent constValue = (ConstExprent) value.copy();
constValue.setConstType(switchType);
}

if (value instanceof FieldExprent field && field.isStatic()) { // enum
ImportCollector importCollector = DecompilerContext.getImportCollector();
buf.appendClass(importCollector.getShortName(field.getClassname()), false, field.getClassname())
.append('.')
.appendField(field.getName(), false, field.getClassname(), field.getName(), field.getDescriptor());
} else {
buf.append(value.toJava(indent));
}
}

TextBuffer body = KExprProcessor.jmpWrapper(stat, indent + 2, true);

if (anyNonDefault) {
buf.append(" -> ");
if (body.countLines() > 1) {
buf.append("{")
.appendLineSeparator()
.append(body)
.appendIndent(indent + 1)
.append("}")
.appendLineSeparator();
} else {
int indentSize = ((String) DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING)).length();
body.setStart(indentSize * (indent + 2));
buf.append(body);
}
}

if (edges.contains(getDefaultEdge())) {
buf.appendIndent(indent + 1)
.append("else -> ");

if (body.countLines() > 1) {
buf.append("{")
.appendLineSeparator()
.append(body)
.appendIndent(indent + 1)
.append("}")
.appendLineSeparator();
} else {
int indentSize = ((String) DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING)).length();
body.setStart(indentSize * (indent + 2));
buf.append(body);
}
}
}

buf.appendIndent(indent)
.append("}")
.appendLineSeparator();

if (isLabeled()) {
buf.appendIndent(--indent)
.append("}")
.appendLineSeparator();
}

if (isPhantom()) {
buf.appendIndent(indent)
.append("*/")
.appendLineSeparator();
}

return buf;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.vineflower.kotlin.util;

import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DummyExitStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.util.TextBuffer;

import java.util.List;

public class KExprProcessor {
public static TextBuffer jmpWrapper(Statement stat, int indent, boolean isSwitch) {
TextBuffer buf = stat.toJava(indent);

List<StatEdge> successors = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL);
if (successors.size() == 1) {
StatEdge edge = successors.get(0);
if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && !(edge.getDestination() instanceof DummyExitStatement)) {
TextBuffer innerBuf = new TextBuffer();
innerBuf.appendIndent(indent);

switch (edge.getType()) {
case StatEdge.TYPE_BREAK -> {
ExprProcessor.addDeletedGotoInstructionMapping(stat, buf);
if (!isSwitch || edge.labeled) {
innerBuf.append("break");
}
}
case StatEdge.TYPE_CONTINUE -> {
ExprProcessor.addDeletedGotoInstructionMapping(stat, buf);
innerBuf.append("continue");
}
}

if (edge.labeled) {
innerBuf.append("@label").append(edge.closure.id);
}

if (!innerBuf.containsOnlyWhitespaces()) {
buf.append(innerBuf).appendLineSeparator();
} else {
innerBuf.convertToStringAndAllowDataDiscard();
}
}
}

return buf;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public static Exprent replaceExprent(Exprent ex) {
return new KFieldExprent((FieldExprent) ex);
} else if (ex instanceof AnnotationExprent) {
return new KAnnotationExprent((AnnotationExprent) ex);
} else if (ex instanceof SwitchHeadExprent) {
return new KSwitchHeadExprent((SwitchHeadExprent) ex);
}

return null;
Expand Down

0 comments on commit e3f36fb

Please sign in to comment.