From 3077a0f64015ee8bc2ea24a8f9cfc380c86a404f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 4 May 2017 18:45:51 +0300 Subject: [PATCH] Fix generation of JS source maps for suspend functions --- .../ast/metadata/metadataProperties.kt | 3 +- .../js/coroutine/CoroutineBodyTransformer.kt | 82 ++++++++++--------- .../coroutine/CoroutineFunctionTransformer.kt | 30 ++++--- .../kotlin/js/coroutine/CoroutinePasses.kt | 12 +-- .../js/test/JsLineNumberTestGenerated.java | 6 ++ .../callTranslator/CallTranslator.kt | 10 ++- .../kotlin/js/translate/utils/utils.kt | 6 +- .../testData/lineNumbers/coroutine.kt | 17 ++++ 8 files changed, 105 insertions(+), 61 deletions(-) create mode 100644 js/js.translator/testData/lineNumbers/coroutine.kt diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt index 4ae7f8576aa..edb5d860254 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt @@ -114,7 +114,8 @@ data class CoroutineMetadata( val baseClassRef: JsExpression, val suspendObjectRef: JsExpression, val hasController: Boolean, - val hasReceiver: Boolean + val hasReceiver: Boolean, + val psiElement: PsiElement? ) enum class TypeCheck { diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineBodyTransformer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineBodyTransformer.kt index 6b1411812cb..b7856ca897c 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineBodyTransformer.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineBodyTransformer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,8 +83,8 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte ifBlock.statements += x val jointBlock = CoroutineBlock() - thenExitBlock.statements += stateAndJump(jointBlock) - elseExitBlock.statements += stateAndJump(jointBlock) + thenExitBlock.statements += stateAndJump(jointBlock, x) + elseExitBlock.statements += stateAndJump(jointBlock, x) currentBlock = jointBlock } @@ -108,7 +108,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte accept(inner) } if (successor in referencedBlocks) { - currentBlock.statements += stateAndJump(successor) + currentBlock.statements += stateAndJump(successor, x) currentBlock = successor } } @@ -118,25 +118,25 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte override fun visitWhile(x: JsWhile) = splitIfNecessary(x) { val successor = CoroutineBlock() val bodyEntryBlock = CoroutineBlock() - currentStatements += stateAndJump(bodyEntryBlock) + currentStatements += stateAndJump(bodyEntryBlock, x) currentBlock = bodyEntryBlock if (x.condition != JsLiteral.TRUE) { - currentStatements += JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor))).apply { source = x.source } + currentStatements += JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor, x))).apply { source = x.source } } withBreakAndContinue(x, successor, bodyEntryBlock) { x.body.accept(this) } - currentStatements += stateAndJump(bodyEntryBlock) + currentStatements += stateAndJump(bodyEntryBlock, x) currentBlock = successor } override fun visitDoWhile(x: JsDoWhile) = splitIfNecessary(x) { val successor = CoroutineBlock() val bodyEntryBlock = CoroutineBlock() - currentStatements += stateAndJump(bodyEntryBlock) + currentStatements += stateAndJump(bodyEntryBlock, x) currentBlock = bodyEntryBlock withBreakAndContinue(x, successor, bodyEntryBlock) { @@ -144,10 +144,10 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte } if (x.condition != JsLiteral.TRUE) { - val jsIf = JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor))).apply { source = x.source } + val jsIf = JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor, x))).apply { source = x.source } currentStatements.add(jsIf) } - currentBlock.statements += stateAndJump(bodyEntryBlock) + currentBlock.statements += stateAndJump(bodyEntryBlock, x) currentBlock = successor } @@ -165,22 +165,22 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte val increment = CoroutineBlock() val successor = CoroutineBlock() val bodyEntryBlock = CoroutineBlock() - currentStatements += stateAndJump(bodyEntryBlock) + currentStatements += stateAndJump(bodyEntryBlock, x) currentBlock = bodyEntryBlock if (x.condition != null && x.condition != JsLiteral.TRUE) { - currentStatements += JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor))).apply { source = x.source } + currentStatements += JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor, x))).apply { source = x.source } } withBreakAndContinue(x, successor, increment) { x.body.accept(this) } - currentStatements += stateAndJump(increment) + currentStatements += stateAndJump(increment, x) currentBlock = increment x.incrementExpression?.let { JsExpressionStatement(it).accept(this) } - currentStatements += stateAndJump(bodyEntryBlock) + currentStatements += stateAndJump(bodyEntryBlock, x) currentBlock = successor } @@ -189,7 +189,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte val targetStatement = breakContinueTargetStatements[x]!! val (targetBlock, targetTryDepth) = breakTargets[targetStatement]!! referencedBlocks += targetBlock - jumpWithFinally(targetTryDepth + 1, targetBlock) + jumpWithFinally(targetTryDepth + 1, targetBlock, x) currentStatements += jump() } @@ -197,7 +197,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte val targetStatement = breakContinueTargetStatements[x]!! val (targetBlock, targetTryDepth) = continueTargets[targetStatement]!! referencedBlocks += targetBlock - jumpWithFinally(targetTryDepth + 1, targetBlock) + jumpWithFinally(targetTryDepth + 1, targetBlock, x) currentStatements += jump() } @@ -205,10 +205,10 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte * When we perform break, continue or return, we can leave try blocks, so we should update $exceptionHandler correspondingly. * Also, these try blocks can contain finally clauses, therefore we need to update $finallyPath as well. */ - private fun jumpWithFinally(targetTryDepth: Int, successor: CoroutineBlock) { + private fun jumpWithFinally(targetTryDepth: Int, successor: CoroutineBlock, fromNode: JsNode) { if (targetTryDepth < tryStack.size) { val tryBlock = tryStack[targetTryDepth] - currentStatements += exceptionState(tryBlock.catchBlock) + currentStatements += exceptionState(tryBlock.catchBlock, fromNode) } val relativeFinallyPath = relativeFinallyPath(targetTryDepth) @@ -216,7 +216,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte if (fullPath.size > 1) { currentStatements += updateFinallyPath(fullPath.drop(1)) } - currentStatements += state(fullPath[0]) + currentStatements += state(fullPath[0], fromNode) } override fun visitTry(x: JsTry) = splitIfNecessary(x) { @@ -231,19 +231,19 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte val oldCatchBlock = currentCatchBlock currentCatchBlock = catchBlock - currentStatements += exceptionState(catchBlock) + currentStatements += exceptionState(catchBlock, x) x.tryBlock.statements.forEach { it.accept(this) } - currentStatements += exceptionState(oldCatchBlock) + currentStatements += exceptionState(oldCatchBlock, x) currentCatchBlock = oldCatchBlock if (finallyNode != null) { currentStatements += updateFinallyPath(listOf(successor)) - currentStatements += stateAndJump(finallyBlock) + currentStatements += stateAndJump(finallyBlock, x) } else { - currentStatements += stateAndJump(successor) + currentStatements += stateAndJump(successor, x) } // Handle catch node @@ -251,10 +251,10 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte if (finallyNode != null) { currentStatements += updateFinallyPath(listOf(oldCatchBlock)) - currentStatements += if (catchNode != null) exceptionState(finallyBlock) else stateAndJump(finallyBlock) + currentStatements += if (catchNode != null) exceptionState(finallyBlock, x) else stateAndJump(finallyBlock, x) } else { - currentStatements += if (catchNode != null) exceptionState(oldCatchBlock) else stateAndJump(oldCatchBlock) + currentStatements += if (catchNode != null) exceptionState(oldCatchBlock, x) else stateAndJump(oldCatchBlock, x) } if (catchNode != null) { @@ -263,11 +263,11 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte catchNode.body.statements.forEach { it.accept(this) } if (finallyNode == null) { - currentStatements += stateAndJump(successor) + currentStatements += stateAndJump(successor, x) } else { currentStatements += updateFinallyPath(listOf(successor)) - currentStatements += stateAndJump(finallyBlock) + currentStatements += stateAndJump(finallyBlock, x) } } @@ -314,7 +314,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte val isInFinally = hasEnclosingFinallyBlock() if (isInFinally) { val returnBlock = CoroutineBlock() - jumpWithFinally(0, returnBlock) + jumpWithFinally(0, returnBlock, x) val returnExpression = x.expression val returnFieldRef = if (returnExpression != null) { val ref = JsNameRef(context.returnValueFieldName, JsAstUtils.stateMachineReceiver()) @@ -343,47 +343,51 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte if (assignment != null) { val rhs = assignment.second if (rhs.isSuspend) { - handleSuspend(expression) + handleSuspend(expression, rhs) return null } } else if (expression.isSuspend) { - handleSuspend(expression) + handleSuspend(expression, expression) return null } return expression } - private fun handleSuspend(invocation: JsExpression) { + private fun handleSuspend(invocation: JsExpression, sourceNode: JsExpression) { + val psi = sourceNode.source ?: invocation.source + val nextBlock = CoroutineBlock() - currentStatements += state(nextBlock) + currentStatements += state(nextBlock, invocation) val resultRef = JsNameRef(context.metadata.resultName, JsAstUtils.stateMachineReceiver()).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE } val invocationStatement = JsAstUtils.assignment(resultRef, invocation).makeStmt() - val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), context.metadata.suspendObjectRef.deepCopy()) - val suspendIfNeeded = JsIf(suspendCondition, JsReturn(context.metadata.suspendObjectRef.deepCopy())) - currentStatements += listOf(invocationStatement, suspendIfNeeded, JsBreak()) + val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), context.metadata.suspendObjectRef.deepCopy()).source(psi) + val suspendIfNeeded = JsIf(suspendCondition, JsReturn(context.metadata.suspendObjectRef.deepCopy().source(psi))) + currentStatements += listOf(invocationStatement, suspendIfNeeded, JsBreak().apply { source = psi }) currentBlock = nextBlock } - private fun state(target: CoroutineBlock): List { + private fun state(target: CoroutineBlock, fromExpression: JsNode): List { val placeholder = JsDebugger() placeholder.targetBlock = target + placeholder.source = fromExpression.source return listOf(placeholder) } private fun jump() = JsContinue() - private fun stateAndJump(target: CoroutineBlock): List { - return state(target) + jump() + private fun stateAndJump(target: CoroutineBlock, fromNode: JsNode): List { + return state(target, fromNode) + jump() } - private fun exceptionState(target: CoroutineBlock): List { + private fun exceptionState(target: CoroutineBlock, fromNode: JsNode): List { val placeholder = JsDebugger() placeholder.targetExceptionBlock = target + placeholder.source = fromNode return listOf(placeholder) } diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt index a90a114d669..c27d7ecda40 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.js.coroutine +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor @@ -57,6 +58,8 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f statements: MutableList, globalCatchBlockIndex: Int ) { + val psiElement = context.metadata.psiElement + val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation") constructor.name = className if (context.metadata.hasReceiver) { @@ -80,17 +83,17 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f constructor.body.statements.run { val baseClass = context.metadata.baseClassRef.deepCopy() - this += JsInvocation(Namer.getFunctionCallRef(baseClass), JsLiteral.THIS, interceptorRef).makeStmt() + this += JsInvocation(Namer.getFunctionCallRef(baseClass), JsLiteral.THIS, interceptorRef).source(psiElement).makeStmt() if (controllerName != null) { - assignToField(context.controllerFieldName, controllerName.makeRef()) + assignToField(context.controllerFieldName, controllerName.makeRef(), psiElement) } - assignToField(context.metadata.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex)) + assignToField(context.metadata.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex), psiElement) if (context.metadata.hasReceiver) { - assignToField(context.receiverFieldName, context.receiverFieldName.makeRef()) + assignToField(context.receiverFieldName, context.receiverFieldName.makeRef(), psiElement) } for (localVariable in localVariables) { val value = if (localVariable !in parameterNames) Namer.getUndefinedExpression() else localVariable.makeRef() - assignToField(context.getFieldName(localVariable), value) + assignToField(context.getFieldName(localVariable), value, psiElement) } } @@ -144,7 +147,8 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f } private fun generateCoroutineInstantiation(context: CoroutineTransformationContext) { - val instantiation = JsNew(className.makeRef()) + val psiElement = context.metadata.psiElement + val instantiation = JsNew(className.makeRef()).apply { source = psiElement } if (context.metadata.hasReceiver) { instantiation.arguments += JsLiteral.THIS } @@ -163,9 +167,13 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f val instanceName = JsScope.declareTemporaryName("instance") functionWithBody.body.statements += JsAstUtils.newVar(instanceName, instantiation) - val invokeResume = JsReturn(JsInvocation(JsNameRef(context.metadata.doResumeName, instanceName.makeRef()), JsLiteral.NULL)) + val invokeResume = JsReturn(JsInvocation(JsNameRef(context.metadata.doResumeName, instanceName.makeRef()), JsLiteral.NULL) + .source(psiElement)) - functionWithBody.body.statements += JsIf(suspendedName.makeRef(), JsReturn(instanceName.makeRef()), invokeResume) + functionWithBody.body.statements += JsIf( + suspendedName.makeRef().source(psiElement), + JsReturn(instanceName.makeRef().source(psiElement)), + invokeResume) } private fun generateCoroutineBody( @@ -208,8 +216,8 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f }) } - private fun MutableList.assignToField(fieldName: JsName, value: JsExpression) { - this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).makeStmt() + private fun MutableList.assignToField(fieldName: JsName, value: JsExpression, psiElement: PsiElement?) { + this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).source(psiElement).makeStmt() } private fun MutableList.assignToPrototype(fieldName: JsName, value: JsExpression) { diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutinePasses.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutinePasses.kt index 467ddc5c091..34a4bc20f36 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutinePasses.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutinePasses.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -122,7 +122,7 @@ fun List.replaceCoroutineFlowStatements(context: CoroutineTransf if (target != null) { val lhs = JsNameRef(context.metadata.stateName, JsAstUtils.stateMachineReceiver()) val rhs = program.getNumberLiteral(blockIndexes[target]!!) - ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply { + ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs).source(x.source)).apply { targetBlock = true }) } @@ -131,7 +131,7 @@ fun List.replaceCoroutineFlowStatements(context: CoroutineTransf if (exceptionTarget != null) { val lhs = JsNameRef(context.metadata.exceptionStateName, JsAstUtils.stateMachineReceiver()) val rhs = program.getNumberLiteral(blockIndexes[exceptionTarget]!!) - ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply { + ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs).source(x.source)).apply { targetExceptionBlock = true }) } @@ -141,7 +141,7 @@ fun List.replaceCoroutineFlowStatements(context: CoroutineTransf if (finallyPath.isNotEmpty()) { val lhs = JsNameRef(context.metadata.finallyPathName, JsAstUtils.stateMachineReceiver()) val rhs = JsArrayLiteral(finallyPath.map { program.getNumberLiteral(blockIndexes[it]!!) }) - ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply { + ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs).source(x.source)).apply { this.finallyPath = true }) } @@ -222,12 +222,14 @@ fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) { x.coroutineController -> { ctx.replaceMe(JsNameRef(context.controllerFieldName, x.qualifier).apply { + source = x.source sideEffects = SideEffectKind.PURE }) } x.coroutineResult -> { ctx.replaceMe(JsNameRef(context.metadata.resultName, x.qualifier).apply { + source = x.source sideEffects = SideEffectKind.DEPENDS_ON_STATE }) } @@ -262,7 +264,7 @@ fun JsBlock.replaceLocalVariables(context: CoroutineTransformationContext, local override fun endVisit(x: JsNameRef, ctx: JsContext) { if (x.qualifier == null && x.name in localVariables) { val fieldName = context.getFieldName(x.name!!) - ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS)) + ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS).source(x.source)) } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java index 9c3dedcbd7d..780ee80e43b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java @@ -42,6 +42,12 @@ public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest { doTest(fileName); } + @TestMetadata("coroutine.kt") + public void testCoroutine() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/coroutine.kt"); + doTest(fileName); + } + @TestMetadata("inlining.kt") public void testInlining() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/inlining.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt index 3293cf7182e..f44d61052d1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,10 +136,14 @@ private fun translateFunctionCall( if (resolvedCall.resultingDescriptor.isSuspend) { if (context.isInStateMachine) { - context.currentBlock.statements += JsAstUtils.asSyntheticStatement(callExpression.apply { isSuspend = true }) - val coroutineRef = TranslationUtils.translateContinuationArgument(context) + context.currentBlock.statements += JsAstUtils.asSyntheticStatement(callExpression.apply { + isSuspend = true + source = resolvedCall.call.callElement + }) + val coroutineRef = TranslationUtils.translateContinuationArgument(context).apply { source = resolvedCall.call.callElement } return context.defineTemporary(JsNameRef("\$\$coroutineResult\$\$", coroutineRef).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE + source = resolvedCall.call.callElement coroutineResult = true }) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index 114509ba0c7..ff78c27a58f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunc import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue +import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType fun generateDelegateCall( @@ -184,6 +185,7 @@ fun JsFunction.fillCoroutineMetadata( resultName = getCoroutinePropertyName("result"), exceptionName = getCoroutinePropertyName("exception"), hasController = hasController, - hasReceiver = descriptor.dispatchReceiverParameter != null + hasReceiver = descriptor.dispatchReceiverParameter != null, + psiElement = descriptor.source.getPsi() ) } diff --git a/js/js.translator/testData/lineNumbers/coroutine.kt b/js/js.translator/testData/lineNumbers/coroutine.kt new file mode 100644 index 00000000000..4fecffacfc0 --- /dev/null +++ b/js/js.translator/testData/lineNumbers/coroutine.kt @@ -0,0 +1,17 @@ +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +suspend fun foo(value: Int): Int = suspendCoroutineOrReturn { c -> + c.resume(value) + COROUTINE_SUSPENDED +} + +suspend fun bar(): Unit { + println("!") + val a = foo(2) + println("!") + val b = foo(3) + println(a + b) +} + +// LINES: 4 5 6 * 9 9 9 9 9 9 9 9 * 10 11 11 11 11 11 * 11 12 13 13 13 13 13 13 14 \ No newline at end of file