From b6ccd03ef453e41e724fbd98c0fab9901bb8dc01 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 20 Jun 2016 14:46:55 +0300 Subject: [PATCH] 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. --- .../kotlin/codegen/ExpressionCodegen.java | 62 +++++++++++++------ .../box/coroutines/lastExpressionIsLoop.kt | 51 +++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++ 3 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 45570c96490..c649c59f3a9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -506,7 +506,17 @@ public class ExpressionCodegen extends KtVisitor 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() { + @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 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() { + @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 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() { + @Override + public Unit invoke(InstructionAdapter adapter) { + generateFor(forExpression); + return Unit.INSTANCE; + } + }); + } + + private void generateFor(@NotNull KtForExpression forExpression) { ResolvedCall 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 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 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()) { diff --git a/compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt b/compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt new file mode 100644 index 00000000000..d5686ab6128 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt @@ -0,0 +1,51 @@ +class Controller { + var result = "" + var ok = false + suspend fun suspendHere(v: String, x: Continuation) { + result += v + x.resume(Unit) + } + + operator fun handleResult(u: Unit, v: Continuation) { + ok = true + } +} + +fun builder(coroutine c: Controller.() -> Continuation): 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" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 047b6165515..e9ea4900d7f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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");