JS: transform coroutines before serializing AST to binary format
This commit is contained in:
@@ -30,7 +30,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
|
||||
private val body = functionWithBody.body
|
||||
private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables() -
|
||||
functionWithBody.parameters.last().name).toMutableSet()
|
||||
private val className = function.scope.parent.declareFreshName("Coroutine\$${name ?: "anonymous"}")
|
||||
private val className = JsScope.declareTemporaryName("Coroutine\$${name ?: "anonymous"}")
|
||||
|
||||
fun transform(): List<JsStatement> {
|
||||
val context = CoroutineTransformationContext(function.scope, function)
|
||||
@@ -41,7 +41,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
|
||||
val globalCatchBlockIndex = coroutineBlocks.indexOf(context.globalCatchBlock)
|
||||
|
||||
coroutineBlocks.forEach { it.jsBlock.collectAdditionalLocalVariables() }
|
||||
coroutineBlocks.forEach { it.jsBlock.replaceLocalVariables(function.scope, context, localVariables) }
|
||||
coroutineBlocks.forEach { it.jsBlock.replaceLocalVariables(context, localVariables) }
|
||||
|
||||
val additionalStatements = mutableListOf<JsStatement>()
|
||||
generateDoResume(coroutineBlocks, context, additionalStatements)
|
||||
@@ -67,7 +67,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
|
||||
val lastParameter = parameters.lastOrNull()?.name
|
||||
|
||||
val controllerName = if (context.metadata.hasController) {
|
||||
function.scope.declareFreshName("controller").apply {
|
||||
JsScope.declareTemporaryName("controller").apply {
|
||||
constructor.parameters.add(constructor.parameters.lastIndex, JsParameter(this))
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
|
||||
}
|
||||
for (localVariable in localVariables) {
|
||||
val value = if (localVariable !in parameterNames) Namer.getUndefinedExpression() else localVariable.makeRef()
|
||||
assignToField(function.scope.getFieldName(localVariable), value)
|
||||
assignToField(context.getFieldName(localVariable), value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,10 +157,10 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
|
||||
|
||||
instantiation.arguments += parameters.last().name.makeRef()
|
||||
|
||||
val suspendedName = functionWithBody.scope.declareFreshName("suspended")
|
||||
val suspendedName = JsScope.declareTemporaryName("suspended")
|
||||
functionWithBody.parameters += JsParameter(suspendedName)
|
||||
|
||||
val instanceName = functionWithBody.scope.declareFreshName("instance")
|
||||
val instanceName = JsScope.declareTemporaryName("instance")
|
||||
functionWithBody.body.statements += JsAstUtils.newVar(instanceName, instantiation)
|
||||
|
||||
val invokeResume = JsReturn(JsInvocation(JsNameRef(context.metadata.doResumeName, instanceName.makeRef()), JsLiteral.NULL))
|
||||
|
||||
@@ -237,8 +237,9 @@ fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) {
|
||||
visitor.accept(this)
|
||||
}
|
||||
|
||||
fun JsBlock.replaceLocalVariables(scope: JsScope, context: CoroutineTransformationContext, localVariables: Set<JsName>) {
|
||||
fun JsBlock.replaceLocalVariables(context: CoroutineTransformationContext, localVariables: Set<JsName>) {
|
||||
replaceSpecialReferences(context)
|
||||
|
||||
val visitor = object : JsVisitorWithContextImpl() {
|
||||
override fun visit(x: JsFunction, ctx: JsContext<*>): Boolean = false
|
||||
|
||||
@@ -260,14 +261,14 @@ fun JsBlock.replaceLocalVariables(scope: JsScope, context: CoroutineTransformati
|
||||
|
||||
override fun endVisit(x: JsNameRef, ctx: JsContext<in JsNode>) {
|
||||
if (x.qualifier == null && x.name in localVariables) {
|
||||
val fieldName = scope.getFieldName(x.name!!)
|
||||
val fieldName = context.getFieldName(x.name!!)
|
||||
ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS))
|
||||
}
|
||||
}
|
||||
|
||||
override fun endVisit(x: JsVars, ctx: JsContext<in JsStatement>) {
|
||||
val assignments = x.vars.mapNotNull {
|
||||
val fieldName = scope.getFieldName(it.name)
|
||||
val fieldName = context.getFieldName(it.name)
|
||||
val initExpression = it.initExpression
|
||||
if (initExpression != null) {
|
||||
JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), it.initExpression)
|
||||
@@ -286,6 +287,4 @@ fun JsBlock.replaceLocalVariables(scope: JsScope, context: CoroutineTransformati
|
||||
}
|
||||
}
|
||||
visitor.accept(this)
|
||||
}
|
||||
|
||||
fun JsScope.getFieldName(variableName: JsName) = declareName("local\$${variableName.ident}")
|
||||
}
|
||||
+17
-3
@@ -17,14 +17,28 @@
|
||||
package org.jetbrains.kotlin.js.coroutine
|
||||
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
|
||||
|
||||
class CoroutineTransformationContext(private val scope: JsScope, function: JsFunction) {
|
||||
private val localVariableNameCache = mutableMapOf<JsName, JsName>()
|
||||
private val usedLocalVariableIds = mutableSetOf<String>()
|
||||
|
||||
val entryBlock = CoroutineBlock()
|
||||
val globalCatchBlock = CoroutineBlock()
|
||||
val metadata = function.coroutineMetadata!!
|
||||
val controllerFieldName by lazy { scope.declareFreshName("\$controller") }
|
||||
val returnValueFieldName by lazy { scope.declareFreshName("\$returnValue") }
|
||||
val receiverFieldName by lazy { scope.declareFreshName("\$this") }
|
||||
val controllerFieldName by lazy { scope.declareName("\$controller") }
|
||||
val returnValueFieldName by lazy { scope.declareName("\$returnValue") }
|
||||
val receiverFieldName by lazy { scope.declareName("\$this") }
|
||||
|
||||
fun getFieldName(variableName: JsName) = localVariableNameCache.getOrPut(variableName) {
|
||||
val baseId = "local\$${variableName.ident}"
|
||||
var suggestedId = baseId
|
||||
var suffix = 0
|
||||
while (!usedLocalVariableIds.add(suggestedId)) {
|
||||
suggestedId = "${baseId}_${suffix++}"
|
||||
}
|
||||
scope.declareName(suggestedId)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user