Skip to content

Commit

Permalink
[Kotlin] Experimental changes to loops / statements
Browse files Browse the repository at this point in the history
  • Loading branch information
sschr15 committed Aug 16, 2024
1 parent a0b6136 commit 1e00a8a
Show file tree
Hide file tree
Showing 14 changed files with 1,348 additions and 1,014 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ private static Pass makePass() {
.addPass("ResugarMethods", new ResugarKotlinMethodsPass())
.addPass("ReplaceContinue", ctx -> LabelHelper.replaceContinueWithBreak(ctx.getRoot()))
.addPass("CollapseStringConcat", new CollapseStringConcatPass())
.addPass("ReplaceStats", new ReplaceStatsPass())

.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import java.util.BitSet;

public class KVarExprent extends VarExprent implements KExprent {
private boolean isExceptionType;
private boolean excludeVarVal;

public KVarExprent(int index, VarType varType, VarProcessor processor, BitSet bytecode) {
super(index, varType, processor, bytecode);
Expand Down Expand Up @@ -47,7 +47,7 @@ public TextBuffer toJava(int indent) {
buffer.addBytecodeMapping(bytecode);

boolean definition = isDefinition();
if (definition && !isExceptionType) {
if (definition && !excludeVarVal) {
VarProcessor processor = getProcessor();

boolean isFinal = isEffectivelyFinal() ||
Expand All @@ -58,16 +58,16 @@ public TextBuffer toJava(int indent) {

buffer.append(getName());

if (definition || isExceptionType) {
if (definition || excludeVarVal) {
buffer.append(": ");
buffer.append(KTypes.getKotlinType(getDefinitionVarType()));
}

return buffer;
}

public void setExceptionType(boolean isExceptionType) {
this.isExceptionType = isExceptionType;
public void setExcludeVarVal(boolean isExceptionType) {
this.excludeVarVal = isExceptionType;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.jetbrains.java.decompiler.api.plugin.pass.Pass;
import org.jetbrains.java.decompiler.api.plugin.pass.PassContext;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
import org.vineflower.kotlin.expr.KVarExprent;
Expand Down Expand Up @@ -33,7 +32,7 @@ private static boolean replace(Statement stat) {
for (int i = 0; i < vars.size(); i++) {
VarExprent expr = vars.get(i);
KVarExprent map = new KVarExprent(expr);
map.setExceptionType(true);
map.setExcludeVarVal(true);
vars.set(i, map);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.vineflower.kotlin.pass;

import org.jetbrains.java.decompiler.api.plugin.pass.Pass;
import org.jetbrains.java.decompiler.api.plugin.pass.PassContext;
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.vineflower.kotlin.stat.KDoStatement;
import org.vineflower.kotlin.stat.KSequenceStatement;

public class ReplaceStatsPass implements Pass {
@Override
public boolean run(PassContext ctx) {
return replace(ctx.getRoot());
}

private static boolean replace(Statement stat) {
boolean res = false;

for (int i = 0; i < stat.getStats().size(); i++) {
Statement st = stat.getStats().get(i);
res |= replace(st);
if (st instanceof SequenceStatement) {
stat.getStats().set(i, new KSequenceStatement((SequenceStatement) st));
res = true;
} else if (st instanceof DoStatement) {
stat.getStats().set(i, new KDoStatement((DoStatement) st));
res = true;
}
}

return res;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.vineflower.kotlin.stat;

import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.TextBuffer;
import org.vineflower.kotlin.expr.KConstExprent;
import org.vineflower.kotlin.expr.KVarExprent;

public class KDoStatement extends KStatement<DoStatement> {
public KDoStatement(DoStatement statement) {
super(statement);
}

private static boolean isIntegerType(VarType type) {
return VarType.VARTYPE_INT.equals(type) ||
VarType.VARTYPE_BYTE.equals(type) ||
VarType.VARTYPE_SHORT.equals(type) ||
VarType.VARTYPE_CHAR.equals(type) ||
VarType.VARTYPE_LONG.equals(type);
}

@Override
public TextBuffer toJava(int indent) {
TextBuffer buf = new TextBuffer();
boolean labeled = statement.isLabeled();

buf.appendIndent(indent);

if (labeled) {
buf.append("label")
.append(this.id)
.append("@ ");
}

switch (statement.getLooptype()) {
case INFINITE -> {
buf.append("while (true) {").appendLineSeparator();
buf.append(ExprProcessor.jmpWrapper(statement.getFirst(), indent + 1, false));
buf.appendIndent(indent).append("}").appendLineSeparator();
}
case DO_WHILE -> {
buf.append("do {").appendLineSeparator();
buf.append(ExprProcessor.jmpWrapper(statement.getFirst(), indent + 1, false));
buf.appendIndent(indent).append("} while (");
buf.pushNewlineGroup(indent, 1);
buf.appendPossibleNewline();
buf.append(statement.getConditionExprent().toJava(indent));
buf.appendPossibleNewline("", true);
buf.popNewlineGroup();
buf.append(")").appendLineSeparator();
}
case WHILE -> {
buf.append("while (");
buf.pushNewlineGroup(indent, 1);
buf.appendPossibleNewline();
buf.append(statement.getConditionExprent().toJava(indent));
buf.appendPossibleNewline("", true);
buf.popNewlineGroup();
buf.append(") {").appendLineSeparator();
buf.append(ExprProcessor.jmpWrapper(statement.getFirst(), indent + 1, false));
buf.appendIndent(indent).append("}").appendLineSeparator();
}
case FOR_EACH -> {
buf.append("for (").append(statement.getInitExprent().toJava(indent));
statement.getIncExprent().getInferredExprType(null); //TODO: see DoStatement
buf.append(" in ").append(statement.getIncExprent().toJava(indent)).append(") {").appendLineSeparator();
buf.append(ExprProcessor.jmpWrapper(statement.getFirst(), indent + 1, false));
buf.appendIndent(indent).append("}").appendLineSeparator();
}
case FOR -> {
// This is a hard one as Kotlin has no one-to-one equivalent to Java's for loop
if (
statement.getInitExprent() instanceof AssignmentExprent init &&
init.getLeft() instanceof VarExprent varExpr &&
isIntegerType(varExpr.getExprType()) &&
init.getRight() instanceof ConstExprent constExpr &&

statement.getIncExprent() instanceof FunctionExprent inc &&
inc.getFuncType() == FunctionExprent.FunctionType.IPP &&

statement.getConditionExprent() instanceof FunctionExprent condition &&
condition.getFuncType() == FunctionExprent.FunctionType.LT &&
condition.getLstOperands().get(condition.getLstOperands().size() - 1) instanceof ConstExprent conditionConst
) {
// Turn for loop into range
varExpr = new KVarExprent(varExpr);
((KVarExprent) varExpr).setExcludeVarVal(true);

constExpr.setConstType(varExpr.getExprType());
conditionConst.setConstType(varExpr.getExprType());
if (conditionConst.getValue() instanceof Integer i) {
conditionConst = new ConstExprent(varExpr.getExprType(), i - 1, conditionConst.bytecode);
conditionConst = new KConstExprent(conditionConst);
}

buf.append("for (")
.append(varExpr.toJava(indent))
.append(" in ")
.append(constExpr.toJava())
.append("..")
.append(conditionConst.toJava())
.append(") {")
.appendLineSeparator();

buf.append(ExprProcessor.jmpWrapper(statement.getFirst(), indent + 1, false));
buf.appendIndent(indent).append("}").appendLineSeparator();
} else {
//TODO other cases
if (labeled) {
buf.setLength(0); // Clear buffer, label needs to be treated differently
buf.appendIndent(indent);
}

buf.append("// $VF: Unable to resugar Kotlin loop from Java for loop")
.appendLineSeparator()
.appendIndent(indent);

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

if (statement.getInitExprent() != null) {
buf.append(statement.getInitExprent().toJava(indent)).appendLineSeparator().appendIndent(indent);
}

buf.append("while (true) {").appendLineSeparator();
buf.appendIndent(indent + 1);
buf.append("if (");
buf.append(statement.getConditionExprent().toJava(indent + 1));
buf.append(") break").appendLineSeparator();
buf.append(ExprProcessor.jmpWrapper(statement.getFirst(), indent + 1, false));
buf.appendLineSeparator();
buf.appendIndent(indent + 1).append(statement.getIncExprent().toJava(indent + 1)).appendLineSeparator();
buf.appendIndent(indent).append("}").appendLineSeparator();

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

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

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

public class KSequenceStatement extends KStatement<SequenceStatement> {
public KSequenceStatement(SequenceStatement statement) {
super(statement);
}

@Override
public TextBuffer toJava(int indent) {
TextBuffer buf = new TextBuffer();
boolean labeled = statement.isLabeled();

buf.append(ExprProcessor.listToJava(statement.getVarDefinitions(), indent));

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

boolean notEmpty = false;
for (int i = 0; i < statement.getStats().size(); i++) {
Statement st = statement.getStats().get(i);
TextBuffer str = ExprProcessor.jmpWrapper(st, indent, false);

if (i > 0 && !str.containsOnlyWhitespaces() && notEmpty) {
buf.appendLineSeparator();
}

buf.append(str);

notEmpty = !str.containsOnlyWhitespaces();
}

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

return buf;
}
}
Loading

0 comments on commit 1e00a8a

Please sign in to comment.