Fix generation of JS source maps for suspend functions

This commit is contained in:
Alexey Andreev
2017-05-04 18:45:51 +03:00
parent 28140b0883
commit 3077a0f640
8 changed files with 105 additions and 61 deletions
@@ -114,7 +114,8 @@ data class CoroutineMetadata(
val baseClassRef: JsExpression, val baseClassRef: JsExpression,
val suspendObjectRef: JsExpression, val suspendObjectRef: JsExpression,
val hasController: Boolean, val hasController: Boolean,
val hasReceiver: Boolean val hasReceiver: Boolean,
val psiElement: PsiElement?
) )
enum class TypeCheck { enum class TypeCheck {
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 ifBlock.statements += x
val jointBlock = CoroutineBlock() val jointBlock = CoroutineBlock()
thenExitBlock.statements += stateAndJump(jointBlock) thenExitBlock.statements += stateAndJump(jointBlock, x)
elseExitBlock.statements += stateAndJump(jointBlock) elseExitBlock.statements += stateAndJump(jointBlock, x)
currentBlock = jointBlock currentBlock = jointBlock
} }
@@ -108,7 +108,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
accept(inner) accept(inner)
} }
if (successor in referencedBlocks) { if (successor in referencedBlocks) {
currentBlock.statements += stateAndJump(successor) currentBlock.statements += stateAndJump(successor, x)
currentBlock = successor currentBlock = successor
} }
} }
@@ -118,25 +118,25 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
override fun visitWhile(x: JsWhile) = splitIfNecessary(x) { override fun visitWhile(x: JsWhile) = splitIfNecessary(x) {
val successor = CoroutineBlock() val successor = CoroutineBlock()
val bodyEntryBlock = CoroutineBlock() val bodyEntryBlock = CoroutineBlock()
currentStatements += stateAndJump(bodyEntryBlock) currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = bodyEntryBlock currentBlock = bodyEntryBlock
if (x.condition != JsLiteral.TRUE) { 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) { withBreakAndContinue(x, successor, bodyEntryBlock) {
x.body.accept(this) x.body.accept(this)
} }
currentStatements += stateAndJump(bodyEntryBlock) currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = successor currentBlock = successor
} }
override fun visitDoWhile(x: JsDoWhile) = splitIfNecessary(x) { override fun visitDoWhile(x: JsDoWhile) = splitIfNecessary(x) {
val successor = CoroutineBlock() val successor = CoroutineBlock()
val bodyEntryBlock = CoroutineBlock() val bodyEntryBlock = CoroutineBlock()
currentStatements += stateAndJump(bodyEntryBlock) currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = bodyEntryBlock currentBlock = bodyEntryBlock
withBreakAndContinue(x, successor, bodyEntryBlock) { withBreakAndContinue(x, successor, bodyEntryBlock) {
@@ -144,10 +144,10 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
} }
if (x.condition != JsLiteral.TRUE) { 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) currentStatements.add(jsIf)
} }
currentBlock.statements += stateAndJump(bodyEntryBlock) currentBlock.statements += stateAndJump(bodyEntryBlock, x)
currentBlock = successor currentBlock = successor
} }
@@ -165,22 +165,22 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
val increment = CoroutineBlock() val increment = CoroutineBlock()
val successor = CoroutineBlock() val successor = CoroutineBlock()
val bodyEntryBlock = CoroutineBlock() val bodyEntryBlock = CoroutineBlock()
currentStatements += stateAndJump(bodyEntryBlock) currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = bodyEntryBlock currentBlock = bodyEntryBlock
if (x.condition != null && x.condition != JsLiteral.TRUE) { 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) { withBreakAndContinue(x, successor, increment) {
x.body.accept(this) x.body.accept(this)
} }
currentStatements += stateAndJump(increment) currentStatements += stateAndJump(increment, x)
currentBlock = increment currentBlock = increment
x.incrementExpression?.let { JsExpressionStatement(it).accept(this) } x.incrementExpression?.let { JsExpressionStatement(it).accept(this) }
currentStatements += stateAndJump(bodyEntryBlock) currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = successor currentBlock = successor
} }
@@ -189,7 +189,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
val targetStatement = breakContinueTargetStatements[x]!! val targetStatement = breakContinueTargetStatements[x]!!
val (targetBlock, targetTryDepth) = breakTargets[targetStatement]!! val (targetBlock, targetTryDepth) = breakTargets[targetStatement]!!
referencedBlocks += targetBlock referencedBlocks += targetBlock
jumpWithFinally(targetTryDepth + 1, targetBlock) jumpWithFinally(targetTryDepth + 1, targetBlock, x)
currentStatements += jump() currentStatements += jump()
} }
@@ -197,7 +197,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
val targetStatement = breakContinueTargetStatements[x]!! val targetStatement = breakContinueTargetStatements[x]!!
val (targetBlock, targetTryDepth) = continueTargets[targetStatement]!! val (targetBlock, targetTryDepth) = continueTargets[targetStatement]!!
referencedBlocks += targetBlock referencedBlocks += targetBlock
jumpWithFinally(targetTryDepth + 1, targetBlock) jumpWithFinally(targetTryDepth + 1, targetBlock, x)
currentStatements += jump() 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. * 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. * 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) { if (targetTryDepth < tryStack.size) {
val tryBlock = tryStack[targetTryDepth] val tryBlock = tryStack[targetTryDepth]
currentStatements += exceptionState(tryBlock.catchBlock) currentStatements += exceptionState(tryBlock.catchBlock, fromNode)
} }
val relativeFinallyPath = relativeFinallyPath(targetTryDepth) val relativeFinallyPath = relativeFinallyPath(targetTryDepth)
@@ -216,7 +216,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
if (fullPath.size > 1) { if (fullPath.size > 1) {
currentStatements += updateFinallyPath(fullPath.drop(1)) currentStatements += updateFinallyPath(fullPath.drop(1))
} }
currentStatements += state(fullPath[0]) currentStatements += state(fullPath[0], fromNode)
} }
override fun visitTry(x: JsTry) = splitIfNecessary(x) { override fun visitTry(x: JsTry) = splitIfNecessary(x) {
@@ -231,19 +231,19 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
val oldCatchBlock = currentCatchBlock val oldCatchBlock = currentCatchBlock
currentCatchBlock = catchBlock currentCatchBlock = catchBlock
currentStatements += exceptionState(catchBlock) currentStatements += exceptionState(catchBlock, x)
x.tryBlock.statements.forEach { it.accept(this) } x.tryBlock.statements.forEach { it.accept(this) }
currentStatements += exceptionState(oldCatchBlock) currentStatements += exceptionState(oldCatchBlock, x)
currentCatchBlock = oldCatchBlock currentCatchBlock = oldCatchBlock
if (finallyNode != null) { if (finallyNode != null) {
currentStatements += updateFinallyPath(listOf(successor)) currentStatements += updateFinallyPath(listOf(successor))
currentStatements += stateAndJump(finallyBlock) currentStatements += stateAndJump(finallyBlock, x)
} }
else { else {
currentStatements += stateAndJump(successor) currentStatements += stateAndJump(successor, x)
} }
// Handle catch node // Handle catch node
@@ -251,10 +251,10 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
if (finallyNode != null) { if (finallyNode != null) {
currentStatements += updateFinallyPath(listOf(oldCatchBlock)) 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 { else {
currentStatements += if (catchNode != null) exceptionState(oldCatchBlock) else stateAndJump(oldCatchBlock) currentStatements += if (catchNode != null) exceptionState(oldCatchBlock, x) else stateAndJump(oldCatchBlock, x)
} }
if (catchNode != null) { if (catchNode != null) {
@@ -263,11 +263,11 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
catchNode.body.statements.forEach { it.accept(this) } catchNode.body.statements.forEach { it.accept(this) }
if (finallyNode == null) { if (finallyNode == null) {
currentStatements += stateAndJump(successor) currentStatements += stateAndJump(successor, x)
} }
else { else {
currentStatements += updateFinallyPath(listOf(successor)) 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() val isInFinally = hasEnclosingFinallyBlock()
if (isInFinally) { if (isInFinally) {
val returnBlock = CoroutineBlock() val returnBlock = CoroutineBlock()
jumpWithFinally(0, returnBlock) jumpWithFinally(0, returnBlock, x)
val returnExpression = x.expression val returnExpression = x.expression
val returnFieldRef = if (returnExpression != null) { val returnFieldRef = if (returnExpression != null) {
val ref = JsNameRef(context.returnValueFieldName, JsAstUtils.stateMachineReceiver()) val ref = JsNameRef(context.returnValueFieldName, JsAstUtils.stateMachineReceiver())
@@ -343,47 +343,51 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
if (assignment != null) { if (assignment != null) {
val rhs = assignment.second val rhs = assignment.second
if (rhs.isSuspend) { if (rhs.isSuspend) {
handleSuspend(expression) handleSuspend(expression, rhs)
return null return null
} }
} }
else if (expression.isSuspend) { else if (expression.isSuspend) {
handleSuspend(expression) handleSuspend(expression, expression)
return null return null
} }
return expression return expression
} }
private fun handleSuspend(invocation: JsExpression) { private fun handleSuspend(invocation: JsExpression, sourceNode: JsExpression) {
val psi = sourceNode.source ?: invocation.source
val nextBlock = CoroutineBlock() val nextBlock = CoroutineBlock()
currentStatements += state(nextBlock) currentStatements += state(nextBlock, invocation)
val resultRef = JsNameRef(context.metadata.resultName, JsAstUtils.stateMachineReceiver()).apply { val resultRef = JsNameRef(context.metadata.resultName, JsAstUtils.stateMachineReceiver()).apply {
sideEffects = SideEffectKind.DEPENDS_ON_STATE sideEffects = SideEffectKind.DEPENDS_ON_STATE
} }
val invocationStatement = JsAstUtils.assignment(resultRef, invocation).makeStmt() val invocationStatement = JsAstUtils.assignment(resultRef, invocation).makeStmt()
val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), context.metadata.suspendObjectRef.deepCopy()) val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), context.metadata.suspendObjectRef.deepCopy()).source(psi)
val suspendIfNeeded = JsIf(suspendCondition, JsReturn(context.metadata.suspendObjectRef.deepCopy())) val suspendIfNeeded = JsIf(suspendCondition, JsReturn(context.metadata.suspendObjectRef.deepCopy().source(psi)))
currentStatements += listOf(invocationStatement, suspendIfNeeded, JsBreak()) currentStatements += listOf(invocationStatement, suspendIfNeeded, JsBreak().apply { source = psi })
currentBlock = nextBlock currentBlock = nextBlock
} }
private fun state(target: CoroutineBlock): List<JsStatement> { private fun state(target: CoroutineBlock, fromExpression: JsNode): List<JsStatement> {
val placeholder = JsDebugger() val placeholder = JsDebugger()
placeholder.targetBlock = target placeholder.targetBlock = target
placeholder.source = fromExpression.source
return listOf(placeholder) return listOf(placeholder)
} }
private fun jump() = JsContinue() private fun jump() = JsContinue()
private fun stateAndJump(target: CoroutineBlock): List<JsStatement> { private fun stateAndJump(target: CoroutineBlock, fromNode: JsNode): List<JsStatement> {
return state(target) + jump() return state(target, fromNode) + jump()
} }
private fun exceptionState(target: CoroutineBlock): List<JsStatement> { private fun exceptionState(target: CoroutineBlock, fromNode: JsNode): List<JsStatement> {
val placeholder = JsDebugger() val placeholder = JsDebugger()
placeholder.targetExceptionBlock = target placeholder.targetExceptionBlock = target
placeholder.source = fromNode
return listOf(placeholder) return listOf(placeholder)
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.coroutine 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.*
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
@@ -57,6 +58,8 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
statements: MutableList<JsStatement>, statements: MutableList<JsStatement>,
globalCatchBlockIndex: Int globalCatchBlockIndex: Int
) { ) {
val psiElement = context.metadata.psiElement
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation") val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
constructor.name = className constructor.name = className
if (context.metadata.hasReceiver) { if (context.metadata.hasReceiver) {
@@ -80,17 +83,17 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
constructor.body.statements.run { constructor.body.statements.run {
val baseClass = context.metadata.baseClassRef.deepCopy() 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) { 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) { if (context.metadata.hasReceiver) {
assignToField(context.receiverFieldName, context.receiverFieldName.makeRef()) assignToField(context.receiverFieldName, context.receiverFieldName.makeRef(), psiElement)
} }
for (localVariable in localVariables) { for (localVariable in localVariables) {
val value = if (localVariable !in parameterNames) Namer.getUndefinedExpression() else localVariable.makeRef() 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) { 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) { if (context.metadata.hasReceiver) {
instantiation.arguments += JsLiteral.THIS instantiation.arguments += JsLiteral.THIS
} }
@@ -163,9 +167,13 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
val instanceName = JsScope.declareTemporaryName("instance") val instanceName = JsScope.declareTemporaryName("instance")
functionWithBody.body.statements += JsAstUtils.newVar(instanceName, instantiation) 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( private fun generateCoroutineBody(
@@ -208,8 +216,8 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
}) })
} }
private fun MutableList<JsStatement>.assignToField(fieldName: JsName, value: JsExpression) { private fun MutableList<JsStatement>.assignToField(fieldName: JsName, value: JsExpression, psiElement: PsiElement?) {
this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).makeStmt() this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).source(psiElement).makeStmt()
} }
private fun MutableList<JsStatement>.assignToPrototype(fieldName: JsName, value: JsExpression) { private fun MutableList<JsStatement>.assignToPrototype(fieldName: JsName, value: JsExpression) {
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -122,7 +122,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
if (target != null) { if (target != null) {
val lhs = JsNameRef(context.metadata.stateName, JsAstUtils.stateMachineReceiver()) val lhs = JsNameRef(context.metadata.stateName, JsAstUtils.stateMachineReceiver())
val rhs = program.getNumberLiteral(blockIndexes[target]!!) 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 targetBlock = true
}) })
} }
@@ -131,7 +131,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
if (exceptionTarget != null) { if (exceptionTarget != null) {
val lhs = JsNameRef(context.metadata.exceptionStateName, JsAstUtils.stateMachineReceiver()) val lhs = JsNameRef(context.metadata.exceptionStateName, JsAstUtils.stateMachineReceiver())
val rhs = program.getNumberLiteral(blockIndexes[exceptionTarget]!!) 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 targetExceptionBlock = true
}) })
} }
@@ -141,7 +141,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
if (finallyPath.isNotEmpty()) { if (finallyPath.isNotEmpty()) {
val lhs = JsNameRef(context.metadata.finallyPathName, JsAstUtils.stateMachineReceiver()) val lhs = JsNameRef(context.metadata.finallyPathName, JsAstUtils.stateMachineReceiver())
val rhs = JsArrayLiteral(finallyPath.map { program.getNumberLiteral(blockIndexes[it]!!) }) 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 this.finallyPath = true
}) })
} }
@@ -222,12 +222,14 @@ fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) {
x.coroutineController -> { x.coroutineController -> {
ctx.replaceMe(JsNameRef(context.controllerFieldName, x.qualifier).apply { ctx.replaceMe(JsNameRef(context.controllerFieldName, x.qualifier).apply {
source = x.source
sideEffects = SideEffectKind.PURE sideEffects = SideEffectKind.PURE
}) })
} }
x.coroutineResult -> { x.coroutineResult -> {
ctx.replaceMe(JsNameRef(context.metadata.resultName, x.qualifier).apply { ctx.replaceMe(JsNameRef(context.metadata.resultName, x.qualifier).apply {
source = x.source
sideEffects = SideEffectKind.DEPENDS_ON_STATE sideEffects = SideEffectKind.DEPENDS_ON_STATE
}) })
} }
@@ -262,7 +264,7 @@ fun JsBlock.replaceLocalVariables(context: CoroutineTransformationContext, local
override fun endVisit(x: JsNameRef, ctx: JsContext<in JsNode>) { override fun endVisit(x: JsNameRef, ctx: JsContext<in JsNode>) {
if (x.qualifier == null && x.name in localVariables) { if (x.qualifier == null && x.name in localVariables) {
val fieldName = context.getFieldName(x.name!!) val fieldName = context.getFieldName(x.name!!)
ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS)) ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS).source(x.source))
} }
} }
@@ -42,6 +42,12 @@ public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest {
doTest(fileName); 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") @TestMetadata("inlining.kt")
public void testInlining() throws Exception { public void testInlining() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/inlining.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/inlining.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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 (resolvedCall.resultingDescriptor.isSuspend) {
if (context.isInStateMachine) { if (context.isInStateMachine) {
context.currentBlock.statements += JsAstUtils.asSyntheticStatement(callExpression.apply { isSuspend = true }) context.currentBlock.statements += JsAstUtils.asSyntheticStatement(callExpression.apply {
val coroutineRef = TranslationUtils.translateContinuationArgument(context) isSuspend = true
source = resolvedCall.call.callElement
})
val coroutineRef = TranslationUtils.translateContinuationArgument(context).apply { source = resolvedCall.call.callElement }
return context.defineTemporary(JsNameRef("\$\$coroutineResult\$\$", coroutineRef).apply { return context.defineTemporary(JsNameRef("\$\$coroutineResult\$\$", coroutineRef).apply {
sideEffects = SideEffectKind.DEPENDS_ON_STATE sideEffects = SideEffectKind.DEPENDS_ON_STATE
source = resolvedCall.call.callElement
coroutineResult = true coroutineResult = true
}) })
} }
@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
fun generateDelegateCall( fun generateDelegateCall(
@@ -184,6 +185,7 @@ fun JsFunction.fillCoroutineMetadata(
resultName = getCoroutinePropertyName("result"), resultName = getCoroutinePropertyName("result"),
exceptionName = getCoroutinePropertyName("exception"), exceptionName = getCoroutinePropertyName("exception"),
hasController = hasController, hasController = hasController,
hasReceiver = descriptor.dispatchReceiverParameter != null hasReceiver = descriptor.dispatchReceiverParameter != null,
psiElement = descriptor.source.getPsi()
) )
} }
+17
View File
@@ -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