JS: implement new coroutine convention

This commit is contained in:
Alexey Andreev
2016-12-14 19:38:00 +03:00
committed by Stanislav Erokhin
parent 9fd1ac72a9
commit e2d969d8b0
20 changed files with 353 additions and 315 deletions
@@ -17,10 +17,10 @@ class Controller {
}
}
fun builder(coroutine c: () -> String): String {
fun builder(c: suspend Controller.() -> String): String {
val controller = Controller()
c.startCoroutine(handleResult {
controller.result += "return($value);"
c.startCoroutine(controller, handleResultContinuation {
controller.result += "return($it);"
})
return controller.result
}
@@ -13,7 +13,7 @@ class Controller {
}
}
// MODULE: main(controller)
// MODULE: main(controller, support)
// FILE: main.kt
import lib.*
import kotlin.coroutines.*
-2
View File
@@ -1,9 +1,7 @@
// IGNORE_BACKEND: JS
// WITH_RUNTIME
// WITH_COROUTINES
import kotlin.coroutines.*
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
SUSPENDED
@@ -602,6 +602,7 @@ public class KotlinTestUtils {
List<F> testFiles = Lists.newArrayList();
Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText);
boolean hasModules = false;
if (!matcher.find()) {
// One file
testFiles.add(factory.createFile(null, testFileName, expectedText, directives));
@@ -614,6 +615,7 @@ public class KotlinTestUtils {
String moduleName = matcher.group(1);
String moduleDependencies = matcher.group(2);
if (moduleName != null) {
hasModules = true;
module = factory.createModule(moduleName, parseDependencies(moduleDependencies));
}
@@ -642,7 +644,8 @@ public class KotlinTestUtils {
}
if (isDirectiveDefined(expectedText, "WITH_COROUTINES")) {
testFiles.add(factory.createFile(null,
M supportModule = hasModules ? factory.createModule("support", Collections.<String>emptyList()) : null;
testFiles.add(factory.createFile(supportModule,
"CoroutineUtil.kt",
"import kotlin.coroutines.*\n" +
"fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {\n" +
@@ -20,9 +20,9 @@ package com.google.dart.compiler.backend.js.ast.metadata
import com.google.dart.compiler.backend.js.ast.*
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
import org.jetbrains.kotlin.types.KotlinType
var JsName.staticRef: JsNode? by MetadataProperty(default = null)
@@ -64,9 +64,7 @@ var HasMetadata.synthetic: Boolean by MetadataProperty(default = false)
var HasMetadata.sideEffects: SideEffectKind by MetadataProperty(default = SideEffectKind.AFFECTS_STATE)
var JsFunction.coroutineType: ClassDescriptor? by MetadataProperty(default = null)
var JsFunction.controllerType: ClassDescriptor? by MetadataProperty(default = null)
var JsFunction.coroutineType: KotlinType? by MetadataProperty(default = null)
/**
* Denotes a suspension call-site that is to be processed by coroutine transformer.
@@ -86,12 +84,6 @@ var JsInvocation.isPreSuspend: Boolean by MetadataProperty(default = false)
*/
var JsInvocation.isFakeSuspend: Boolean by MetadataProperty(default = false)
/**
* Denotes a call to coroutine's controller `handleResult` function.
* See coroutine spec for explanation.
*/
var JsInvocation.isHandleResult: Boolean by MetadataProperty(default = false)
/**
* Denotes a reference to coroutine's `result` field that contains result of
* last suspended invocation.
@@ -99,17 +91,25 @@ var JsInvocation.isHandleResult: Boolean by MetadataProperty(default = false)
var JsNameRef.coroutineResult by MetadataProperty(default = false)
/**
* Denotes a reference to coroutine's `controller` field that contains coroutines's controller
* Denotes a reference to coroutine's `interceptor` field that contains coroutines's interceptor
*/
var JsNameRef.coroutineController by MetadataProperty(default = false)
var JsFunction.suspendObjectRef: JsExpression? by MetadataProperty(default = null)
var JsFunction.continuationInterfaceRef: JsExpression? by MetadataProperty(default = null)
var JsName.imported by MetadataProperty(default = false)
var JsFunction.interceptResumeRef: JsExpression? by MetadataProperty(default = null)
var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default = null)
class CoroutineMetadata(
val doResumeName: JsName,
val stateName: JsName,
val exceptionStateName: JsName,
val finallyPathName: JsName,
val resultName: JsName,
val exceptionName: JsName,
val baseClassRef: JsExpression,
val suspendObjectRef: JsExpression,
val hasController: Boolean
)
enum class TypeCheck {
TYPEOF,
@@ -21,13 +21,8 @@ import com.google.dart.compiler.backend.js.ast.metadata.*
import org.jetbrains.kotlin.js.inline.util.collectBreakContinueTargets
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.singletonOrEmptyList
class CoroutineBodyTransformer(
private val program: JsProgram,
private val context: CoroutineTransformationContext,
private val throwFunctionName: JsName?
) : RecursiveJsVisitor() {
class CoroutineBodyTransformer(private val program: JsProgram,private val context: CoroutineTransformationContext) : RecursiveJsVisitor() {
private val entryBlock = context.entryBlock
private val globalCatchBlock = context.globalCatchBlock
private var currentBlock = entryBlock
@@ -254,7 +249,7 @@ class CoroutineBodyTransformer(
}
if (catchNode != null) {
currentStatements += JsAstUtils.newVar(catchNode.parameter.name, JsNameRef(context.exceptionFieldName, JsLiteral.THIS))
currentStatements += JsAstUtils.newVar(catchNode.parameter.name, JsNameRef(context.metadata.exceptionName, JsLiteral.THIS))
catchNode.body.statements.forEach { it.accept(this) }
if (finallyNode == null) {
@@ -283,8 +278,8 @@ class CoroutineBodyTransformer(
// for simple `when` statement, we will need to support JsSwitch here
private fun generateFinallyExit() {
val finallyPathRef = JsNameRef(context.finallyPathFieldName, JsLiteral.THIS)
val stateRef = JsNameRef(context.stateFieldName, JsLiteral.THIS)
val finallyPathRef = JsNameRef(context.metadata.finallyPathName, JsLiteral.THIS)
val stateRef = JsNameRef(context.metadata.stateName, JsLiteral.THIS)
val nextState = JsInvocation(JsNameRef("shift", finallyPathRef))
currentStatements += JsAstUtils.assignment(stateRef, nextState).makeStmt()
currentStatements += jump()
@@ -310,11 +305,12 @@ class CoroutineBodyTransformer(
if (isInFinally) {
val returnBlock = CoroutineBlock()
jumpWithFinally(0, returnBlock)
val returnFieldRef = JsNameRef(context.returnValueFieldName, JsLiteral.THIS)
currentStatements += JsAstUtils.assignment(returnFieldRef, x.expression).makeStmt()
currentStatements += jump()
currentBlock = returnBlock
currentStatements += x.expression?.makeStmt().singletonOrEmptyList()
currentStatements += JsReturn()
currentStatements += JsReturn(returnFieldRef.deepCopy())
}
else {
currentStatements += x
@@ -322,16 +318,7 @@ class CoroutineBodyTransformer(
}
override fun visitThrow(x: JsThrow) {
if (throwFunctionName != null && tryStack.isEmpty()) {
val methodRef = JsNameRef(throwFunctionName, JsNameRef(context.controllerFieldName, JsLiteral.THIS))
val invocation = JsInvocation(methodRef, x.expression).apply {
source = x.source
}
currentStatements += JsReturn(invocation)
}
else {
currentStatements += x
}
currentStatements += x
}
private fun handleExpression(expression: JsExpression): JsExpression? {
@@ -361,15 +348,14 @@ class CoroutineBodyTransformer(
private fun handleSuspend(invocation: JsInvocation) {
val invokeExpression = if (invocation.isFakeSuspend) invocation.arguments.getOrNull(0) else invocation
val suspendObjectVar = context.suspendObjectVar
val statements = if (invokeExpression == null || suspendObjectVar == null) {
listOf(JsReturn(invokeExpression))
val statements = if (invokeExpression == null) {
listOf(JsReturn(context.metadata.suspendObjectRef.deepCopy()))
}
else {
val resultRef = JsNameRef(context.resultFieldName, JsLiteral.THIS).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }
val resultRef = JsNameRef(context.metadata.resultName, JsLiteral.THIS).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }
val invocationStatement = JsAstUtils.assignment(resultRef, invokeExpression).makeStmt()
val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), JsAstUtils.pureFqn(suspendObjectVar, null))
val suspendIfNeeded = JsIf(suspendCondition, JsReturn())
val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), context.metadata.suspendObjectRef.deepCopy())
val suspendIfNeeded = JsIf(suspendCondition, JsReturn(context.metadata.suspendObjectRef.deepCopy()))
listOf(invocationStatement, suspendIfNeeded, JsBreak())
}
currentStatements += statements
@@ -17,39 +17,23 @@
package org.jetbrains.kotlin.js.coroutine
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import com.google.dart.compiler.backend.js.ast.metadata.coroutineMetadata
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
import org.jetbrains.kotlin.js.inline.util.getInnerFunction
import org.jetbrains.kotlin.js.naming.NameSuggestion
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
class CoroutineFunctionTransformer(
private val program: JsProgram,
private val function: JsFunction,
private val coroutineType: ClassDescriptor,
private val controllerType: ClassDescriptor
) {
class CoroutineFunctionTransformer(private val program: JsProgram, private val function: JsFunction) {
private val innerFunction = function.getInnerFunction()
private val functionWithBody = innerFunction ?: function
private val body = functionWithBody.body
private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables()).toMutableSet()
private val nameSuggestion = NameSuggestion()
private val className = function.scope.parent.declareFreshName("Coroutine\$${function.name}")
fun transform(): List<JsStatement> {
val throwFunction = controllerType.findFunction("handleException")
val throwName = throwFunction?.let {
val throwId = nameSuggestion.suggest(it)!!.names.last()
function.scope.declareName(throwId)
}
val context = CoroutineTransformationContext(function.scope, function.suspendObjectRef != null)
val bodyTransformer = CoroutineBodyTransformer(program, context, throwName)
val context = CoroutineTransformationContext(function.scope, function)
val bodyTransformer = CoroutineBodyTransformer(program, context)
bodyTransformer.preProcess(body)
body.statements.forEach { it.accept(bodyTransformer) }
val coroutineBlocks = bodyTransformer.postProcess()
@@ -59,9 +43,8 @@ class CoroutineFunctionTransformer(
coroutineBlocks.forEach { it.jsBlock.replaceLocalVariables(function.scope, context, localVariables) }
val additionalStatements = mutableListOf<JsStatement>()
val resumeName = generateDoResume(coroutineBlocks, context, additionalStatements, throwName)
generateContinuationConstructor(context, additionalStatements, bodyTransformer.hasFinallyBlocks, globalCatchBlockIndex)
generateContinuationMethods(context, resumeName, additionalStatements)
generateDoResume(coroutineBlocks, context, additionalStatements)
generateContinuationConstructor(context, additionalStatements, globalCatchBlockIndex)
generateCoroutineInstantiation()
@@ -71,7 +54,6 @@ class CoroutineFunctionTransformer(
private fun generateContinuationConstructor(
context: CoroutineTransformationContext,
statements: MutableList<JsStatement>,
hasFinallyBlocks: Boolean,
globalCatchBlockIndex: Int
) {
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
@@ -81,29 +63,46 @@ class CoroutineFunctionTransformer(
constructor.parameters += innerFunction.parameters.map { JsParameter(it.name) }
}
val controllerName = function.scope.declareFreshName("controller")
constructor.parameters += JsParameter(controllerName)
val controllerName = if (context.metadata.hasController) {
function.scope.declareFreshName("controller").apply { constructor.parameters += JsParameter(this) }
}
else {
null
}
val interceptorName = function.scope.declareFreshName("interceptor")
constructor.parameters += JsParameter(interceptorName)
val parameterNames = (function.parameters.map { it.name } + innerFunction?.parameters?.map { it.name }.orEmpty()).toSet()
constructor.body.statements.run {
assignToField(context.stateFieldName, program.getNumberLiteral(0))
assignToField(context.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex))
if (hasFinallyBlocks) {
assignToField(context.finallyPathFieldName, JsLiteral.NULL)
val baseClass = context.metadata.baseClassRef.deepCopy()
this += JsInvocation(Namer.getFunctionCallRef(baseClass), JsLiteral.THIS, interceptorName.makeRef()).makeStmt()
if (controllerName != null) {
assignToField(context.controllerFieldName, controllerName.makeRef())
}
assignToField(context.controllerFieldName, controllerName.makeRef())
assignToField(context.metadata.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex))
for (localVariable in localVariables) {
val value = if (localVariable !in parameterNames) JsLiteral.NULL else localVariable.makeRef()
assignToField(function.scope.getFieldName(localVariable), value)
}
}
statements.addAll(0, listOf(constructor.makeStmt(), generateCoroutineMetadata(constructor.name)))
statements.addAll(0, listOf(constructor.makeStmt(), generateCoroutineMetadata(constructor.name)) +
generateCoroutinePrototype(constructor.name))
}
private fun generateCoroutinePrototype(constructorName: JsName): List<JsStatement> {
val prototype = JsAstUtils.prototypeOf(JsNameRef(constructorName))
val baseClass = Namer.createObjectWithPrototypeFrom(function.coroutineMetadata!!.baseClassRef.deepCopy())
val assignPrototype = JsAstUtils.assignment(prototype, baseClass)
val assignConstructor = JsAstUtils.assignment(JsNameRef("constructor", prototype.deepCopy()), JsNameRef(constructorName))
return listOf(assignPrototype.makeStmt(), assignConstructor.makeStmt())
}
private fun generateCoroutineMetadata(constructorName: JsName): JsStatement {
val interfaceRef = function.continuationInterfaceRef!!.deepCopy()
val baseClassRefRef = function.coroutineMetadata!!.baseClassRef.deepCopy()
val metadataObject = JsObjectLiteral(true)
metadataObject.propertyInitializers += JsPropertyInitializer(
@@ -111,89 +110,31 @@ class CoroutineFunctionTransformer(
metadataObject.propertyInitializers += JsPropertyInitializer(
JsNameRef("classIndex"), JsInvocation(JsNameRef("newClassIndex", Namer.KOTLIN_NAME)))
metadataObject.propertyInitializers += JsPropertyInitializer(JsNameRef("simpleName"), JsLiteral.NULL)
metadataObject.propertyInitializers += JsPropertyInitializer(JsNameRef("baseClasses"), JsArrayLiteral(listOf(interfaceRef)))
metadataObject.propertyInitializers += JsPropertyInitializer(JsNameRef("baseClasses"), JsArrayLiteral(listOf(baseClassRefRef)))
return JsAstUtils.assignment(JsNameRef(Namer.METADATA, constructorName.makeRef()), metadataObject).makeStmt()
}
private fun generateContinuationMethods(
context: CoroutineTransformationContext,
doResumeName: JsName,
statements: MutableList<JsStatement>
) {
generateResumeFunction(context, doResumeName, statements, "resume", "data", listOf())
generateResumeFunction(context, doResumeName, statements, "resumeWithException", "exception",
listOf(Namer.getUndefinedExpression()))
}
private fun generateResumeFunction(
context: CoroutineTransformationContext,
doResumeName: JsName,
statements: MutableList<JsStatement>,
name: String,
parameterName: String,
additionalArgs: List<JsExpression>
) {
val resumeDescriptor = coroutineType.findFunction(name)!!
val resumeId = nameSuggestion.suggest(resumeDescriptor)!!.names.last()
val resumeName = function.scope.declareName(resumeId)
val resumeFunction = JsFunction(function.scope.parent, JsBlock(), resumeDescriptor.toString())
val resumeParameter = resumeFunction.scope.declareFreshName(parameterName)
resumeFunction.parameters += JsParameter(resumeParameter)
resumeFunction.body.statements.apply {
val interceptResumeRef = function.interceptResumeRef
val invocation = JsInvocation(JsNameRef(doResumeName, JsLiteral.THIS), additionalArgs + resumeParameter.makeRef())
if (interceptResumeRef == null) {
this += JsReturn(invocation)
}
else {
val interceptLambda = JsFunction(resumeFunction.scope, JsBlock(JsReturn(invocation)), "")
val interceptParameter = JsInvocation(JsNameRef("bind", interceptLambda), JsLiteral.THIS)
val interceptInvocation = JsInvocation(interceptResumeRef.deepCopy(), interceptParameter)
this += JsReturn(interceptInvocation)
}
}
resumeFunction.body.replaceSpecialReferences(context)
statements.apply {
assignToPrototype(resumeName, resumeFunction)
}
}
private fun generateDoResume(
coroutineBlocks: List<CoroutineBlock>,
context: CoroutineTransformationContext,
statements: MutableList<JsStatement>,
throwName: JsName?
): JsName {
statements: MutableList<JsStatement>
) {
val resumeFunction = JsFunction(function.scope.parent, JsBlock(), "resume function")
val resumeParameter = resumeFunction.scope.declareFreshName("data")
val resumeException = resumeFunction.scope.declareFreshName("exception")
resumeFunction.parameters += listOf(JsParameter(resumeParameter), JsParameter(resumeException))
val coroutineBody = generateCoroutineBody(context, coroutineBlocks, throwName, resumeException)
val coroutineBody = generateCoroutineBody(context, coroutineBlocks)
functionWithBody.body.statements.clear()
resumeFunction.body.statements.apply {
assignToField(context.resultFieldName, resumeParameter.makeRef())
if (context.suspendObjectVar != null) {
add(JsAstUtils.newVar(context.suspendObjectVar!!, function.suspendObjectRef!!.deepCopy()).apply {
synthetic = true
})
}
this += coroutineBody
}
val resumeName = function.scope.parent.declareFreshName("doResume")
val resumeName = function.coroutineMetadata!!.doResumeName
statements.apply {
assignToPrototype(resumeName, resumeFunction)
}
FunctionPostProcessor(resumeFunction).apply()
return resumeName
}
private fun generateCoroutineInstantiation() {
@@ -203,36 +144,36 @@ class CoroutineFunctionTransformer(
instantiation.arguments += innerFunction.parameters.map { it.name.makeRef() }
}
instantiation.arguments += JsLiteral.THIS
if (function.coroutineMetadata!!.hasController) {
instantiation.arguments += JsLiteral.THIS
}
val interceptorParamName = functionWithBody.scope.declareFreshName("interceptor")
functionWithBody.parameters += JsParameter(interceptorParamName)
instantiation.arguments += interceptorParamName.makeRef()
functionWithBody.body.statements += JsReturn(instantiation)
}
private fun generateCoroutineBody(
context: CoroutineTransformationContext,
blocks: List<CoroutineBlock>,
throwName: JsName?,
exceptionName: JsName
blocks: List<CoroutineBlock>
): List<JsStatement> {
val indexOfGlobalCatch = blocks.indexOf(context.globalCatchBlock)
val stateRef = JsNameRef(context.stateFieldName, JsLiteral.THIS)
val stateRef = JsNameRef(context.metadata.stateName, JsLiteral.THIS)
val isFromGlobalCatch = JsAstUtils.equality(stateRef, program.getNumberLiteral(indexOfGlobalCatch))
val catch = JsCatch(functionWithBody.scope, "e")
val continueWithException = JsBlock(
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.exceptionStateName, JsLiteral.THIS)).makeStmt(),
JsAstUtils.assignment(JsNameRef(context.exceptionFieldName, JsLiteral.THIS), catch.parameter.name.makeRef()).makeStmt()
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.metadata.exceptionStateName, JsLiteral.THIS)).makeStmt(),
JsAstUtils.assignment(JsNameRef(context.metadata.exceptionName, JsLiteral.THIS),
catch.parameter.name.makeRef()).makeStmt()
)
catch.body = JsBlock(JsIf(isFromGlobalCatch, JsThrow(catch.parameter.name.makeRef()), continueWithException))
val throwResultRef = JsNameRef(context.exceptionFieldName, JsLiteral.THIS)
if (throwName != null) {
val throwMethodRef = JsNameRef(throwName, JsNameRef(context.controllerFieldName, JsLiteral.THIS))
context.globalCatchBlock.statements += JsReturn(JsInvocation(throwMethodRef.deepCopy(), throwResultRef))
}
else {
context.globalCatchBlock.statements += JsThrow(throwResultRef)
}
val throwResultRef = JsNameRef(context.metadata.exceptionName, JsLiteral.THIS)
context.globalCatchBlock.statements += JsThrow(throwResultRef)
val cases = blocks.withIndex().map { (index, block) ->
JsCase().apply {
@@ -243,15 +184,7 @@ class CoroutineFunctionTransformer(
val switchStatement = JsSwitch(stateRef.deepCopy(), cases)
val loop = JsDoWhile(JsLiteral.TRUE, JsTry(JsBlock(switchStatement), catch, null))
val testExceptionPassed = JsAstUtils.notOptimized(
JsAstUtils.typeOfIs(exceptionName.makeRef(), program.getStringLiteral("undefined")))
val stateToException = JsAstUtils.assignment(
JsNameRef(context.stateFieldName, JsLiteral.THIS),
JsNameRef(context.exceptionStateName, JsLiteral.THIS))
val exceptionToResult = JsAstUtils.assignment(JsNameRef(context.exceptionFieldName, JsLiteral.THIS), exceptionName.makeRef())
val throwExceptionIfNeeded = JsIf(testExceptionPassed, JsBlock(stateToException.makeStmt(), exceptionToResult.makeStmt()))
return listOf(throwExceptionIfNeeded, loop)
return listOf(loop)
}
private fun JsBlock.collectAdditionalLocalVariables() {
@@ -263,13 +196,6 @@ class CoroutineFunctionTransformer(
})
}
private fun ClassDescriptor.findFunction(name: String): FunctionDescriptor? {
val functions = unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
.filter { it.name.asString() == name }
return functions.mapNotNull { it as? FunctionDescriptor }.firstOrNull { it.kind.isReal }
}
private fun MutableList<JsStatement>.assignToField(fieldName: JsName, value: JsExpression) {
this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).makeStmt()
}
@@ -112,7 +112,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
override fun endVisit(x: JsDebugger, ctx: JsContext<in JsStatement>) {
val target = x.targetBlock
if (target != null) {
val lhs = JsNameRef(context.stateFieldName, JsLiteral.THIS)
val lhs = JsNameRef(context.metadata.stateName, JsLiteral.THIS)
val rhs = program.getNumberLiteral(blockIndexes[target]!!)
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
targetBlock = true
@@ -121,7 +121,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
val exceptionTarget = x.targetExceptionBlock
if (exceptionTarget != null) {
val lhs = JsNameRef(context.exceptionStateName, JsLiteral.THIS)
val lhs = JsNameRef(context.metadata.exceptionStateName, JsLiteral.THIS)
val rhs = program.getNumberLiteral(blockIndexes[exceptionTarget]!!)
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
targetExceptionBlock = true
@@ -131,7 +131,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
val finallyPath = x.finallyPath
if (finallyPath != null) {
if (finallyPath.isNotEmpty()) {
val lhs = JsNameRef(context.finallyPathFieldName, JsLiteral.THIS)
val lhs = JsNameRef(context.metadata.finallyPathName, JsLiteral.THIS)
val rhs = JsArrayLiteral(finallyPath.map { program.getNumberLiteral(blockIndexes[it]!!) })
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
this.finallyPath = true
@@ -209,7 +209,7 @@ fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) {
}
x.coroutineResult -> {
ctx.replaceMe(JsNameRef(context.resultFieldName, x.qualifier).apply {
ctx.replaceMe(JsNameRef(context.metadata.resultName, x.qualifier).apply {
sideEffects = SideEffectKind.DEPENDS_ON_STATE
})
}
@@ -16,16 +16,14 @@
package org.jetbrains.kotlin.js.coroutine
import com.google.dart.compiler.backend.js.ast.JsFunction
import com.google.dart.compiler.backend.js.ast.JsScope
import com.google.dart.compiler.backend.js.ast.metadata.coroutineMetadata
class CoroutineTransformationContext(private val scope: JsScope, private val stackUnwinding: Boolean) {
class CoroutineTransformationContext(private val scope: JsScope, function: JsFunction) {
val entryBlock = CoroutineBlock()
val globalCatchBlock = CoroutineBlock()
val resultFieldName by lazy { scope.declareFreshName("\$result") }
val exceptionFieldName by lazy { scope.declareFreshName("\$exception") }
val stateFieldName by lazy { scope.declareFreshName("\$state") }
val metadata = function.coroutineMetadata!!
val controllerFieldName by lazy { scope.declareFreshName("\$controller") }
val exceptionStateName by lazy { scope.declareFreshName("\$exceptionState") }
val finallyPathFieldName by lazy { scope.declareFreshName("\$finallyPath") }
val suspendObjectVar by lazy { if (stackUnwinding) scope.declareFreshName("\$suspendObject") else null }
val returnValueFieldName by lazy { scope.declareFreshName("\$returnValue") }
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.js.coroutine
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.controllerType
import com.google.dart.compiler.backend.js.ast.metadata.coroutineType
class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContextImpl() {
@@ -32,7 +31,7 @@ class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContex
override fun endVisit(x: JsFunction, ctx: JsContext<in JsStatement>) {
val coroutineType = x.coroutineType
if (coroutineType != null) {
additionalStatements += CoroutineFunctionTransformer(program, x, coroutineType, x.controllerType!!).transform()
additionalStatements += CoroutineFunctionTransformer(program, x).transform()
}
}
}
@@ -33,9 +33,7 @@
package org.jetbrains.kotlin.js.inline.clean
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.continuationInterfaceRef
import com.google.dart.compiler.backend.js.ast.metadata.interceptResumeRef
import com.google.dart.compiler.backend.js.ast.metadata.suspendObjectRef
import com.google.dart.compiler.backend.js.ast.metadata.coroutineMetadata
import org.jetbrains.kotlin.js.inline.util.collectReferencedTemporaryNames
fun JsNode.resolveTemporaryNames() {
@@ -59,9 +57,10 @@ fun JsNode.resolveTemporaryNames() {
}
override fun visitFunction(x: JsFunction) {
x.continuationInterfaceRef?.let { accept(it) }
x.interceptResumeRef?.let { accept(it) }
x.suspendObjectRef?.let { accept(it) }
x.coroutineMetadata?.apply {
accept(suspendObjectRef)
accept(baseClassRef)
}
super.visitFunction(x)
}
})
@@ -86,7 +85,7 @@ private fun Map<JsScope, Set<JsScope>>.resolveNames(knownNames: Set<JsName>): Ma
return replacements
}
private class ScopeCollector() : RecursiveJsVisitor() {
private class ScopeCollector : RecursiveJsVisitor() {
val scopeTree = mutableMapOf<JsScope, MutableSet<JsScope>>()
override fun visitCatch(x: JsCatch) {
+162
View File
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.coroutines
/**
* A strategy to intercept resumptions inside coroutine.
* Interceptor may shift resumption into another execution frame by scheduling asynchronous execution
* in this or another thread.
*/
@SinceKotlin("1.1")
public interface ResumeInterceptor {
/**
* Intercepts [Continuation.resume] invocation.
* This function must either return `false` or return `true` and invoke `continuation.resume(data)` asynchronously.
*/
public fun <P> interceptResume(data: P, continuation: Continuation<P>): Boolean = false
/**
* Intercepts [Continuation.resumeWithException] invocation.
* This function must either return `false` or return `true` and invoke `continuation.resumeWithException(exception)` asynchronously.
*/
public fun interceptResumeWithException(exception: Throwable, continuation: Continuation<*>): Boolean = false
}
/**
* Creates coroutine with receiver type [R] and result type [T].
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
* The result of the coroutine's execution is provided via invocation of [resultHandler].
*/
@SinceKotlin("1.1")
public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
): Continuation<Unit> = this.asDynamic().call(receiver, withInterceptor(resultHandler, resumeInterceptor))
/**
* Starts coroutine with receiver type [R] and result type [T].
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
* The result of the coroutine's execution is provided via invocation of [resultHandler].
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <R, T> (suspend R.() -> T).startCoroutine(
receiver: R,
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
) {
createCoroutine(receiver, resultHandler, resumeInterceptor).resume(Unit)
}
/**
* Creates coroutine without receiver and with result type [T].
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
* The result of the coroutine's execution is provided via invocation of [resultHandler].
* An optional [resumeInterceptor] may be specified to intercept resumes at suspension points inside the coroutine.
*/
@SinceKotlin("1.1")
public fun <T> (suspend () -> T).createCoroutine(
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
): Continuation<Unit> = this.asDynamic()(withInterceptor(resultHandler, resumeInterceptor))
/**
* Starts coroutine without receiver and with result type [T].
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
* The result of the coroutine's execution is provided via invocation of [resultHandler].
* An optional [resumeInterceptor] may be specified to intercept resumes at suspension points inside the coroutine.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).startCoroutine(
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
) {
createCoroutine(resultHandler, resumeInterceptor).resume(Unit)
}
// ------- internal stuff -------
private fun <T> withInterceptor(resultHandler: Continuation<T>, resumeInterceptor: ResumeInterceptor?): Continuation<T> {
val finalResumeInterceptor = resumeInterceptor ?: object : ResumeInterceptor {
override fun <P> interceptResume(data: P, continuation: Continuation<P>) = false
override fun interceptResumeWithException(exception: Throwable, continuation: Continuation<*>) = false
}
return object : Continuation<T> by resultHandler, ResumeInterceptor by finalResumeInterceptor {}
}
@JsName("CoroutineImpl")
internal abstract class CoroutineImpl(private val resultContinuation: Continuation<Any?>) : Continuation<Any?> {
protected var state = 0
protected var exceptionState = 0
protected var result: Any? = null
protected var exception: Throwable? = null
protected var finallyPath: Array<Int>? = null
private val resumeInterceptor = resultContinuation as? ResumeInterceptor
override fun resume(data: Any?) {
if (resumeInterceptor != null && (state and INTERCEPTING) == 0) {
state = state or INTERCEPTING
if (resumeInterceptor.interceptResume(data, this)) {
state = state and INTERCEPTING.inv()
return
}
}
state = state and INTERCEPTING.inv()
this.result = data
try {
val result = doResume()
if (result != SUSPENDED) {
resultContinuation.resume(result)
}
}
catch (e: Throwable) {
resultContinuation.resumeWithException(e)
}
}
override fun resumeWithException(exception: Throwable) {
if (resumeInterceptor != null && (state and INTERCEPTING) == 0) {
state = state or INTERCEPTING
if (resumeInterceptor.interceptResumeWithException(exception, this)) {
state = state and INTERCEPTING.inv()
return
}
}
state = exceptionState
this.exception = exception
try {
val result = doResume()
if (result != SUSPENDED) {
resultContinuation.resume(result)
}
}
catch (e: Throwable) {
resultContinuation.resumeWithException(e)
}
}
protected abstract fun doResume(): Any?
}
private const val INTERCEPTING = 1 shl 31
@@ -5505,13 +5505,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simple.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("simpleException.kt")
@@ -174,8 +174,8 @@ public final class Namer {
}
@NotNull
public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) {
return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, JsAstUtils.prototypeOf(referenceToClass));
public static JsInvocation createObjectWithPrototypeFrom(@NotNull JsExpression referenceToClass) {
return new JsInvocation(JS_OBJECT_CREATE_FUNCTION.deepCopy(), JsAstUtils.prototypeOf(referenceToClass));
}
@NotNull
@@ -21,6 +21,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.kotlin.js.translate.reference.*;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
@@ -128,11 +127,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
}
JsReturn jsReturn;
JsExpression handleResultExpression = TranslationUtils.tryTranslateHandleResult(context, jetReturnExpression, returned);
if (handleResultExpression != null) {
jsReturn = new JsReturn(handleResultExpression);
}
else if (returned == null) {
if (returned == null) {
jsReturn = new JsReturn(null);
}
else {
@@ -451,13 +446,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitLambdaExpression(@NotNull KtLambdaExpression expression, @NotNull TranslationContext context) {
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral(), null, null);
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral(), null);
}
@Override
@NotNull
public JsNode visitNamedFunction(@NotNull KtNamedFunction expression, @NotNull TranslationContext context) {
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression, null, null);
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression, null);
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
JsNameRef nameRef = (JsNameRef) ReferenceTranslator.translateAsValueReference(descriptor, context);
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.js.translate.expression
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda
@@ -32,25 +32,24 @@ import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.KotlinType
class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) {
companion object {
private val SUSPEND_FQ_NAME = "kotlin.coroutines.Suspend"
private val SUSPEND_FQ_NAME = FqName("kotlin.coroutines.SUSPENDED")
}
fun translate(
declaration: KtDeclarationWithBody,
continuationType: ClassDescriptor? = null,
controllerType: ClassDescriptor? = null
continuationType: KotlinType? = null
): JsExpression {
val invokingContext = context()
val descriptor = getFunctionDescriptor(invokingContext.bindingContext(), declaration)
@@ -88,41 +87,50 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
invokingContext.getInnerNameForDescriptor(descriptor)
}
val suspendObjectRef = if (descriptor.isCoroutineLambda && KotlinBuiltIns.isUnit(descriptor.returnType!!)) {
val suspendObjectDescriptor = context().currentModule.builtIns.getBuiltInClassByFqName(FqName(SUSPEND_FQ_NAME))
ReferenceTranslator.translateAsValueReference(suspendObjectDescriptor, context())
}
else {
null
}
if (tracker.hasCapturedExceptContaining()) {
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
lambdaCreator.name = invokingContext.getInnerNameForDescriptor(descriptor)
lambdaCreator.isLocal = true
lambdaCreator.coroutineType = continuationType
lambdaCreator.controllerType = controllerType
if (!isRecursive || descriptor.isCoroutineLambda) {
lambda.name = null
}
lambdaCreator.name.staticRef = lambdaCreator
lambdaCreator.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference()
lambdaCreator.suspendObjectRef = suspendObjectRef
lambdaCreator.fillCoroutineMetadata(invokingContext, continuationType)
return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext),
invokingContext)
}
lambda.isLocal = true
lambda.coroutineType = continuationType
lambda.controllerType = controllerType
invokingContext.addDeclarationStatement(lambda.makeStmt())
lambda.fillCoroutineMetadata(invokingContext, continuationType)
lambda.name.staticRef = lambda
lambda.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference()
lambda.suspendObjectRef = suspendObjectRef
return getReferenceToLambda(invokingContext, descriptor, lambda.name)
}
fun JsFunction.fillCoroutineMetadata(context: TranslationContext, continuationType: KotlinType?) {
if (continuationType == null) return
val suspendObjectDescriptor = context().currentModule.getPackage(SUSPEND_FQ_NAME.parent())
.memberScope.getContributedDescriptors(DescriptorKindFilter.CALLABLES)
.first { it is PropertyDescriptor && it.name == SUSPEND_FQ_NAME.shortName() }
val coroutineBaseClassRef = ReferenceTranslator.translateAsTypeReference(TranslationUtils.getCoroutineBaseClass(context), context)
coroutineMetadata = CoroutineMetadata(
doResumeName = context.getNameForDescriptor(TranslationUtils.getCoroutineDoResumeFunction(context)),
suspendObjectRef = ReferenceTranslator.translateAsValueReference(suspendObjectDescriptor, context()),
baseClassRef = coroutineBaseClassRef,
stateName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "state")),
exceptionStateName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "exceptionState")),
finallyPathName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "finallyPath")),
resultName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "result")),
exceptionName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "exception")),
hasController = continuationType.isBuiltinExtensionFunctionalType
)
coroutineType = continuationType
}
fun ValueParameterDescriptorImpl.WithDestructuringDeclaration.translate(context: TranslationContext): JsVars {
val destructuringDeclaration =
(DescriptorToSourceUtils.descriptorToDeclaration(this) as? KtParameter)?.destructuringDeclaration
@@ -133,12 +141,6 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
}
}
private fun TranslationContext.getContinuationInterfaceReference(): JsExpression {
val coroutineClassId = ClassId.topLevel(KotlinBuiltIns.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("Continuation")))
val classDescriptor = currentModule.findClassAcrossModuleDependencies(coroutineClassId)!!
return ReferenceTranslator.translateAsTypeReference(classDescriptor, this)
}
private fun CallableMemberDescriptor.wrapContextForCoroutineIfNecessary(context: TranslationContext): TranslationContext {
return if (isCoroutineLambda) context.innerContextWithDescriptorsAliased(mapOf(this to JsLiteral.THIS)) else context
}
@@ -254,7 +256,7 @@ private fun moveCapturedLocalInside(capturingFunction: JsFunction, capturedName:
val alias = JsInvocation(localFunAlias.qualifier, aliasCallArguments)
declareAliasInsideFunction(capturingFunction, capturedName, alias)
val capturedParameters = freshNames.map {JsParameter(it)}
val capturedParameters = freshNames.map(::JsParameter)
return CapturedArgsParams(capturedArgs, capturedParameters)
}
@@ -20,7 +20,8 @@ import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.coroutines.isSuspendLambda
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -31,10 +32,8 @@ import org.jetbrains.kotlin.js.translate.expression.LiteralFunctionTranslator
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.ValueArgument
@@ -206,19 +205,21 @@ class CallArgumentTranslator private constructor(
val parenthesizedArgumentExpression = valueArguments[0].getArgumentExpression()!!
val argumentExpression = KtPsiUtil.deparenthesize(parenthesizedArgumentExpression)
result += if (/*parameterDescriptor.isCoroutine=*/false && argumentExpression is KtLambdaExpression) {
val continuationType = parameterDescriptor.type.arguments.last().type
val continuationDescriptor = continuationType.constructor.declarationDescriptor as ClassDescriptor
val controllerType = parameterDescriptor.type.arguments[0].type
val controllerDescriptor = controllerType.constructor.declarationDescriptor as ClassDescriptor
LiteralFunctionTranslator(context).translate(
argumentExpression.functionLiteral, continuationDescriptor, controllerDescriptor)
val lambdaDescriptor = argumentExpression?.let { context.extractLambda(it) }
if (lambdaDescriptor?.isSuspendLambda == true) {
val lambdaExpression = (argumentExpression as KtLambdaExpression).functionLiteral
result += LiteralFunctionTranslator(context).translate(lambdaExpression, parameterDescriptor.type)
}
else {
Translation.translateAsExpression(parenthesizedArgumentExpression, context)
result += Translation.translateAsExpression(parenthesizedArgumentExpression, context)
}
}
private fun TranslationContext.extractLambda(expression: KtExpression): CallableDescriptor? {
val lambdaExpression = expression as? KtLambdaExpression ?: return null
return BindingUtils.getFunctionDescriptor(bindingContext(), lambdaExpression.functionLiteral)
}
private fun translateVarargArgument(arguments: List<ValueArgument>, result: MutableList<JsExpression>,
context: TranslationContext, shouldWrapVarargInArray: Boolean) {
if (arguments.isEmpty()) {
@@ -118,16 +118,8 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
KotlinType returnType = descriptor.getReturnType();
assert returnType != null;
TranslationContext handleResultContext = context().innerBlock(jsBlock);
JsExpression handleResultExpr = TranslationUtils.tryTranslateHandleResult(handleResultContext, declaration, jetBodyExpression);
if (handleResultExpr != null) {
jsBlock.getStatements().add(new JsReturn(handleResultExpr));
}
else {
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements());
}
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements());
return jsBlock;
}
@@ -18,34 +18,34 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.serialization.deserialization.FindClassInModuleKt;
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
@@ -130,7 +130,7 @@ public final class TranslationUtils {
}
@NotNull
public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
private static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
return nullCheck(expressionToCheck, true);
}
@@ -312,48 +312,6 @@ public final class TranslationUtils {
return false;
}
@Nullable
public static JsExpression tryTranslateHandleResult(
@NotNull TranslationContext context,
@NotNull KtExpression expression,
@Nullable KtExpression returnExpression
) {
ResolvedCall<? extends FunctionDescriptor> returnCall = context.bindingContext().get(
BindingContext.RETURN_HANDLE_RESULT_RESOLVED_CALL, expression);
if (returnCall == null) return null;
Map<KtExpression, JsExpression> aliases = new HashMap<KtExpression, JsExpression>();
List<ResolvedValueArgument> arguments = returnCall.getValueArgumentsByIndex();
assert arguments != null : "Arguments should be defined here: " + PsiUtilsKt.getTextWithLocation(expression);
KotlinType returnType = returnCall.getResultingDescriptor().getValueParameters().get(0).getType();
ValueArgument returnValueArgument = arguments.get(0).getArguments().get(0);
if (returnExpression != null) {
aliases.put(returnValueArgument.getArgumentExpression(), Translation.translateAsExpression(returnExpression, context));
}
else if (KotlinBuiltIns.isUnit(returnType)) {
aliases.put(returnValueArgument.getArgumentExpression(), JsLiteral.NULL);
}
ValueArgument continuationArgument = arguments.get(1).getArguments().get(0);
aliases.put(continuationArgument.getArgumentExpression(), JsLiteral.THIS);
TranslationContext returnContext = context.innerContextWithAliasesForExpressions(aliases);
ImplicitReceiver receiver = (ImplicitReceiver) returnCall.getDispatchReceiver();
assert receiver != null;
FunctionDescriptor lambdaDescriptor = (FunctionDescriptor) receiver.getDeclarationDescriptor();
ReceiverParameterDescriptor receiverParameter = lambdaDescriptor.getExtensionReceiverParameter();
assert receiverParameter != null;
JsExpression jsReceiver = returnContext.getDispatchReceiver(receiverParameter);
JsInvocation handleResultInvocation = (JsInvocation) CallTranslator.translate(returnContext, returnCall, jsReceiver);
MetadataProperties.setHandleResult(handleResultInvocation, true);
return handleResultInvocation;
}
@NotNull
public static JsExpression translateContinuationArgument(@NotNull TranslationContext context, @NotNull ResolvedCall<?> resolvedCall) {
CallableDescriptor continuationDescriptor =
@@ -375,4 +333,28 @@ public final class TranslationUtils {
}
return result;
}
@NotNull
public static ClassDescriptor getCoroutineBaseClass(@NotNull TranslationContext context) {
FqName className = KotlinBuiltIns.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineImpl"));
ClassDescriptor descriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(
context.getCurrentModule(), ClassId.topLevel(className));
assert descriptor != null;
return descriptor;
}
@NotNull
public static PropertyDescriptor getCoroutineProperty(@NotNull TranslationContext context, @NotNull String name) {
return getCoroutineBaseClass(context).getUnsubstitutedMemberScope()
.getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_DESERIALIZATION)
.iterator().next();
}
@NotNull
public static FunctionDescriptor getCoroutineDoResumeFunction(@NotNull TranslationContext context) {
return getCoroutineBaseClass(context).getUnsubstitutedMemberScope()
.getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_DESERIALIZATION)
.iterator().next();
}
}