Make loops generation lazy
As well as all other kinds of expressions While it's not necessary in a sense that 'for' loop can not be plain expression, i.e. it can't be an argument for safe-call etc., but laziness is still very convenient property. E.g. within attached test they were generated twice in case of last expression of coroutine block, because coroutine related codegen part is built upon assumption that all expressions should be generated lazy.
This commit is contained in:
@@ -506,7 +506,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackValue visitWhileExpression(@NotNull KtWhileExpression expression, StackValue receiver) {
|
||||
public StackValue visitWhileExpression(@NotNull final KtWhileExpression expression, StackValue receiver) {
|
||||
return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter adapter) {
|
||||
generateWhile(expression);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void generateWhile(@NotNull KtWhileExpression expression) {
|
||||
Label condition = new Label();
|
||||
v.mark(condition);
|
||||
|
||||
@@ -523,13 +533,20 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
v.mark(end);
|
||||
|
||||
blockStackElements.pop();
|
||||
|
||||
return StackValue.onStack(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public StackValue visitDoWhileExpression(@NotNull KtDoWhileExpression expression, StackValue receiver) {
|
||||
public StackValue visitDoWhileExpression(@NotNull final KtDoWhileExpression expression, StackValue receiver) {
|
||||
return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter adapter) {
|
||||
generateDoWhile(expression);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void generateDoWhile(@NotNull KtDoWhileExpression expression) {
|
||||
Label beginLoopLabel = new Label();
|
||||
v.mark(beginLoopLabel);
|
||||
|
||||
@@ -567,25 +584,34 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
v.mark(breakLabel);
|
||||
|
||||
blockStackElements.pop();
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackValue visitForExpression(@NotNull KtForExpression forExpression, StackValue receiver) {
|
||||
public StackValue visitForExpression(@NotNull final KtForExpression forExpression, StackValue receiver) {
|
||||
return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter adapter) {
|
||||
generateFor(forExpression);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void generateFor(@NotNull KtForExpression forExpression) {
|
||||
ResolvedCall<? extends CallableDescriptor> loopRangeCall = RangeCodegenUtil.getLoopRangeResolvedCall(forExpression, bindingContext);
|
||||
if (loopRangeCall != null) {
|
||||
CallableDescriptor loopRangeCallee = loopRangeCall.getResultingDescriptor();
|
||||
if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) {
|
||||
generateForLoop(createForInRangeLiteralLoopGenerator(forExpression, loopRangeCall));
|
||||
return StackValue.none();
|
||||
return;
|
||||
}
|
||||
else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
||||
generateForLoop(createForInArrayIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
||||
return StackValue.none();
|
||||
return;
|
||||
}
|
||||
else if (RangeCodegenUtil.isCollectionIndices(loopRangeCallee)) {
|
||||
generateForLoop(createForInCollectionIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
||||
return StackValue.none();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,21 +622,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
Type asmLoopRangeType = asmType(loopRangeType);
|
||||
if (asmLoopRangeType.getSort() == Type.ARRAY) {
|
||||
generateForLoop(new ForInArrayLoopGenerator(forExpression));
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
if (RangeCodegenUtil.isRange(loopRangeType)) {
|
||||
else if (RangeCodegenUtil.isRange(loopRangeType)) {
|
||||
generateForLoop(new ForInRangeInstanceLoopGenerator(forExpression));
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
if (RangeCodegenUtil.isProgression(loopRangeType)) {
|
||||
else if (RangeCodegenUtil.isProgression(loopRangeType)) {
|
||||
generateForLoop(new ForInProgressionExpressionLoopGenerator(forExpression));
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
generateForLoop(new IteratorForLoopGenerator(forExpression));
|
||||
return StackValue.none();
|
||||
else {
|
||||
generateForLoop(new IteratorForLoopGenerator(forExpression));
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractForLoopGenerator createForInRangeLiteralLoopGenerator(
|
||||
@@ -1705,6 +1726,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
// Note that this result value is potentially unused (in case of handleResult coroutine call)
|
||||
// It's supposed here that no bytecode is emitted until 'put' call on relevant StackValue object
|
||||
StackValue result = isExpression ? gen(possiblyLabeledStatement) : genStatement(possiblyLabeledStatement);
|
||||
|
||||
if (!iterator.hasNext()) {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
class Controller {
|
||||
var result = ""
|
||||
var ok = false
|
||||
suspend fun suspendHere(v: String, x: Continuation<Unit>) {
|
||||
result += v
|
||||
x.resume(Unit)
|
||||
}
|
||||
|
||||
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
if (!controller.ok) throw java.lang.RuntimeException("Fail ok")
|
||||
return controller.result
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val r1 = builder {
|
||||
for (i in 5..6) {
|
||||
suspendHere(i.toString())
|
||||
}
|
||||
}
|
||||
|
||||
if (r1 != "56") return "fail 1: $r1"
|
||||
|
||||
val r2 = builder {
|
||||
var i = 7
|
||||
while (i <= 8) {
|
||||
suspendHere(i.toString())
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if (r2 != "78") return "fail 2: $r2"
|
||||
|
||||
val r3 = builder {
|
||||
var i = 9
|
||||
do {
|
||||
suspendHere(i.toString())
|
||||
i++
|
||||
} while (i <= 10);
|
||||
}
|
||||
|
||||
if (r3 != "910") return "fail 3: $r3"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -4183,6 +4183,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lastExpressionIsLoop.kt")
|
||||
public void testLastExpressionIsLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lastUnitExpression.kt")
|
||||
public void testLastUnitExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastUnitExpression.kt");
|
||||
|
||||
Reference in New Issue
Block a user