JS: improve inline suspend function declaration splitter
This commit is contained in:
@@ -73,7 +73,6 @@ fun transformCoroutines(fragments: Iterable<JsProgramFragment>) {
|
|||||||
val coroutineTransformer = CoroutineTransformer()
|
val coroutineTransformer = CoroutineTransformer()
|
||||||
for (fragment in fragments) {
|
for (fragment in fragments) {
|
||||||
ImportInfoFragmentInliningScope.process(fragment) { scope ->
|
ImportInfoFragmentInliningScope.process(fragment) { scope ->
|
||||||
InlineSuspendFunctionSplitter(scope).accept(scope.allCode)
|
|
||||||
coroutineTransformer.accept(scope.allCode)
|
coroutineTransformer.accept(scope.allCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,64 +8,45 @@ package org.jetbrains.kotlin.js.inline
|
|||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
|
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineableCoroutineBody
|
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineableCoroutineBody
|
||||||
|
import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper
|
||||||
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
|
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
|
||||||
|
|
||||||
// Goes through `suspend inline` function definitions and transforms into two entites:
|
// Goes through `suspend inline` function definitions and transforms into two entities:
|
||||||
// 1. A `suspend` function definition, which could be invoked at run time
|
// 1. A `suspend` function definition, which could be invoked at run time
|
||||||
// 2. A `suspend inline` function definition, which doesn't work at run time, but is understood by the inliner.
|
// 2. A `suspend inline` function definition, which doesn't work at run time, but is understood by the inliner.
|
||||||
// TODO remove the wrapped version for `private inline suspend fun`'s
|
|
||||||
class InlineSuspendFunctionSplitter(
|
class InlineSuspendFunctionSplitter(
|
||||||
val scope: ImportInfoFragmentInliningScope
|
val scope: ImportInfoFragmentInliningScope
|
||||||
) : JsVisitorWithContextImpl() {
|
) : JsVisitorWithContextImpl() {
|
||||||
|
|
||||||
override fun endVisit(x: JsExpressionStatement, ctx: JsContext<*>) {
|
override fun visit(x: JsInvocation, ctx: JsContext<JsNode>): Boolean {
|
||||||
val e = x.expression
|
// Is it `defineInlineFunction('tag', ...)`?
|
||||||
if (e is JsBinaryOperation) {
|
InlineMetadata.decompose(x)?.let { metadata ->
|
||||||
if (e.operator == JsBinaryOperator.ASG) {
|
val fn = metadata.function
|
||||||
e.arg2?.let { argument2 ->
|
if (fn.function.coroutineMetadata != null) {
|
||||||
val splitSuspendInlineFunction = splitExportedSuspendInlineFunctionDeclarations(argument2)
|
// This function will be exported to JS
|
||||||
if (splitSuspendInlineFunction != null) {
|
ctx.replaceMe(scope.importFunctionDefinition(InlineFunctionDefinition(fn, metadata.tag.value)))
|
||||||
e.arg2 = splitSuspendInlineFunction
|
|
||||||
}
|
// Original function should be not be transformed into a state machine
|
||||||
}
|
fn.function.name = null
|
||||||
|
fn.function.coroutineMetadata = null
|
||||||
|
fn.function.isInlineableCoroutineBody = true
|
||||||
|
|
||||||
|
// Keep the `defineInlineFunction` for the inliner to find
|
||||||
|
lastStatementLevelContext.addNext(x.makeStmt())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
super.endVisit(x, ctx)
|
// Is it `wrapFunction(...)`? Which means it'a a private inline function
|
||||||
}
|
InlineMetadata.tryExtractFunction(x)?.let { fn ->
|
||||||
|
if (fn.function.coroutineMetadata != null) {
|
||||||
override fun endVisit(x: JsVars.JsVar, ctx: JsContext<*>) {
|
// This function will be exported to JS
|
||||||
val initExpression = x.initExpression ?: return
|
ctx.replaceMe(scope.importFunctionDefinition(InlineFunctionDefinition(fn, null)))
|
||||||
|
|
||||||
val splitSuspendInlineFunction = splitExportedSuspendInlineFunctionDeclarations(initExpression)
|
|
||||||
if (splitSuspendInlineFunction != null) {
|
|
||||||
x.initExpression = splitSuspendInlineFunction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun splitExportedSuspendInlineFunctionDeclarations(expression: JsExpression): JsFunction? {
|
|
||||||
val inlineMetadata = InlineMetadata.decompose(expression)
|
|
||||||
if (inlineMetadata != null) {
|
|
||||||
inlineMetadata.function.let { f ->
|
|
||||||
if (f.function.coroutineMetadata != null) {
|
|
||||||
val statementContext = lastStatementLevelContext
|
|
||||||
|
|
||||||
// This function will be exported to JS
|
|
||||||
val function = scope.importFunctionDefinition(InlineFunctionDefinition(inlineMetadata.function, inlineMetadata.tag.value))
|
|
||||||
|
|
||||||
// Original function should be not be transformed into a state machine
|
|
||||||
f.function.name = null
|
|
||||||
f.function.coroutineMetadata = null
|
|
||||||
f.function.isInlineableCoroutineBody = true
|
|
||||||
|
|
||||||
// Keep the `defineInlineFunction` for the inliner to find
|
|
||||||
statementContext.addNext(expression.makeStmt())
|
|
||||||
|
|
||||||
// Return the function body to be used without inlining.
|
|
||||||
return function
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
|
return super.visit(x, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,10 @@ class ImportInfoFragmentInliningScope private constructor(
|
|||||||
fragment.declarationBlock.statements.addAll(0, additionalDeclarations)
|
fragment.declarationBlock.statements.addAll(0, additionalDeclarations)
|
||||||
|
|
||||||
// post-processing
|
// post-processing
|
||||||
|
|
||||||
|
// If run separately `private inline suspend fun`'s local declarations get inlined twice.
|
||||||
|
InlineSuspendFunctionSplitter(this).accept(allCode)
|
||||||
|
|
||||||
simplifyWrappedFunctions(allCode)
|
simplifyWrappedFunctions(allCode)
|
||||||
removeUnusedFunctionDefinitions(allCode, collectNamedFunctions(allCode))
|
removeUnusedFunctionDefinitions(allCode, collectNamedFunctions(allCode))
|
||||||
removeUnusedImports(fragment, allCode)
|
removeUnusedImports(fragment, allCode)
|
||||||
|
|||||||
Reference in New Issue
Block a user