JS: fix translation of for statement when either next or hasNext method translates to multiple statements. Fix KT-15367

This commit is contained in:
Alexey Andreev
2016-12-21 17:22:43 +03:00
parent cef32b3327
commit 1ab78dffe5
3 changed files with 32 additions and 9 deletions
@@ -1,6 +1,5 @@
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// TARGET_BACKEND: JVM
import kotlin.coroutines.* import kotlin.coroutines.*
interface AsyncGenerator<in T> { interface AsyncGenerator<in T> {
@@ -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); 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") @TestMetadata("await.kt")
public void testAwait() throws Exception { public void testAwait() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt");
@@ -179,25 +179,43 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
fun translateForOverIterator(): JsStatement { fun translateForOverIterator(): JsStatement {
fun translateMethodInvocation(receiver: JsExpression?, resolvedCall: ResolvedCall<FunctionDescriptor>): JsExpression = fun translateMethodInvocation(
CallTranslator.translate(context, resolvedCall, receiver) receiver: JsExpression?,
resolvedCall: ResolvedCall<FunctionDescriptor>,
block: JsBlock
): JsExpression = CallTranslator.translate(context.innerBlock(block), resolvedCall, receiver)
fun iteratorMethodInvocation(): JsExpression { fun iteratorMethodInvocation(): JsExpression {
val range = Translation.translateAsExpression(loopRange, context) val range = Translation.translateAsExpression(loopRange, context)
val resolvedCall = getIteratorFunction(context.bindingContext(), loopRange) val resolvedCall = getIteratorFunction(context.bindingContext(), loopRange)
return translateMethodInvocation(range, resolvedCall) return CallTranslator.translate(context, resolvedCall, range)
} }
val iteratorVar = context.defineTemporary(iteratorMethodInvocation()) val iteratorVar = context.defineTemporary(iteratorMethodInvocation())
fun hasNextMethodInvocation(): JsExpression { fun hasNextMethodInvocation(block: JsBlock): JsExpression {
val resolvedCall = getHasNextCallable(context.bindingContext(), loopRange) val resolvedCall = getHasNextCallable(context.bindingContext(), loopRange)
return translateMethodInvocation(iteratorVar, resolvedCall) return translateMethodInvocation(iteratorVar, resolvedCall, block)
} }
val nextInvoke = translateMethodInvocation(iteratorVar, getNextFunction(context.bindingContext(), loopRange)) val hasNextBlock = JsBlock()
val body = translateBody(nextInvoke) val hasNextInvocation = hasNextMethodInvocation(hasNextBlock)
return JsWhile(hasNextMethodInvocation(), body ?: nextInvoke.makeStmt())
val nextBlock = JsBlock()
val nextInvoke = translateMethodInvocation(iteratorVar, getNextFunction(context.bindingContext(), loopRange), nextBlock)
val bodyStatements = mutableListOf<JsStatement>()
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 { return when {