Skip to content

Commit b876b97

Browse files
authored
Handle Kotlin's statements and their frequent sugar (#472)
* [Kotlin] Add support for downTo, fix non-KExprent instances in other "do" statements # Conflicts: # plugins/kotlin/src/main/java/org/vineflower/kotlin/stat/KDoStatement.java # plugins/kotlin/testData/results/pkg/TestForRange.dec # plugins/kotlin/testData/results/pkg/TestFunVarargs.dec # plugins/kotlin/testData/results/pkg/TestNonInlineLambda.dec * [Kotlin] Improve for-loop resugaring * [Kotlin] Fix objects, companion object members, and whitespace * [Kotlin] Update tests * Subclass statement types instead of a custom one in its entirety * Add tests for compiling a `when` statement to switch blocks * Modify exprent replacement logic * Add accessibility to some private members * `switch` statement decompilation to `when` * Update test results and statement stuff after merge * Fix jmpWrapper calls, add `if` statement support * Update tests * Fix some instances of odd spacing in `when` blocks I should look into this more and see why it's seemingly quite inconsistent * Add test cases to `TestWhen` * Even more `when` tests * Fix additional `when` issues * Update test case * Reconfigure Kotlin variable references for more versatility in the many ways they're used * Add replace stats pass This existed prior to the rebase, but something went amiss and I'm not quite sure what * Be more lenient on allowed loops This also fixes a potential issue where `repeat` would be generated despite counting down
1 parent 7e522e3 commit b876b97

22 files changed

+1812
-526
lines changed

plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinPlugin.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ private static Pass makePass() {
7979
.addPass("RedundantReturns", ctx -> ExitHelper.removeRedundantReturns(ctx.getRoot()))
8080
.addPass("IdentifySecondary", ctx -> SecondaryFunctionsHelper.identifySecondaryFunctions(ctx.getRoot(), ctx.getVarProc(), FORCE_TERNARY_SIMPLIFY))
8181
.addPass("SetVarDefinitions", WrappedPass.of(ctx -> ctx.getVarProc().setVarDefinitions(ctx.getRoot())))
82+
.addPass("ReplaceStats", new ReplaceStatsPass())
8283
.addPass("ReplaceExprs", new ReplaceExprentsPass())
8384
// TODO: preference for this pass
8485
.addPass("ResugarMethods", new ResugarKotlinMethodsPass())
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.vineflower.kotlin.expr;
2+
3+
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
4+
import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchHeadExprent;
5+
import org.jetbrains.java.decompiler.util.TextBuffer;
6+
7+
public class KSwitchHeadExprent extends SwitchHeadExprent implements KExprent {
8+
public KSwitchHeadExprent(SwitchHeadExprent ex) {
9+
super(ex.getValue(), ex.bytecode);
10+
setCaseValues(ex.getCaseValues());
11+
}
12+
13+
@Override
14+
public TextBuffer toJava(int indent) {
15+
TextBuffer buf = new TextBuffer();
16+
17+
buf.append(getValue().toJava(indent)).enclose("when (", ")");
18+
buf.addStartBytecodeMapping(bytecode);
19+
20+
return buf;
21+
}
22+
}

plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KVarExprent.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
import java.util.BitSet;
1313

1414
public class KVarExprent extends VarExprent implements KExprent {
15-
private boolean isExceptionType;
15+
public enum DeclarationType {
16+
DEFINITION,
17+
USAGE,
18+
FOR_LOOP_VARIABLE,
19+
EXCEPTION_TYPE,
20+
}
21+
22+
private DeclarationType declarationType;
1623

1724
public KVarExprent(int index, VarType varType, VarProcessor processor, BitSet bytecode) {
1825
super(index, varType, processor, bytecode);
@@ -27,6 +34,11 @@ public KVarExprent(VarExprent ex) {
2734
// this.setLVT(ex.getLVT());
2835
this.setEffectivelyFinal(ex.isEffectivelyFinal());
2936
this.setDefinition(ex.isDefinition());
37+
if (ex instanceof KVarExprent kVarExprent) {
38+
declarationType = kVarExprent.declarationType;
39+
} else {
40+
declarationType = ex.isDefinition() ? DeclarationType.DEFINITION : DeclarationType.USAGE;
41+
}
3042
}
3143

3244
@Override
@@ -46,8 +58,7 @@ public TextBuffer toJava(int indent) {
4658

4759
buffer.addBytecodeMapping(bytecode);
4860

49-
boolean definition = isDefinition();
50-
if (definition && !isExceptionType) {
61+
if (declarationType == DeclarationType.DEFINITION) {
5162
VarProcessor processor = getProcessor();
5263

5364
boolean isFinal = isEffectivelyFinal() ||
@@ -58,16 +69,16 @@ public TextBuffer toJava(int indent) {
5869

5970
buffer.append(getName());
6071

61-
if (definition || isExceptionType) {
72+
if (declarationType == DeclarationType.DEFINITION || declarationType == DeclarationType.EXCEPTION_TYPE) {
6273
buffer.append(": ");
6374
buffer.append(KTypes.getKotlinType(getDefinitionVarType()));
6475
}
6576

6677
return buffer;
6778
}
6879

69-
public void setExceptionType(boolean isExceptionType) {
70-
this.isExceptionType = isExceptionType;
80+
public void setDeclarationType(DeclarationType declarationType) {
81+
this.declarationType = declarationType;
7182
}
7283

7384
@Override

plugins/kotlin/src/main/java/org/vineflower/kotlin/pass/ReplaceExprentsPass.java

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.vineflower.kotlin.expr.KVarExprent;
99
import org.vineflower.kotlin.util.KUtils;
1010

11+
import java.util.ArrayList;
1112
import java.util.List;
1213

1314
public class ReplaceExprentsPass implements Pass {
@@ -23,22 +24,52 @@ private static boolean replace(Statement stat) {
2324
res |= replace(st);
2425
}
2526

26-
List<Exprent> exprs = List.of();
27-
if (stat instanceof BasicBlockStatement) {
28-
exprs = stat.getExprents();
29-
} else if (stat instanceof IfStatement) {
30-
exprs = ((IfStatement)stat).getHeadexprentList();
27+
List<List<Exprent>> exprLists = new ArrayList<>();
28+
exprLists.add(stat.getExprents());
29+
exprLists.add(stat.getVarDefinitions());
30+
31+
if (stat instanceof CatchAllStatement || stat instanceof CatchStatement) {
32+
// Special handling for catch declarations
33+
List<VarExprent> vars = stat instanceof CatchAllStatement ? ((CatchAllStatement)stat).getVars() : ((CatchStatement)stat).getVars();
34+
for (int i = 0; i < vars.size(); i++) {
35+
VarExprent expr = vars.get(i);
36+
KVarExprent map = new KVarExprent(expr);
37+
map.setDeclarationType(KVarExprent.DeclarationType.EXCEPTION_TYPE);
38+
vars.set(i, map);
39+
}
40+
41+
if (stat instanceof CatchAllStatement catchAll && catchAll.getMonitor() != null) {
42+
catchAll.setMonitor(new KVarExprent(catchAll.getMonitor()));
43+
} else if (stat instanceof CatchStatement catchStat) {
44+
exprLists.add(catchStat.getResources());
45+
}
46+
} else if (stat instanceof DoStatement doStat) {
47+
exprLists.add(doStat.getInitExprentList());
48+
exprLists.add(doStat.getConditionExprentList());
49+
exprLists.add(doStat.getIncExprentList());
50+
} else if (stat instanceof IfStatement ifStat) {
51+
exprLists.add(ifStat.getHeadexprentList());
52+
} else if (stat instanceof SwitchStatement switchStat) {
53+
exprLists.addAll(switchStat.getCaseValues());
54+
exprLists.add(switchStat.getCaseGuards());
55+
exprLists.add(switchStat.getHeadexprentList());
56+
} else if (stat instanceof SynchronizedStatement syncStat) {
57+
exprLists.add(syncStat.getHeadexprentList());
3158
} else if (stat instanceof CatchAllStatement || stat instanceof CatchStatement) {
3259
List<VarExprent> vars = stat instanceof CatchAllStatement ? ((CatchAllStatement)stat).getVars() : ((CatchStatement)stat).getVars();
3360
for (int i = 0; i < vars.size(); i++) {
3461
VarExprent expr = vars.get(i);
3562
KVarExprent map = new KVarExprent(expr);
36-
map.setExceptionType(true);
63+
map.setDeclarationType(KVarExprent.DeclarationType.EXCEPTION_TYPE);
3764
vars.set(i, map);
3865
}
3966
}
4067

41-
if (!exprs.isEmpty()) {
68+
for (List<Exprent> exprs : exprLists) {
69+
if (exprs == null) {
70+
continue;
71+
}
72+
4273
for(int i = 0; i < exprs.size(); i++){
4374
Exprent expr = exprs.get(i);
4475
Exprent map = KUtils.replaceExprent(expr);
@@ -50,18 +81,11 @@ private static boolean replace(Statement stat) {
5081
}
5182

5283
for (Exprent ex : exprs) {
53-
res |= replace(ex);
54-
}
55-
}
56-
57-
for (int i = 0; i < stat.getVarDefinitions().size(); i++) {
58-
Exprent expr = stat.getVarDefinitions().get(i);
59-
60-
Exprent map = KUtils.replaceExprent(expr);
61-
if (map != null) {
62-
stat.getVarDefinitions().set(i, map);
84+
if (ex == null) {
85+
continue;
86+
}
6387

64-
res = true;
88+
res |= replace(ex);
6589
}
6690
}
6791

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.vineflower.kotlin.pass;
2+
3+
import org.jetbrains.java.decompiler.api.plugin.pass.Pass;
4+
import org.jetbrains.java.decompiler.api.plugin.pass.PassContext;
5+
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
6+
import org.vineflower.kotlin.stat.*;
7+
8+
public class ReplaceStatsPass implements Pass {
9+
@Override
10+
public boolean run(PassContext ctx) {
11+
return replace(ctx.getRoot());
12+
}
13+
14+
private static boolean replace(Statement stat) {
15+
boolean res = false;
16+
17+
for (int i = 0; i < stat.getStats().size(); i++) {
18+
Statement st = stat.getStats().get(i);
19+
res |= replace(st);
20+
if (st instanceof SequenceStatement) {
21+
st.replaceWith(new KSequenceStatement((SequenceStatement) st));
22+
res = true;
23+
} else if (st instanceof DoStatement) {
24+
st.replaceWith(new KDoStatement((DoStatement) st));
25+
res = true;
26+
} else if (st instanceof SwitchStatement) {
27+
st.replaceWith(new KSwitchStatement((SwitchStatement) st));
28+
res = true;
29+
} else if (st instanceof IfStatement) {
30+
st.replaceWith(new KIfStatement((IfStatement) st));
31+
res = true;
32+
}
33+
}
34+
35+
return res;
36+
}
37+
}

0 commit comments

Comments
 (0)