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
|
@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();
|
Label condition = new Label();
|
||||||
v.mark(condition);
|
v.mark(condition);
|
||||||
|
|
||||||
@@ -523,13 +533,20 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
v.mark(end);
|
v.mark(end);
|
||||||
|
|
||||||
blockStackElements.pop();
|
blockStackElements.pop();
|
||||||
|
|
||||||
return StackValue.onStack(Type.VOID_TYPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@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();
|
Label beginLoopLabel = new Label();
|
||||||
v.mark(beginLoopLabel);
|
v.mark(beginLoopLabel);
|
||||||
|
|
||||||
@@ -567,25 +584,34 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
v.mark(breakLabel);
|
v.mark(breakLabel);
|
||||||
|
|
||||||
blockStackElements.pop();
|
blockStackElements.pop();
|
||||||
return StackValue.none();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
ResolvedCall<? extends CallableDescriptor> loopRangeCall = RangeCodegenUtil.getLoopRangeResolvedCall(forExpression, bindingContext);
|
||||||
if (loopRangeCall != null) {
|
if (loopRangeCall != null) {
|
||||||
CallableDescriptor loopRangeCallee = loopRangeCall.getResultingDescriptor();
|
CallableDescriptor loopRangeCallee = loopRangeCall.getResultingDescriptor();
|
||||||
if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) {
|
if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) {
|
||||||
generateForLoop(createForInRangeLiteralLoopGenerator(forExpression, loopRangeCall));
|
generateForLoop(createForInRangeLiteralLoopGenerator(forExpression, loopRangeCall));
|
||||||
return StackValue.none();
|
return;
|
||||||
}
|
}
|
||||||
else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
||||||
generateForLoop(createForInArrayIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
generateForLoop(createForInArrayIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
||||||
return StackValue.none();
|
return;
|
||||||
}
|
}
|
||||||
else if (RangeCodegenUtil.isCollectionIndices(loopRangeCallee)) {
|
else if (RangeCodegenUtil.isCollectionIndices(loopRangeCallee)) {
|
||||||
generateForLoop(createForInCollectionIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
generateForLoop(createForInCollectionIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
||||||
return StackValue.none();
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,21 +622,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
Type asmLoopRangeType = asmType(loopRangeType);
|
Type asmLoopRangeType = asmType(loopRangeType);
|
||||||
if (asmLoopRangeType.getSort() == Type.ARRAY) {
|
if (asmLoopRangeType.getSort() == Type.ARRAY) {
|
||||||
generateForLoop(new ForInArrayLoopGenerator(forExpression));
|
generateForLoop(new ForInArrayLoopGenerator(forExpression));
|
||||||
return StackValue.none();
|
|
||||||
}
|
}
|
||||||
|
else if (RangeCodegenUtil.isRange(loopRangeType)) {
|
||||||
if (RangeCodegenUtil.isRange(loopRangeType)) {
|
|
||||||
generateForLoop(new ForInRangeInstanceLoopGenerator(forExpression));
|
generateForLoop(new ForInRangeInstanceLoopGenerator(forExpression));
|
||||||
return StackValue.none();
|
|
||||||
}
|
}
|
||||||
|
else if (RangeCodegenUtil.isProgression(loopRangeType)) {
|
||||||
if (RangeCodegenUtil.isProgression(loopRangeType)) {
|
|
||||||
generateForLoop(new ForInProgressionExpressionLoopGenerator(forExpression));
|
generateForLoop(new ForInProgressionExpressionLoopGenerator(forExpression));
|
||||||
return StackValue.none();
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
generateForLoop(new IteratorForLoopGenerator(forExpression));
|
generateForLoop(new IteratorForLoopGenerator(forExpression));
|
||||||
return StackValue.none();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractForLoopGenerator createForInRangeLiteralLoopGenerator(
|
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)
|
// 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);
|
StackValue result = isExpression ? gen(possiblyLabeledStatement) : genStatement(possiblyLabeledStatement);
|
||||||
|
|
||||||
if (!iterator.hasNext()) {
|
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);
|
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")
|
@TestMetadata("lastUnitExpression.kt")
|
||||||
public void testLastUnitExpression() throws Exception {
|
public void testLastUnitExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastUnitExpression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastUnitExpression.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user