JS: implement new coroutine convention
This commit is contained in:
committed by
Stanislav Erokhin
parent
9fd1ac72a9
commit
e2d969d8b0
@@ -17,10 +17,10 @@ class Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: () -> String): String {
|
fun builder(c: suspend Controller.() -> String): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c.startCoroutine(handleResult {
|
c.startCoroutine(controller, handleResultContinuation {
|
||||||
controller.result += "return($value);"
|
controller.result += "return($it);"
|
||||||
})
|
})
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODULE: main(controller)
|
// MODULE: main(controller, support)
|
||||||
// FILE: main.kt
|
// FILE: main.kt
|
||||||
import lib.*
|
import lib.*
|
||||||
import kotlin.coroutines.*
|
import kotlin.coroutines.*
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// IGNORE_BACKEND: JS
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// WITH_COROUTINES
|
// WITH_COROUTINES
|
||||||
import kotlin.coroutines.*
|
import kotlin.coroutines.*
|
||||||
|
|
||||||
|
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
SUSPENDED
|
SUSPENDED
|
||||||
|
|||||||
@@ -602,6 +602,7 @@ public class KotlinTestUtils {
|
|||||||
|
|
||||||
List<F> testFiles = Lists.newArrayList();
|
List<F> testFiles = Lists.newArrayList();
|
||||||
Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText);
|
Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText);
|
||||||
|
boolean hasModules = false;
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
// One file
|
// One file
|
||||||
testFiles.add(factory.createFile(null, testFileName, expectedText, directives));
|
testFiles.add(factory.createFile(null, testFileName, expectedText, directives));
|
||||||
@@ -614,6 +615,7 @@ public class KotlinTestUtils {
|
|||||||
String moduleName = matcher.group(1);
|
String moduleName = matcher.group(1);
|
||||||
String moduleDependencies = matcher.group(2);
|
String moduleDependencies = matcher.group(2);
|
||||||
if (moduleName != null) {
|
if (moduleName != null) {
|
||||||
|
hasModules = true;
|
||||||
module = factory.createModule(moduleName, parseDependencies(moduleDependencies));
|
module = factory.createModule(moduleName, parseDependencies(moduleDependencies));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,7 +644,8 @@ public class KotlinTestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isDirectiveDefined(expectedText, "WITH_COROUTINES")) {
|
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",
|
"CoroutineUtil.kt",
|
||||||
"import kotlin.coroutines.*\n" +
|
"import kotlin.coroutines.*\n" +
|
||||||
"fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {\n" +
|
"fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {\n" +
|
||||||
|
|||||||
+16
-16
@@ -20,9 +20,9 @@ package com.google.dart.compiler.backend.js.ast.metadata
|
|||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
var JsName.staticRef: JsNode? by MetadataProperty(default = null)
|
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 HasMetadata.sideEffects: SideEffectKind by MetadataProperty(default = SideEffectKind.AFFECTS_STATE)
|
||||||
|
|
||||||
var JsFunction.coroutineType: ClassDescriptor? by MetadataProperty(default = null)
|
var JsFunction.coroutineType: KotlinType? by MetadataProperty(default = null)
|
||||||
|
|
||||||
var JsFunction.controllerType: ClassDescriptor? by MetadataProperty(default = null)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Denotes a suspension call-site that is to be processed by coroutine transformer.
|
* 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)
|
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
|
* Denotes a reference to coroutine's `result` field that contains result of
|
||||||
* last suspended invocation.
|
* last suspended invocation.
|
||||||
@@ -99,17 +91,25 @@ var JsInvocation.isHandleResult: Boolean by MetadataProperty(default = false)
|
|||||||
var JsNameRef.coroutineResult 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 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 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 {
|
enum class TypeCheck {
|
||||||
TYPEOF,
|
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.inline.util.collectBreakContinueTargets
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
|
||||||
|
|
||||||
class CoroutineBodyTransformer(
|
class CoroutineBodyTransformer(private val program: JsProgram,private val context: CoroutineTransformationContext) : RecursiveJsVisitor() {
|
||||||
private val program: JsProgram,
|
|
||||||
private val context: CoroutineTransformationContext,
|
|
||||||
private val throwFunctionName: JsName?
|
|
||||||
) : RecursiveJsVisitor() {
|
|
||||||
private val entryBlock = context.entryBlock
|
private val entryBlock = context.entryBlock
|
||||||
private val globalCatchBlock = context.globalCatchBlock
|
private val globalCatchBlock = context.globalCatchBlock
|
||||||
private var currentBlock = entryBlock
|
private var currentBlock = entryBlock
|
||||||
@@ -254,7 +249,7 @@ class CoroutineBodyTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (catchNode != null) {
|
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) }
|
catchNode.body.statements.forEach { it.accept(this) }
|
||||||
|
|
||||||
if (finallyNode == null) {
|
if (finallyNode == null) {
|
||||||
@@ -283,8 +278,8 @@ class CoroutineBodyTransformer(
|
|||||||
// for simple `when` statement, we will need to support JsSwitch here
|
// for simple `when` statement, we will need to support JsSwitch here
|
||||||
|
|
||||||
private fun generateFinallyExit() {
|
private fun generateFinallyExit() {
|
||||||
val finallyPathRef = JsNameRef(context.finallyPathFieldName, JsLiteral.THIS)
|
val finallyPathRef = JsNameRef(context.metadata.finallyPathName, JsLiteral.THIS)
|
||||||
val stateRef = JsNameRef(context.stateFieldName, JsLiteral.THIS)
|
val stateRef = JsNameRef(context.metadata.stateName, JsLiteral.THIS)
|
||||||
val nextState = JsInvocation(JsNameRef("shift", finallyPathRef))
|
val nextState = JsInvocation(JsNameRef("shift", finallyPathRef))
|
||||||
currentStatements += JsAstUtils.assignment(stateRef, nextState).makeStmt()
|
currentStatements += JsAstUtils.assignment(stateRef, nextState).makeStmt()
|
||||||
currentStatements += jump()
|
currentStatements += jump()
|
||||||
@@ -310,11 +305,12 @@ class CoroutineBodyTransformer(
|
|||||||
if (isInFinally) {
|
if (isInFinally) {
|
||||||
val returnBlock = CoroutineBlock()
|
val returnBlock = CoroutineBlock()
|
||||||
jumpWithFinally(0, returnBlock)
|
jumpWithFinally(0, returnBlock)
|
||||||
|
val returnFieldRef = JsNameRef(context.returnValueFieldName, JsLiteral.THIS)
|
||||||
|
currentStatements += JsAstUtils.assignment(returnFieldRef, x.expression).makeStmt()
|
||||||
currentStatements += jump()
|
currentStatements += jump()
|
||||||
|
|
||||||
currentBlock = returnBlock
|
currentBlock = returnBlock
|
||||||
currentStatements += x.expression?.makeStmt().singletonOrEmptyList()
|
currentStatements += JsReturn(returnFieldRef.deepCopy())
|
||||||
currentStatements += JsReturn()
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
currentStatements += x
|
currentStatements += x
|
||||||
@@ -322,16 +318,7 @@ class CoroutineBodyTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitThrow(x: JsThrow) {
|
override fun visitThrow(x: JsThrow) {
|
||||||
if (throwFunctionName != null && tryStack.isEmpty()) {
|
currentStatements += x
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleExpression(expression: JsExpression): JsExpression? {
|
private fun handleExpression(expression: JsExpression): JsExpression? {
|
||||||
@@ -361,15 +348,14 @@ class CoroutineBodyTransformer(
|
|||||||
|
|
||||||
private fun handleSuspend(invocation: JsInvocation) {
|
private fun handleSuspend(invocation: JsInvocation) {
|
||||||
val invokeExpression = if (invocation.isFakeSuspend) invocation.arguments.getOrNull(0) else invocation
|
val invokeExpression = if (invocation.isFakeSuspend) invocation.arguments.getOrNull(0) else invocation
|
||||||
val suspendObjectVar = context.suspendObjectVar
|
val statements = if (invokeExpression == null) {
|
||||||
val statements = if (invokeExpression == null || suspendObjectVar == null) {
|
listOf(JsReturn(context.metadata.suspendObjectRef.deepCopy()))
|
||||||
listOf(JsReturn(invokeExpression))
|
|
||||||
}
|
}
|
||||||
else {
|
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 invocationStatement = JsAstUtils.assignment(resultRef, invokeExpression).makeStmt()
|
||||||
val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), JsAstUtils.pureFqn(suspendObjectVar, null))
|
val suspendCondition = JsAstUtils.equality(resultRef.deepCopy(), context.metadata.suspendObjectRef.deepCopy())
|
||||||
val suspendIfNeeded = JsIf(suspendCondition, JsReturn())
|
val suspendIfNeeded = JsIf(suspendCondition, JsReturn(context.metadata.suspendObjectRef.deepCopy()))
|
||||||
listOf(invocationStatement, suspendIfNeeded, JsBreak())
|
listOf(invocationStatement, suspendIfNeeded, JsBreak())
|
||||||
}
|
}
|
||||||
currentStatements += statements
|
currentStatements += statements
|
||||||
|
|||||||
+53
-127
@@ -17,39 +17,23 @@
|
|||||||
package org.jetbrains.kotlin.js.coroutine
|
package org.jetbrains.kotlin.js.coroutine
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.*
|
import com.google.dart.compiler.backend.js.ast.metadata.coroutineMetadata
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
|
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
|
||||||
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
|
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
|
||||||
import org.jetbrains.kotlin.js.inline.util.getInnerFunction
|
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.context.Namer
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
|
||||||
|
|
||||||
class CoroutineFunctionTransformer(
|
class CoroutineFunctionTransformer(private val program: JsProgram, private val function: JsFunction) {
|
||||||
private val program: JsProgram,
|
|
||||||
private val function: JsFunction,
|
|
||||||
private val coroutineType: ClassDescriptor,
|
|
||||||
private val controllerType: ClassDescriptor
|
|
||||||
) {
|
|
||||||
private val innerFunction = function.getInnerFunction()
|
private val innerFunction = function.getInnerFunction()
|
||||||
private val functionWithBody = innerFunction ?: function
|
private val functionWithBody = innerFunction ?: function
|
||||||
private val body = functionWithBody.body
|
private val body = functionWithBody.body
|
||||||
private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables()).toMutableSet()
|
private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables()).toMutableSet()
|
||||||
private val nameSuggestion = NameSuggestion()
|
|
||||||
private val className = function.scope.parent.declareFreshName("Coroutine\$${function.name}")
|
private val className = function.scope.parent.declareFreshName("Coroutine\$${function.name}")
|
||||||
|
|
||||||
fun transform(): List<JsStatement> {
|
fun transform(): List<JsStatement> {
|
||||||
val throwFunction = controllerType.findFunction("handleException")
|
val context = CoroutineTransformationContext(function.scope, function)
|
||||||
val throwName = throwFunction?.let {
|
val bodyTransformer = CoroutineBodyTransformer(program, context)
|
||||||
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)
|
|
||||||
bodyTransformer.preProcess(body)
|
bodyTransformer.preProcess(body)
|
||||||
body.statements.forEach { it.accept(bodyTransformer) }
|
body.statements.forEach { it.accept(bodyTransformer) }
|
||||||
val coroutineBlocks = bodyTransformer.postProcess()
|
val coroutineBlocks = bodyTransformer.postProcess()
|
||||||
@@ -59,9 +43,8 @@ class CoroutineFunctionTransformer(
|
|||||||
coroutineBlocks.forEach { it.jsBlock.replaceLocalVariables(function.scope, context, localVariables) }
|
coroutineBlocks.forEach { it.jsBlock.replaceLocalVariables(function.scope, context, localVariables) }
|
||||||
|
|
||||||
val additionalStatements = mutableListOf<JsStatement>()
|
val additionalStatements = mutableListOf<JsStatement>()
|
||||||
val resumeName = generateDoResume(coroutineBlocks, context, additionalStatements, throwName)
|
generateDoResume(coroutineBlocks, context, additionalStatements)
|
||||||
generateContinuationConstructor(context, additionalStatements, bodyTransformer.hasFinallyBlocks, globalCatchBlockIndex)
|
generateContinuationConstructor(context, additionalStatements, globalCatchBlockIndex)
|
||||||
generateContinuationMethods(context, resumeName, additionalStatements)
|
|
||||||
|
|
||||||
generateCoroutineInstantiation()
|
generateCoroutineInstantiation()
|
||||||
|
|
||||||
@@ -71,7 +54,6 @@ class CoroutineFunctionTransformer(
|
|||||||
private fun generateContinuationConstructor(
|
private fun generateContinuationConstructor(
|
||||||
context: CoroutineTransformationContext,
|
context: CoroutineTransformationContext,
|
||||||
statements: MutableList<JsStatement>,
|
statements: MutableList<JsStatement>,
|
||||||
hasFinallyBlocks: Boolean,
|
|
||||||
globalCatchBlockIndex: Int
|
globalCatchBlockIndex: Int
|
||||||
) {
|
) {
|
||||||
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
|
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
|
||||||
@@ -81,29 +63,46 @@ class CoroutineFunctionTransformer(
|
|||||||
constructor.parameters += innerFunction.parameters.map { JsParameter(it.name) }
|
constructor.parameters += innerFunction.parameters.map { JsParameter(it.name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val controllerName = function.scope.declareFreshName("controller")
|
val controllerName = if (context.metadata.hasController) {
|
||||||
constructor.parameters += JsParameter(controllerName)
|
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()
|
val parameterNames = (function.parameters.map { it.name } + innerFunction?.parameters?.map { it.name }.orEmpty()).toSet()
|
||||||
|
|
||||||
constructor.body.statements.run {
|
constructor.body.statements.run {
|
||||||
assignToField(context.stateFieldName, program.getNumberLiteral(0))
|
val baseClass = context.metadata.baseClassRef.deepCopy()
|
||||||
assignToField(context.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex))
|
this += JsInvocation(Namer.getFunctionCallRef(baseClass), JsLiteral.THIS, interceptorName.makeRef()).makeStmt()
|
||||||
if (hasFinallyBlocks) {
|
if (controllerName != null) {
|
||||||
assignToField(context.finallyPathFieldName, JsLiteral.NULL)
|
assignToField(context.controllerFieldName, controllerName.makeRef())
|
||||||
}
|
}
|
||||||
assignToField(context.controllerFieldName, controllerName.makeRef())
|
assignToField(context.metadata.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex))
|
||||||
for (localVariable in localVariables) {
|
for (localVariable in localVariables) {
|
||||||
val value = if (localVariable !in parameterNames) JsLiteral.NULL else localVariable.makeRef()
|
val value = if (localVariable !in parameterNames) JsLiteral.NULL else localVariable.makeRef()
|
||||||
assignToField(function.scope.getFieldName(localVariable), value)
|
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 {
|
private fun generateCoroutineMetadata(constructorName: JsName): JsStatement {
|
||||||
val interfaceRef = function.continuationInterfaceRef!!.deepCopy()
|
val baseClassRefRef = function.coroutineMetadata!!.baseClassRef.deepCopy()
|
||||||
|
|
||||||
val metadataObject = JsObjectLiteral(true)
|
val metadataObject = JsObjectLiteral(true)
|
||||||
metadataObject.propertyInitializers += JsPropertyInitializer(
|
metadataObject.propertyInitializers += JsPropertyInitializer(
|
||||||
@@ -111,89 +110,31 @@ class CoroutineFunctionTransformer(
|
|||||||
metadataObject.propertyInitializers += JsPropertyInitializer(
|
metadataObject.propertyInitializers += JsPropertyInitializer(
|
||||||
JsNameRef("classIndex"), JsInvocation(JsNameRef("newClassIndex", Namer.KOTLIN_NAME)))
|
JsNameRef("classIndex"), JsInvocation(JsNameRef("newClassIndex", Namer.KOTLIN_NAME)))
|
||||||
metadataObject.propertyInitializers += JsPropertyInitializer(JsNameRef("simpleName"), JsLiteral.NULL)
|
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()
|
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(
|
private fun generateDoResume(
|
||||||
coroutineBlocks: List<CoroutineBlock>,
|
coroutineBlocks: List<CoroutineBlock>,
|
||||||
context: CoroutineTransformationContext,
|
context: CoroutineTransformationContext,
|
||||||
statements: MutableList<JsStatement>,
|
statements: MutableList<JsStatement>
|
||||||
throwName: JsName?
|
) {
|
||||||
): JsName {
|
|
||||||
val resumeFunction = JsFunction(function.scope.parent, JsBlock(), "resume function")
|
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()
|
functionWithBody.body.statements.clear()
|
||||||
|
|
||||||
resumeFunction.body.statements.apply {
|
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
|
this += coroutineBody
|
||||||
}
|
}
|
||||||
|
|
||||||
val resumeName = function.scope.parent.declareFreshName("doResume")
|
val resumeName = function.coroutineMetadata!!.doResumeName
|
||||||
statements.apply {
|
statements.apply {
|
||||||
assignToPrototype(resumeName, resumeFunction)
|
assignToPrototype(resumeName, resumeFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionPostProcessor(resumeFunction).apply()
|
FunctionPostProcessor(resumeFunction).apply()
|
||||||
|
|
||||||
return resumeName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateCoroutineInstantiation() {
|
private fun generateCoroutineInstantiation() {
|
||||||
@@ -203,36 +144,36 @@ class CoroutineFunctionTransformer(
|
|||||||
instantiation.arguments += innerFunction.parameters.map { it.name.makeRef() }
|
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)
|
functionWithBody.body.statements += JsReturn(instantiation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateCoroutineBody(
|
private fun generateCoroutineBody(
|
||||||
context: CoroutineTransformationContext,
|
context: CoroutineTransformationContext,
|
||||||
blocks: List<CoroutineBlock>,
|
blocks: List<CoroutineBlock>
|
||||||
throwName: JsName?,
|
|
||||||
exceptionName: JsName
|
|
||||||
): List<JsStatement> {
|
): List<JsStatement> {
|
||||||
val indexOfGlobalCatch = blocks.indexOf(context.globalCatchBlock)
|
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 isFromGlobalCatch = JsAstUtils.equality(stateRef, program.getNumberLiteral(indexOfGlobalCatch))
|
||||||
val catch = JsCatch(functionWithBody.scope, "e")
|
val catch = JsCatch(functionWithBody.scope, "e")
|
||||||
val continueWithException = JsBlock(
|
val continueWithException = JsBlock(
|
||||||
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.exceptionStateName, JsLiteral.THIS)).makeStmt(),
|
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.metadata.exceptionStateName, JsLiteral.THIS)).makeStmt(),
|
||||||
JsAstUtils.assignment(JsNameRef(context.exceptionFieldName, JsLiteral.THIS), catch.parameter.name.makeRef()).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))
|
catch.body = JsBlock(JsIf(isFromGlobalCatch, JsThrow(catch.parameter.name.makeRef()), continueWithException))
|
||||||
|
|
||||||
val throwResultRef = JsNameRef(context.exceptionFieldName, JsLiteral.THIS)
|
val throwResultRef = JsNameRef(context.metadata.exceptionName, JsLiteral.THIS)
|
||||||
if (throwName != null) {
|
context.globalCatchBlock.statements += JsThrow(throwResultRef)
|
||||||
val throwMethodRef = JsNameRef(throwName, JsNameRef(context.controllerFieldName, JsLiteral.THIS))
|
|
||||||
context.globalCatchBlock.statements += JsReturn(JsInvocation(throwMethodRef.deepCopy(), throwResultRef))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
context.globalCatchBlock.statements += JsThrow(throwResultRef)
|
|
||||||
}
|
|
||||||
|
|
||||||
val cases = blocks.withIndex().map { (index, block) ->
|
val cases = blocks.withIndex().map { (index, block) ->
|
||||||
JsCase().apply {
|
JsCase().apply {
|
||||||
@@ -243,15 +184,7 @@ class CoroutineFunctionTransformer(
|
|||||||
val switchStatement = JsSwitch(stateRef.deepCopy(), cases)
|
val switchStatement = JsSwitch(stateRef.deepCopy(), cases)
|
||||||
val loop = JsDoWhile(JsLiteral.TRUE, JsTry(JsBlock(switchStatement), catch, null))
|
val loop = JsDoWhile(JsLiteral.TRUE, JsTry(JsBlock(switchStatement), catch, null))
|
||||||
|
|
||||||
val testExceptionPassed = JsAstUtils.notOptimized(
|
return listOf(loop)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JsBlock.collectAdditionalLocalVariables() {
|
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) {
|
private fun MutableList<JsStatement>.assignToField(fieldName: JsName, value: JsExpression) {
|
||||||
this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).makeStmt()
|
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>) {
|
override fun endVisit(x: JsDebugger, ctx: JsContext<in JsStatement>) {
|
||||||
val target = x.targetBlock
|
val target = x.targetBlock
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
val lhs = JsNameRef(context.stateFieldName, JsLiteral.THIS)
|
val lhs = JsNameRef(context.metadata.stateName, JsLiteral.THIS)
|
||||||
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)).apply {
|
||||||
targetBlock = true
|
targetBlock = true
|
||||||
@@ -121,7 +121,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
|
|||||||
|
|
||||||
val exceptionTarget = x.targetExceptionBlock
|
val exceptionTarget = x.targetExceptionBlock
|
||||||
if (exceptionTarget != null) {
|
if (exceptionTarget != null) {
|
||||||
val lhs = JsNameRef(context.exceptionStateName, JsLiteral.THIS)
|
val lhs = JsNameRef(context.metadata.exceptionStateName, JsLiteral.THIS)
|
||||||
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)).apply {
|
||||||
targetExceptionBlock = true
|
targetExceptionBlock = true
|
||||||
@@ -131,7 +131,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
|
|||||||
val finallyPath = x.finallyPath
|
val finallyPath = x.finallyPath
|
||||||
if (finallyPath != null) {
|
if (finallyPath != null) {
|
||||||
if (finallyPath.isNotEmpty()) {
|
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]!!) })
|
val rhs = JsArrayLiteral(finallyPath.map { program.getNumberLiteral(blockIndexes[it]!!) })
|
||||||
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
|
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
|
||||||
this.finallyPath = true
|
this.finallyPath = true
|
||||||
@@ -209,7 +209,7 @@ fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
x.coroutineResult -> {
|
x.coroutineResult -> {
|
||||||
ctx.replaceMe(JsNameRef(context.resultFieldName, x.qualifier).apply {
|
ctx.replaceMe(JsNameRef(context.metadata.resultName, x.qualifier).apply {
|
||||||
sideEffects = SideEffectKind.DEPENDS_ON_STATE
|
sideEffects = SideEffectKind.DEPENDS_ON_STATE
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-7
@@ -16,16 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.coroutine
|
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.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 entryBlock = CoroutineBlock()
|
||||||
val globalCatchBlock = CoroutineBlock()
|
val globalCatchBlock = CoroutineBlock()
|
||||||
val resultFieldName by lazy { scope.declareFreshName("\$result") }
|
val metadata = function.coroutineMetadata!!
|
||||||
val exceptionFieldName by lazy { scope.declareFreshName("\$exception") }
|
|
||||||
val stateFieldName by lazy { scope.declareFreshName("\$state") }
|
|
||||||
val controllerFieldName by lazy { scope.declareFreshName("\$controller") }
|
val controllerFieldName by lazy { scope.declareFreshName("\$controller") }
|
||||||
val exceptionStateName by lazy { scope.declareFreshName("\$exceptionState") }
|
val returnValueFieldName by lazy { scope.declareFreshName("\$returnValue") }
|
||||||
val finallyPathFieldName by lazy { scope.declareFreshName("\$finallyPath") }
|
|
||||||
val suspendObjectVar by lazy { if (stackUnwinding) scope.declareFreshName("\$suspendObject") else null }
|
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.js.coroutine
|
package org.jetbrains.kotlin.js.coroutine
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
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
|
import com.google.dart.compiler.backend.js.ast.metadata.coroutineType
|
||||||
|
|
||||||
class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContextImpl() {
|
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>) {
|
override fun endVisit(x: JsFunction, ctx: JsContext<in JsStatement>) {
|
||||||
val coroutineType = x.coroutineType
|
val coroutineType = x.coroutineType
|
||||||
if (coroutineType != null) {
|
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
|
package org.jetbrains.kotlin.js.inline.clean
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
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.coroutineMetadata
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.interceptResumeRef
|
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.suspendObjectRef
|
|
||||||
import org.jetbrains.kotlin.js.inline.util.collectReferencedTemporaryNames
|
import org.jetbrains.kotlin.js.inline.util.collectReferencedTemporaryNames
|
||||||
|
|
||||||
fun JsNode.resolveTemporaryNames() {
|
fun JsNode.resolveTemporaryNames() {
|
||||||
@@ -59,9 +57,10 @@ fun JsNode.resolveTemporaryNames() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunction(x: JsFunction) {
|
override fun visitFunction(x: JsFunction) {
|
||||||
x.continuationInterfaceRef?.let { accept(it) }
|
x.coroutineMetadata?.apply {
|
||||||
x.interceptResumeRef?.let { accept(it) }
|
accept(suspendObjectRef)
|
||||||
x.suspendObjectRef?.let { accept(it) }
|
accept(baseClassRef)
|
||||||
|
}
|
||||||
super.visitFunction(x)
|
super.visitFunction(x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -86,7 +85,7 @@ private fun Map<JsScope, Set<JsScope>>.resolveNames(knownNames: Set<JsName>): Ma
|
|||||||
return replacements
|
return replacements
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ScopeCollector() : RecursiveJsVisitor() {
|
private class ScopeCollector : RecursiveJsVisitor() {
|
||||||
val scopeTree = mutableMapOf<JsScope, MutableSet<JsScope>>()
|
val scopeTree = mutableMapOf<JsScope, MutableSet<JsScope>>()
|
||||||
|
|
||||||
override fun visitCatch(x: JsCatch) {
|
override fun visitCatch(x: JsCatch) {
|
||||||
|
|||||||
@@ -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
|
||||||
+1
-7
@@ -5505,13 +5505,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
@TestMetadata("simple.kt")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simple.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simple.kt");
|
||||||
try {
|
doTest(fileName);
|
||||||
doTest(fileName);
|
|
||||||
}
|
|
||||||
catch (Throwable ignore) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("simpleException.kt")
|
@TestMetadata("simpleException.kt")
|
||||||
|
|||||||
@@ -174,8 +174,8 @@ public final class Namer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) {
|
public static JsInvocation createObjectWithPrototypeFrom(@NotNull JsExpression referenceToClass) {
|
||||||
return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, JsAstUtils.prototypeOf(referenceToClass));
|
return new JsInvocation(JS_OBJECT_CREATE_FUNCTION.deepCopy(), JsAstUtils.prototypeOf(referenceToClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
|
|||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||||
|
|||||||
+3
-8
@@ -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.reference.*;
|
||||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
|
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
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.js.translate.utils.UtilsKt;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||||
@@ -128,11 +127,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsReturn jsReturn;
|
JsReturn jsReturn;
|
||||||
JsExpression handleResultExpression = TranslationUtils.tryTranslateHandleResult(context, jetReturnExpression, returned);
|
if (returned == null) {
|
||||||
if (handleResultExpression != null) {
|
|
||||||
jsReturn = new JsReturn(handleResultExpression);
|
|
||||||
}
|
|
||||||
else if (returned == null) {
|
|
||||||
jsReturn = new JsReturn(null);
|
jsReturn = new JsReturn(null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -451,13 +446,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitLambdaExpression(@NotNull KtLambdaExpression expression, @NotNull TranslationContext context) {
|
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
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitNamedFunction(@NotNull KtNamedFunction expression, @NotNull TranslationContext context) {
|
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);
|
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
|
||||||
JsNameRef nameRef = (JsNameRef) ReferenceTranslator.translateAsValueReference(descriptor, context);
|
JsNameRef nameRef = (JsNameRef) ReferenceTranslator.translateAsValueReference(descriptor, context);
|
||||||
|
|||||||
+32
-30
@@ -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.*
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.*
|
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.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda
|
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.BindingUtils.getFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody
|
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
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.js.translate.utils.TranslationUtils.simpleReturnFunction
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
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) {
|
class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) {
|
||||||
companion object {
|
companion object {
|
||||||
private val SUSPEND_FQ_NAME = "kotlin.coroutines.Suspend"
|
private val SUSPEND_FQ_NAME = FqName("kotlin.coroutines.SUSPENDED")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translate(
|
fun translate(
|
||||||
declaration: KtDeclarationWithBody,
|
declaration: KtDeclarationWithBody,
|
||||||
continuationType: ClassDescriptor? = null,
|
continuationType: KotlinType? = null
|
||||||
controllerType: ClassDescriptor? = null
|
|
||||||
): JsExpression {
|
): JsExpression {
|
||||||
val invokingContext = context()
|
val invokingContext = context()
|
||||||
val descriptor = getFunctionDescriptor(invokingContext.bindingContext(), declaration)
|
val descriptor = getFunctionDescriptor(invokingContext.bindingContext(), declaration)
|
||||||
@@ -88,41 +87,50 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
|
|||||||
invokingContext.getInnerNameForDescriptor(descriptor)
|
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()) {
|
if (tracker.hasCapturedExceptContaining()) {
|
||||||
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
|
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
|
||||||
lambdaCreator.name = invokingContext.getInnerNameForDescriptor(descriptor)
|
lambdaCreator.name = invokingContext.getInnerNameForDescriptor(descriptor)
|
||||||
lambdaCreator.isLocal = true
|
lambdaCreator.isLocal = true
|
||||||
lambdaCreator.coroutineType = continuationType
|
|
||||||
lambdaCreator.controllerType = controllerType
|
|
||||||
if (!isRecursive || descriptor.isCoroutineLambda) {
|
if (!isRecursive || descriptor.isCoroutineLambda) {
|
||||||
lambda.name = null
|
lambda.name = null
|
||||||
}
|
}
|
||||||
lambdaCreator.name.staticRef = lambdaCreator
|
lambdaCreator.name.staticRef = lambdaCreator
|
||||||
lambdaCreator.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference()
|
lambdaCreator.fillCoroutineMetadata(invokingContext, continuationType)
|
||||||
lambdaCreator.suspendObjectRef = suspendObjectRef
|
|
||||||
return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext),
|
return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext),
|
||||||
invokingContext)
|
invokingContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
lambda.isLocal = true
|
lambda.isLocal = true
|
||||||
lambda.coroutineType = continuationType
|
|
||||||
lambda.controllerType = controllerType
|
|
||||||
|
|
||||||
invokingContext.addDeclarationStatement(lambda.makeStmt())
|
invokingContext.addDeclarationStatement(lambda.makeStmt())
|
||||||
|
lambda.fillCoroutineMetadata(invokingContext, continuationType)
|
||||||
lambda.name.staticRef = lambda
|
lambda.name.staticRef = lambda
|
||||||
lambda.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference()
|
|
||||||
lambda.suspendObjectRef = suspendObjectRef
|
|
||||||
return getReferenceToLambda(invokingContext, descriptor, lambda.name)
|
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 {
|
fun ValueParameterDescriptorImpl.WithDestructuringDeclaration.translate(context: TranslationContext): JsVars {
|
||||||
val destructuringDeclaration =
|
val destructuringDeclaration =
|
||||||
(DescriptorToSourceUtils.descriptorToDeclaration(this) as? KtParameter)?.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 {
|
private fun CallableMemberDescriptor.wrapContextForCoroutineIfNecessary(context: TranslationContext): TranslationContext {
|
||||||
return if (isCoroutineLambda) context.innerContextWithDescriptorsAliased(mapOf(this to JsLiteral.THIS)) else context
|
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)
|
val alias = JsInvocation(localFunAlias.qualifier, aliasCallArguments)
|
||||||
declareAliasInsideFunction(capturingFunction, capturedName, alias)
|
declareAliasInsideFunction(capturingFunction, capturedName, alias)
|
||||||
|
|
||||||
val capturedParameters = freshNames.map {JsParameter(it)}
|
val capturedParameters = freshNames.map(::JsParameter)
|
||||||
return CapturedArgsParams(capturedArgs, capturedParameters)
|
return CapturedArgsParams(capturedArgs, capturedParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-13
@@ -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.SideEffectKind
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
|
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
|
||||||
import com.intellij.util.SmartList
|
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.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
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.expression.PatternTranslator
|
||||||
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
|
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
|
||||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
import org.jetbrains.kotlin.js.translate.utils.*
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
|
|
||||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.psi.ValueArgument
|
import org.jetbrains.kotlin.psi.ValueArgument
|
||||||
@@ -206,19 +205,21 @@ class CallArgumentTranslator private constructor(
|
|||||||
val parenthesizedArgumentExpression = valueArguments[0].getArgumentExpression()!!
|
val parenthesizedArgumentExpression = valueArguments[0].getArgumentExpression()!!
|
||||||
val argumentExpression = KtPsiUtil.deparenthesize(parenthesizedArgumentExpression)
|
val argumentExpression = KtPsiUtil.deparenthesize(parenthesizedArgumentExpression)
|
||||||
|
|
||||||
result += if (/*parameterDescriptor.isCoroutine=*/false && argumentExpression is KtLambdaExpression) {
|
val lambdaDescriptor = argumentExpression?.let { context.extractLambda(it) }
|
||||||
val continuationType = parameterDescriptor.type.arguments.last().type
|
if (lambdaDescriptor?.isSuspendLambda == true) {
|
||||||
val continuationDescriptor = continuationType.constructor.declarationDescriptor as ClassDescriptor
|
val lambdaExpression = (argumentExpression as KtLambdaExpression).functionLiteral
|
||||||
val controllerType = parameterDescriptor.type.arguments[0].type
|
result += LiteralFunctionTranslator(context).translate(lambdaExpression, parameterDescriptor.type)
|
||||||
val controllerDescriptor = controllerType.constructor.declarationDescriptor as ClassDescriptor
|
|
||||||
LiteralFunctionTranslator(context).translate(
|
|
||||||
argumentExpression.functionLiteral, continuationDescriptor, controllerDescriptor)
|
|
||||||
}
|
}
|
||||||
else {
|
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>,
|
private fun translateVarargArgument(arguments: List<ValueArgument>, result: MutableList<JsExpression>,
|
||||||
context: TranslationContext, shouldWrapVarargInArray: Boolean) {
|
context: TranslationContext, shouldWrapVarargInArray: Boolean) {
|
||||||
if (arguments.isEmpty()) {
|
if (arguments.isEmpty()) {
|
||||||
|
|||||||
+2
-10
@@ -118,16 +118,8 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
|
|||||||
KotlinType returnType = descriptor.getReturnType();
|
KotlinType returnType = descriptor.getReturnType();
|
||||||
assert returnType != null;
|
assert returnType != null;
|
||||||
|
|
||||||
|
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
|
||||||
TranslationContext handleResultContext = context().innerBlock(jsBlock);
|
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements());
|
||||||
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());
|
|
||||||
}
|
|
||||||
return jsBlock;
|
return jsBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
-48
@@ -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.*;
|
||||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
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.Namer;
|
||||||
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
|
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||||
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
|
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
|
||||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
|
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.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
|
||||||
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.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
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.resolve.scopes.receivers.ImplicitReceiver;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
|
import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
|
||||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
|
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
|
||||||
@@ -130,7 +130,7 @@ public final class TranslationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
|
private static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
|
||||||
return nullCheck(expressionToCheck, true);
|
return nullCheck(expressionToCheck, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,48 +312,6 @@ public final class TranslationUtils {
|
|||||||
return false;
|
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
|
@NotNull
|
||||||
public static JsExpression translateContinuationArgument(@NotNull TranslationContext context, @NotNull ResolvedCall<?> resolvedCall) {
|
public static JsExpression translateContinuationArgument(@NotNull TranslationContext context, @NotNull ResolvedCall<?> resolvedCall) {
|
||||||
CallableDescriptor continuationDescriptor =
|
CallableDescriptor continuationDescriptor =
|
||||||
@@ -375,4 +333,28 @@ public final class TranslationUtils {
|
|||||||
}
|
}
|
||||||
return result;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user