From 1ab78dffe5c932d4d0fadb8cdc3a4f668c7d08d1 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 21 Dec 2016 17:22:43 +0300 Subject: [PATCH] JS: fix translation of `for` statement when either `next` or `hasNext` method translates to multiple statements. Fix KT-15367 --- .../codegen/box/coroutines/asyncIterator.kt | 1 - .../semantics/JsCodegenBoxTestGenerated.java | 6 ++++ .../js/translate/expression/LoopTranslator.kt | 34 ++++++++++++++----- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/compiler/testData/codegen/box/coroutines/asyncIterator.kt b/compiler/testData/codegen/box/coroutines/asyncIterator.kt index bb986cb6266..027eeb08845 100644 --- a/compiler/testData/codegen/box/coroutines/asyncIterator.kt +++ b/compiler/testData/codegen/box/coroutines/asyncIterator.kt @@ -1,6 +1,5 @@ // WITH_RUNTIME // WITH_COROUTINES -// TARGET_BACKEND: JVM import kotlin.coroutines.* interface AsyncGenerator { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 03ca841e255..98d48f87f9d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -5298,6 +5298,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("asyncIterator.kt") + public void testAsyncIterator() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIterator.kt"); + doTest(fileName); + } + @TestMetadata("await.kt") public void testAwait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt index 2faebf8bb6b..2ee1c7d4052 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt @@ -179,25 +179,43 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont fun translateForOverIterator(): JsStatement { - fun translateMethodInvocation(receiver: JsExpression?, resolvedCall: ResolvedCall): JsExpression = - CallTranslator.translate(context, resolvedCall, receiver) + fun translateMethodInvocation( + receiver: JsExpression?, + resolvedCall: ResolvedCall, + block: JsBlock + ): JsExpression = CallTranslator.translate(context.innerBlock(block), resolvedCall, receiver) fun iteratorMethodInvocation(): JsExpression { val range = Translation.translateAsExpression(loopRange, context) val resolvedCall = getIteratorFunction(context.bindingContext(), loopRange) - return translateMethodInvocation(range, resolvedCall) + return CallTranslator.translate(context, resolvedCall, range) } val iteratorVar = context.defineTemporary(iteratorMethodInvocation()) - fun hasNextMethodInvocation(): JsExpression { + fun hasNextMethodInvocation(block: JsBlock): JsExpression { val resolvedCall = getHasNextCallable(context.bindingContext(), loopRange) - return translateMethodInvocation(iteratorVar, resolvedCall) + return translateMethodInvocation(iteratorVar, resolvedCall, block) } - val nextInvoke = translateMethodInvocation(iteratorVar, getNextFunction(context.bindingContext(), loopRange)) - val body = translateBody(nextInvoke) - return JsWhile(hasNextMethodInvocation(), body ?: nextInvoke.makeStmt()) + val hasNextBlock = JsBlock() + val hasNextInvocation = hasNextMethodInvocation(hasNextBlock) + + val nextBlock = JsBlock() + val nextInvoke = translateMethodInvocation(iteratorVar, getNextFunction(context.bindingContext(), loopRange), nextBlock) + + val bodyStatements = mutableListOf() + val exitCondition = if (hasNextBlock.isEmpty) { + hasNextInvocation + } + else { + bodyStatements += hasNextBlock.statements + bodyStatements += JsIf(notOptimized(hasNextInvocation), JsBreak()) + JsLiteral.TRUE + } + bodyStatements += nextBlock.statements + bodyStatements += translateBody(nextInvoke)?.let(::flattenStatement).orEmpty() + return JsWhile(exitCondition, bodyStatements.singleOrNull() ?: JsBlock(bodyStatements)) } return when {