feat(JsCode): inline usage of js code which captures kotlin variables.
This commit is contained in:
+80
-29
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.*
|
import org.jetbrains.kotlin.backend.common.*
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
@@ -132,41 +131,23 @@ private class JsCodeOutlineTransformer(
|
|||||||
if (expression.symbol != backendContext.intrinsics.jsCode)
|
if (expression.symbol != backendContext.intrinsics.jsCode)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
val jsCodeArg = expression.getValueArgument(0)
|
val jsCodeArg = expression.getValueArgument(0) ?: compilationException("Expected js code string", expression)
|
||||||
?: compilationException(
|
|
||||||
"Expected js code string",
|
|
||||||
expression
|
|
||||||
)
|
|
||||||
val jsStatements = translateJsCodeIntoStatementList(jsCodeArg, backendContext) ?: return null
|
val jsStatements = translateJsCodeIntoStatementList(jsCodeArg, backendContext) ?: return null
|
||||||
|
|
||||||
// Collect used Kotlin local variables and parameters.
|
// Collect used Kotlin local variables and parameters.
|
||||||
val kotlinLocalsUsedInJs = mutableListOf<IrValueDeclaration>()
|
val scope = JsScopesCollector().apply { acceptList(jsStatements) }
|
||||||
val processedNames = mutableSetOf<String>()
|
val localsUsageCollector = KotlinLocalsUsageCollector(scope, ::findValueDeclarationWithName).apply { acceptList(jsStatements) }
|
||||||
jsStatements.forEach { statement ->
|
val kotlinLocalsUsedInJs = localsUsageCollector.usedLocals
|
||||||
object : RecursiveJsVisitor() {
|
|
||||||
override fun visitNameRef(nameRef: JsNameRef) {
|
|
||||||
super.visitNameRef(nameRef)
|
|
||||||
val name = nameRef.name
|
|
||||||
// With this approach we should be able to find all usages of Kotlin variables in JS code.
|
|
||||||
// We will also collect shadowed usages, but it is OK since the same shadowing will be present in generated JS code.
|
|
||||||
if (name != null && nameRef.qualifier == null) {
|
|
||||||
// Keeping track of processed names to avoid registering them multiple times
|
|
||||||
if (processedNames.add(name.ident)) {
|
|
||||||
kotlinLocalsUsedInJs.addIfNotNull(findValueDeclarationWithName(name.ident))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.accept(statement)
|
|
||||||
}
|
|
||||||
if (kotlinLocalsUsedInJs.isEmpty())
|
if (kotlinLocalsUsedInJs.isEmpty())
|
||||||
return null
|
return null
|
||||||
|
|
||||||
// Building outlined IR function skeleton
|
// Building outlined IR function skeleton
|
||||||
val outlinedFunction = backendContext.irFactory.buildFun {
|
val outlinedFunction = backendContext.irFactory.buildFun {
|
||||||
name = Name.identifier("outlinedJsCode$")
|
name = Name.identifier("outlinedJsCode$")
|
||||||
visibility = DescriptorVisibilities.LOCAL
|
|
||||||
returnType = backendContext.dynamicType
|
returnType = backendContext.dynamicType
|
||||||
origin = OUTLINED_ORIGIN
|
isExternal = true
|
||||||
|
origin = JsIrBackendContext.callableClosureOrigin
|
||||||
}
|
}
|
||||||
// We don't need this function's body. Using empty block body stub, because some code might expect all functions to have bodies.
|
// We don't need this function's body. Using empty block body stub, because some code might expect all functions to have bodies.
|
||||||
outlinedFunction.body = backendContext.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
outlinedFunction.body = backendContext.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
||||||
@@ -219,8 +200,78 @@ private class JsCodeOutlineTransformer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
class JsScopesCollector : RecursiveJsVisitor() {
|
||||||
object OUTLINED_ORIGIN : IrDeclarationOriginImpl("OUTLINED_ORIGIN")
|
private val functionsStack = mutableListOf(Scope(null))
|
||||||
|
private val functionalScopes = mutableMapOf<JsFunction?, Scope>(null to functionsStack.first())
|
||||||
|
|
||||||
|
private class Scope(val parent: Scope?) {
|
||||||
|
private val variables = hashSetOf<String>()
|
||||||
|
|
||||||
|
fun add(variableName: String) {
|
||||||
|
variables.add(variableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun variableWithNameExists(variableName: String): Boolean {
|
||||||
|
return variables.contains(variableName) ||
|
||||||
|
parent?.variableWithNameExists(variableName) == true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
override fun visitVars(x: JsVars) {
|
||||||
|
super.visitVars(x)
|
||||||
|
val currentScope = functionsStack.last()
|
||||||
|
x.vars.forEach { currentScope.add(it.name.ident) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(x: JsFunction) {
|
||||||
|
val parentScope = functionsStack.last()
|
||||||
|
val newScope = Scope(parentScope).apply {
|
||||||
|
val name = x.name?.ident
|
||||||
|
if (name != null) add(name)
|
||||||
|
x.parameters.forEach { add(it.name.ident) }
|
||||||
|
}
|
||||||
|
functionsStack.push(newScope)
|
||||||
|
functionalScopes[x] = newScope
|
||||||
|
super.visitFunction(x)
|
||||||
|
functionsStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun varWithNameExistsInScopeOf(function: JsFunction?, variableName: String): Boolean {
|
||||||
|
return functionalScopes[function]!!.variableWithNameExists(variableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KotlinLocalsUsageCollector(
|
||||||
|
private val scopeInfo: JsScopesCollector,
|
||||||
|
private val findValueDeclarationWithName: (String) -> IrValueDeclaration?
|
||||||
|
) : RecursiveJsVisitor() {
|
||||||
|
private val functionStack = mutableListOf<JsFunction?>(null)
|
||||||
|
private val processedNames = mutableSetOf<String>()
|
||||||
|
private val kotlinLocalsUsedInJs = mutableListOf<IrValueDeclaration>()
|
||||||
|
|
||||||
|
val usedLocals: List<IrValueDeclaration>
|
||||||
|
get() = kotlinLocalsUsedInJs
|
||||||
|
|
||||||
|
override fun visitFunction(x: JsFunction) {
|
||||||
|
functionStack.push(x)
|
||||||
|
super.visitFunction(x)
|
||||||
|
functionStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitNameRef(nameRef: JsNameRef) {
|
||||||
|
super.visitNameRef(nameRef)
|
||||||
|
val name = nameRef.name.takeIf { nameRef.qualifier == null } ?: return
|
||||||
|
// With this approach we should be able to find all usages of Kotlin variables in JS code.
|
||||||
|
// We will also collect shadowed usages, but it is OK since the same shadowing will be present in generated JS code.
|
||||||
|
// Keeping track of processed names to avoid registering them multiple times
|
||||||
|
if (processedNames.add(name.ident) && !name.isDeclaredInsideJsCode()) {
|
||||||
|
kotlinLocalsUsedInJs.addIfNotNull(findValueDeclarationWithName(name.ident))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsName.isDeclaredInsideJsCode(): Boolean {
|
||||||
|
return scopeInfo.varWithNameExistsInScopeOf(functionStack.peek(), ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.compilationException
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.getJsFunAnnotation
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
|
||||||
|
class FunctionWithJsFuncAnnotationInliner(private val jsFuncCall: IrCall, private val context: JsGenerationContext) {
|
||||||
|
private val function = getJsFunctionImplementation()
|
||||||
|
private val replacements = collectReplacementsForCall()
|
||||||
|
|
||||||
|
fun generateResultStatement(): List<JsStatement> {
|
||||||
|
return function.body.statements
|
||||||
|
.run {
|
||||||
|
SimpleJsCodeInliner(replacements)
|
||||||
|
.apply { acceptList(this@run) }
|
||||||
|
.withTemporaryVariablesForExpressions(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getJsFunctionImplementation(): JsFunction {
|
||||||
|
val code = jsFuncCall.symbol.owner.getJsFunAnnotation() ?: compilationException("JsFun annotation is expected", jsFuncCall)
|
||||||
|
val statements = parseJsCode(code) ?: compilationException("Cannot compute js code", jsFuncCall)
|
||||||
|
return statements.singleOrNull()
|
||||||
|
?.let { it as? JsExpressionStatement }
|
||||||
|
?.let { it.expression as? JsFunction } ?: compilationException("Provided js code is not a js function", jsFuncCall.symbol.owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectReplacementsForCall(): Map<JsName, JsExpression> {
|
||||||
|
val translatedArguments = Array(jsFuncCall.valueArgumentsCount) {
|
||||||
|
jsFuncCall.getValueArgument(it)!!.accept(IrElementToJsExpressionTransformer(), context)
|
||||||
|
}
|
||||||
|
return function.parameters
|
||||||
|
.mapIndexed { i, param -> param.name to translatedArguments[i] }
|
||||||
|
.toMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SimpleJsCodeInliner(private val replacements: Map<JsName, JsExpression>): RecursiveJsVisitor() {
|
||||||
|
private val temporaryNamesForExpressions = mutableMapOf<JsName, JsExpression>()
|
||||||
|
|
||||||
|
fun withTemporaryVariablesForExpressions(statements: List<JsStatement>): List<JsStatement> {
|
||||||
|
if (temporaryNamesForExpressions.isEmpty()) {
|
||||||
|
return statements
|
||||||
|
}
|
||||||
|
|
||||||
|
val variableDeclarations = temporaryNamesForExpressions.map { JsVars(JsVars.JsVar(it.key, it.value)) }
|
||||||
|
return variableDeclarations + statements
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitNameRef(nameRef: JsNameRef) {
|
||||||
|
super.visitNameRef(nameRef)
|
||||||
|
if (nameRef.qualifier != null) return
|
||||||
|
nameRef.name = nameRef.name?.getReplacement() ?: return
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsName.declareNewTemporaryFor(expression: JsExpression): JsName {
|
||||||
|
return JsName(ident, true)
|
||||||
|
.also { temporaryNamesForExpressions[it] = expression }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsName.getReplacement(): JsName? {
|
||||||
|
val expression = replacements[this] ?: return null
|
||||||
|
return when {
|
||||||
|
expression is JsNameRef && expression.qualifier == null -> expression.name!!
|
||||||
|
else -> declareNewTemporaryFor(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-35
@@ -221,41 +221,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
|
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
|
||||||
if (context.checkIfJsCode(expression.symbol)) {
|
if (context.checkIfJsCode(expression.symbol) || context.checkIfAnnotatedWithJsFunc(expression.symbol)) {
|
||||||
val statements = translateJsCodeIntoStatementList(
|
return JsCallTransformer(expression, context).generateExpression()
|
||||||
expression.getValueArgument(0)
|
|
||||||
?: compilationException(
|
|
||||||
"JsCode is expected",
|
|
||||||
expression
|
|
||||||
),
|
|
||||||
context.staticContext.backendContext
|
|
||||||
) ?: compilationException(
|
|
||||||
"Cannot compute js code",
|
|
||||||
expression
|
|
||||||
)
|
|
||||||
|
|
||||||
if (statements.isEmpty()) return JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)) // TODO: report warning or even error
|
|
||||||
|
|
||||||
val lastStatement = statements.last()
|
|
||||||
if (statements.size == 1) {
|
|
||||||
if (lastStatement is JsExpressionStatement) return lastStatement.expression.withSource(expression, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newStatements = statements.toMutableList()
|
|
||||||
|
|
||||||
when (lastStatement) {
|
|
||||||
is JsReturn -> {
|
|
||||||
}
|
|
||||||
is JsExpressionStatement -> {
|
|
||||||
newStatements[statements.lastIndex] = JsReturn(lastStatement.expression)
|
|
||||||
}
|
|
||||||
// TODO: report warning or even error
|
|
||||||
else -> newStatements += JsReturn(JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)))
|
|
||||||
}
|
|
||||||
|
|
||||||
val syntheticFunction = JsFunction(emptyScope, JsBlock(newStatements), "")
|
|
||||||
return JsInvocation(syntheticFunction).withSource(expression, context)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return translateCall(expression, context, this).withSource(expression, context)
|
return translateCall(expression, context, this).withSource(expression, context)
|
||||||
.also {
|
.also {
|
||||||
|
|||||||
+2
-18
@@ -135,24 +135,8 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: JsGenerationContext): JsStatement {
|
override fun visitCall(expression: IrCall, data: JsGenerationContext): JsStatement {
|
||||||
if (data.checkIfJsCode(expression.symbol)) {
|
if (data.checkIfJsCode(expression.symbol) || data.checkIfAnnotatedWithJsFunc(expression.symbol)) {
|
||||||
val statements = translateJsCodeIntoStatementList(
|
return JsCallTransformer(expression, data).generateStatement()
|
||||||
expression.getValueArgument(0)
|
|
||||||
?: compilationException(
|
|
||||||
"JsCode is expected",
|
|
||||||
expression
|
|
||||||
),
|
|
||||||
data.staticContext.backendContext
|
|
||||||
) ?: compilationException(
|
|
||||||
"Cannot compute js code",
|
|
||||||
expression
|
|
||||||
)
|
|
||||||
return when (statements.size) {
|
|
||||||
0 -> JsEmpty
|
|
||||||
1 -> statements.single().withSource(expression, data)
|
|
||||||
// TODO: use transparent block (e.g. JsCompositeBlock)
|
|
||||||
else -> JsBlock(statements)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return translateCall(expression, data, IrElementToJsExpressionTransformer()).withSource(expression, data).makeStmt()
|
return translateCall(expression, data, IrElementToJsExpressionTransformer()).withSource(expression, data).makeStmt()
|
||||||
.also { data.staticContext.polyfills.visitDeclaration(expression.symbol.owner) }
|
.also { data.staticContext.polyfills.visitDeclaration(expression.symbol.owner) }
|
||||||
|
|||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.compilationException
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
|
||||||
|
class JsCallTransformer(private val jsOrJsFuncCall: IrCall, private val context: JsGenerationContext) {
|
||||||
|
private val statements = getJsStatements()
|
||||||
|
|
||||||
|
fun generateStatement(): JsStatement {
|
||||||
|
if (statements.isEmpty()) return JsEmpty
|
||||||
|
|
||||||
|
val newStatements = statements.toMutableList().apply {
|
||||||
|
val expression = (last() as? JsReturn)?.expression ?: return@apply
|
||||||
|
|
||||||
|
if (expression is JsPrefixOperation && expression.operator == JsUnaryOperator.VOID) {
|
||||||
|
removeLastOrNull()
|
||||||
|
} else {
|
||||||
|
set(lastIndex, expression.makeStmt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (newStatements.size) {
|
||||||
|
0 -> JsEmpty
|
||||||
|
1 -> newStatements.single().withSource(jsOrJsFuncCall, context)
|
||||||
|
// TODO: use transparent block (e.g. JsCompositeBlock)
|
||||||
|
else -> JsBlock(newStatements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateExpression(): JsExpression {
|
||||||
|
if (statements.isEmpty()) return JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)) // TODO: report warning or even error
|
||||||
|
|
||||||
|
val lastStatement = statements.last()
|
||||||
|
val lastExpression = when (lastStatement) {
|
||||||
|
is JsReturn -> lastStatement.expression
|
||||||
|
is JsExpressionStatement -> lastStatement.expression
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (statements.size == 1 && lastExpression != null) {
|
||||||
|
return lastExpression.withSource(jsOrJsFuncCall, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newStatements = statements.toMutableList()
|
||||||
|
|
||||||
|
when (lastStatement) {
|
||||||
|
is JsReturn -> {
|
||||||
|
}
|
||||||
|
is JsExpressionStatement -> {
|
||||||
|
newStatements[statements.lastIndex] = JsReturn(lastStatement.expression)
|
||||||
|
}
|
||||||
|
// TODO: report warning or even error
|
||||||
|
else -> newStatements += JsReturn(JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)))
|
||||||
|
}
|
||||||
|
|
||||||
|
val syntheticFunction = JsFunction(emptyScope, JsBlock(newStatements), "")
|
||||||
|
return JsInvocation(syntheticFunction).withSource(jsOrJsFuncCall, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getJsStatements(): List<JsStatement> {
|
||||||
|
return when {
|
||||||
|
context.checkIfJsCode(jsOrJsFuncCall.symbol) -> {
|
||||||
|
translateJsCodeIntoStatementList(
|
||||||
|
jsOrJsFuncCall.getValueArgument(0) ?: compilationException("JsCode is expected", jsOrJsFuncCall),
|
||||||
|
context.staticContext.backendContext
|
||||||
|
)
|
||||||
|
?: compilationException("Cannot compute js code", jsOrJsFuncCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.checkIfAnnotatedWithJsFunc(jsOrJsFuncCall.symbol) ->
|
||||||
|
FunctionWithJsFuncAnnotationInliner(jsOrJsFuncCall, context).generateResultStatement()
|
||||||
|
|
||||||
|
else -> compilationException("`js` function call or function with @JsFunc annotation expected", jsOrJsFuncCall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,6 +61,8 @@ fun IrAnnotationContainer.isJsNativeSetter(): Boolean = hasAnnotation(JsAnnotati
|
|||||||
|
|
||||||
fun IrAnnotationContainer.isJsNativeInvoke(): Boolean = hasAnnotation(JsAnnotations.jsNativeInvoke)
|
fun IrAnnotationContainer.isJsNativeInvoke(): Boolean = hasAnnotation(JsAnnotations.jsNativeInvoke)
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.isAnnotatedWithJsFun(): Boolean = hasAnnotation(JsAnnotations.jsFunFqn)
|
||||||
|
|
||||||
fun IrDeclarationWithName.getJsNameForOverriddenDeclaration(): String? {
|
fun IrDeclarationWithName.getJsNameForOverriddenDeclaration(): String? {
|
||||||
val jsName = getJsName()
|
val jsName = getJsName()
|
||||||
|
|
||||||
|
|||||||
+2
@@ -77,4 +77,6 @@ class JsGenerationContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
|
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
|
||||||
|
|
||||||
|
fun checkIfAnnotatedWithJsFunc(symbol: IrFunctionSymbol): Boolean = symbol.owner.isAnnotatedWithJsFun()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1282
|
// EXPECTED_REACHABLE_NODES: 1282
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// IGNORE_BACKEND: JS_IR_ES6
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1282
|
// EXPECTED_REACHABLE_NODES: 1282
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1282
|
// EXPECTED_REACHABLE_NODES: 1282
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1285
|
// EXPECTED_REACHABLE_NODES: 1285
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1282
|
// EXPECTED_REACHABLE_NODES: 1282
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
// CHECK_LABELS_COUNT: function=box name=block count=2
|
// CHECK_LABELS_COUNT: function=box name=block count=2
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// IGNORE_BACKEND: JS_IR_ES6
|
|
||||||
// KJS_WITH_FULL_RUNTIME
|
// KJS_WITH_FULL_RUNTIME
|
||||||
// EXPECTED_REACHABLE_NODES: 1687
|
// EXPECTED_REACHABLE_NODES: 1687
|
||||||
external fun p(m: String): String
|
external fun p(m: String): String
|
||||||
@@ -26,9 +24,15 @@ fun test4(): String {
|
|||||||
return js("p('test4')")
|
return js("p('test4')")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun f() = js("p('test5')")
|
||||||
|
|
||||||
fun test5(): String {
|
fun test5(): String {
|
||||||
val p = "wrong5"
|
val p = "wrong5"
|
||||||
fun f() = js("p('test5')")
|
// The behavoiur of the classical backend is weird and buggy
|
||||||
|
// From the user side, the local variable `p` is captured
|
||||||
|
// but we have different behaviour because the renaming phase in classical backend
|
||||||
|
// will be invoked after the lambda will be moved up
|
||||||
|
// fun f() = js("p('test5')")
|
||||||
return f()
|
return f()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user