[JS IR] Lift lambdas that capture no context
If a lambda expression does not capture any local variables, convert
it to a global free function and replace the lambda creation with
a reference to that function.
Example: for the following Kotlin code
```kotlin
fun foo(f: () -> Unit) = f()
fun bar() = foo { console.log("hello") }
```
before this patch, we generated:
```js
function foo(f) {
return f();
}
function bar() {
return foo(bar$lambda());
}
function bar$lambda() {
return function () {
console.log('hello');
};
}
```
after this patch, we generate:
```js
function foo(f) {
return f();
}
function bar() {
return foo(bar$lambda);
}
function bar$lambda() {
console.log('hello');
}
```
This commit is contained in:
+156
-72
@@ -34,38 +34,66 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
val ctorToFactoryMap = mutableMapOf<IrConstructorSymbol, IrSimpleFunctionSymbol>()
|
val ctorToFactoryMap = mutableMapOf<IrConstructorSymbol, IrSimpleFunctionSymbol>()
|
||||||
irFile.transform(CallableReferenceClassTransformer(ctorToFactoryMap), null)
|
val ctorToFreeFunctionMap = mutableMapOf<IrConstructorSymbol, IrSimpleFunctionSymbol>()
|
||||||
|
irFile.transform(CallableReferenceClassTransformer(ctorToFactoryMap, ctorToFreeFunctionMap), null)
|
||||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
expression.transformChildrenVoid()
|
expression.transformChildrenVoid()
|
||||||
if (expression.origin != JsStatementOrigins.CALLABLE_REFERENCE_CREATE) return expression
|
if (expression.origin != JsStatementOrigins.CALLABLE_REFERENCE_CREATE) return expression
|
||||||
return ctorToFactoryMap[expression.symbol]?.let { factory ->
|
|
||||||
val newCall = expression.run {
|
|
||||||
IrCallImpl(startOffset, endOffset, type, factory, typeArgumentsCount, valueArgumentsCount, origin)
|
|
||||||
}
|
|
||||||
|
|
||||||
newCall.dispatchReceiver = expression.dispatchReceiver
|
ctorToFreeFunctionMap[expression.symbol]?.let { liftedLambda ->
|
||||||
newCall.extensionReceiver = expression.extensionReceiver
|
return replaceLambdaConstructorCallWithReferenceToLiftedLambda(expression, liftedLambda)
|
||||||
|
}
|
||||||
|
|
||||||
for (i in 0 until expression.typeArgumentsCount) {
|
ctorToFactoryMap[expression.symbol]?.let { factory ->
|
||||||
newCall.putTypeArgument(i, expression.getTypeArgument(i))
|
return replaceLambdaConstructorCallWithFactoryCall(expression, factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until expression.valueArgumentsCount) {
|
return expression
|
||||||
newCall.putValueArgument(i, expression.getValueArgument(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
newCall
|
|
||||||
} ?: expression
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun replaceLambdaConstructorCallWithFactoryCall(
|
||||||
|
expression: IrConstructorCall,
|
||||||
|
factory: IrSimpleFunctionSymbol
|
||||||
|
): IrCall {
|
||||||
|
val newCall = expression.run {
|
||||||
|
IrCallImpl(startOffset, endOffset, type, factory, typeArgumentsCount, valueArgumentsCount, origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCall.dispatchReceiver = expression.dispatchReceiver
|
||||||
|
newCall.extensionReceiver = expression.extensionReceiver
|
||||||
|
|
||||||
|
for (i in 0 until expression.typeArgumentsCount) {
|
||||||
|
newCall.putTypeArgument(i, expression.getTypeArgument(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until expression.valueArgumentsCount) {
|
||||||
|
newCall.putValueArgument(i, expression.getValueArgument(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCall
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceLambdaConstructorCallWithReferenceToLiftedLambda(
|
||||||
|
expression: IrConstructorCall,
|
||||||
|
liftedLambda: IrSimpleFunctionSymbol
|
||||||
|
): IrRawFunctionReference = IrRawFunctionReferenceImpl(
|
||||||
|
expression.startOffset,
|
||||||
|
expression.endOffset,
|
||||||
|
expression.type,
|
||||||
|
liftedLambda,
|
||||||
|
)
|
||||||
|
|
||||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
compilationException("Unreachable", irBody)
|
compilationException("Unreachable", irBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class CallableReferenceClassTransformer(private val ctorToFactoryMap: MutableMap<IrConstructorSymbol, IrSimpleFunctionSymbol>) : IrElementTransformerVoid() {
|
private inner class CallableReferenceClassTransformer(
|
||||||
|
private val ctorToFactoryMap: MutableMap<IrConstructorSymbol, IrSimpleFunctionSymbol>,
|
||||||
|
private val ctorToFreeFunctionMap: MutableMap<IrConstructorSymbol, IrSimpleFunctionSymbol>
|
||||||
|
) : IrElementTransformerVoid() {
|
||||||
override fun visitFile(declaration: IrFile): IrFile {
|
override fun visitFile(declaration: IrFile): IrFile {
|
||||||
declaration.transformChildrenVoid()
|
declaration.transformChildrenVoid()
|
||||||
declaration.transformDeclarationsFlat { it.transformCallableReference() }
|
declaration.transformDeclarationsFlat { it.transformCallableReference() }
|
||||||
@@ -100,7 +128,17 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceWithFactory(lambdaClass: IrClass): List<IrDeclaration> {
|
private fun replaceWithFactory(lambdaClass: IrClass): List<IrDeclaration> {
|
||||||
return buildFactoryFunction(lambdaClass, ctorToFactoryMap).onEach { it.parent = lambdaClass.parent }
|
val lambdaInfo = LambdaInfo(lambdaClass)
|
||||||
|
|
||||||
|
// Optimization:
|
||||||
|
// If the lambda has no context, we lift it, i.e. instead of generating a factory function that creates lambda objects,
|
||||||
|
// we generate a named free function. The usage of the lambda is then replaced with a reference to the free function.
|
||||||
|
// This allows us to avoid allocating a new object each time the lambda is created.
|
||||||
|
return if (lambdaClass.origin == CallableReferenceLowering.Companion.LAMBDA_IMPL && !lambdaInfo.isSuspendLambda && lambdaClass.fields.none()) {
|
||||||
|
liftLambda(ctorToFreeFunctionMap, lambdaInfo)
|
||||||
|
} else {
|
||||||
|
buildFactoryFunction(ctorToFactoryMap, lambdaInfo)
|
||||||
|
}.onEach { it.parent = lambdaClass.parent }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,47 +251,42 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
return returnStmt.value
|
return returnStmt.value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFactoryBody(
|
private class LambdaInfo(val lambdaClass: IrClass) {
|
||||||
factoryFunction: IrSimpleFunction,
|
|
||||||
lambdaClass: IrClass,
|
|
||||||
newDeclarations: MutableList<IrDeclaration>
|
|
||||||
): IrBlockBody {
|
|
||||||
val invokeFun = lambdaClass.invokeFun!!
|
val invokeFun = lambdaClass.invokeFun!!
|
||||||
val superInvokeFun = invokeFun.overriddenSymbols.first { it.owner.isSuspend == invokeFun.isSuspend }.owner
|
val superInvokeFun = invokeFun.overriddenSymbols.first { it.owner.isSuspend == invokeFun.isSuspend }.owner
|
||||||
val lambdaName = Name.identifier("${lambdaClass.name.asString()}\$lambda")
|
val isSuspendLambda = invokeFun.overriddenSymbols.any { it.owner.isSuspend }
|
||||||
|
|
||||||
val superClass = superInvokeFun.parentAsClass
|
fun createOldToNewInvokeParametersMapping(lambdaDeclaration: IrSimpleFunction) =
|
||||||
val anyNType = context.irBuiltIns.anyNType
|
invokeFun.valueParameters.associateBy({ it.symbol }, { lambdaDeclaration.valueParameters[it.index].symbol })
|
||||||
val lambdaDeclaration = context.irFactory.buildFun {
|
|
||||||
startOffset = invokeFun.startOffset
|
|
||||||
endOffset = invokeFun.endOffset
|
|
||||||
// Since box/unbox is done on declaration side in case of suspend function use the specified type
|
|
||||||
returnType = if (invokeFun.isSuspend) invokeFun.returnType else anyNType
|
|
||||||
visibility = DescriptorVisibilities.LOCAL
|
|
||||||
name = lambdaName
|
|
||||||
isSuspend = invokeFun.isSuspend
|
|
||||||
}
|
|
||||||
|
|
||||||
lambdaDeclaration.parent = factoryFunction
|
fun lambdaInnerClasses() =
|
||||||
|
lambdaClass.declarations.filter { it is IrClass || (it is IrSimpleFunction && it.dispatchReceiverParameter == null) }
|
||||||
|
}
|
||||||
|
|
||||||
lambdaDeclaration.valueParameters = superInvokeFun.valueParameters.mapIndexed { id, vp ->
|
private fun buildFactoryBody(
|
||||||
vp.copyTo(lambdaDeclaration, type = anyNType, name = invokeFun.valueParameters[id].name)
|
factoryFunction: IrSimpleFunction,
|
||||||
}
|
newDeclarations: MutableList<IrDeclaration>,
|
||||||
|
lambdaInfo: LambdaInfo
|
||||||
|
): IrBlockBody {
|
||||||
|
val superClass = lambdaInfo.superInvokeFun.parentAsClass
|
||||||
|
val lambdaName = Name.identifier("${lambdaInfo.lambdaClass.name.asString()}\$lambda")
|
||||||
|
|
||||||
|
val lambdaDeclaration =
|
||||||
|
createLambdaDeclaration(lambdaInfo.invokeFun, lambdaName, factoryFunction, lambdaInfo.superInvokeFun)
|
||||||
|
|
||||||
val statements = ArrayList<IrStatement>(4)
|
val statements = ArrayList<IrStatement>(4)
|
||||||
val isSuspendLambda = invokeFun.overriddenSymbols.any { it.owner.isSuspend }
|
val constructor = lambdaInfo.lambdaClass.declarations.firstNotNullOf { it as? IrConstructor }
|
||||||
val constructor = lambdaClass.declarations.firstNotNullOf { it as? IrConstructor }
|
|
||||||
|
|
||||||
if (isSuspendLambda) {
|
if (lambdaInfo.isSuspendLambda) {
|
||||||
// Due to suspend lambda is a class itself it's not easy to inline it correctly and moreover I see no reason to do so
|
// Due to suspend lambda is a class itself it's not easy to inline it correctly and moreover I see no reason to do so
|
||||||
val lambdaType = lambdaClass.defaultType
|
val lambdaType = lambdaInfo.lambdaClass.defaultType
|
||||||
val instanceVal = JsIrBuilder.buildVar(lambdaType, factoryFunction, "i").apply {
|
val instanceVal = JsIrBuilder.buildVar(lambdaType, factoryFunction, "i").apply {
|
||||||
val newCtorCall = IrConstructorCallImpl(
|
val newCtorCall = IrConstructorCallImpl(
|
||||||
lambdaClass.startOffset,
|
lambdaInfo.lambdaClass.startOffset,
|
||||||
lambdaClass.endOffset,
|
lambdaInfo.lambdaClass.endOffset,
|
||||||
lambdaType,
|
lambdaType,
|
||||||
constructor.symbol,
|
constructor.symbol,
|
||||||
lambdaClass.typeParameters.size,
|
lambdaInfo.lambdaClass.typeParameters.size,
|
||||||
constructor.typeParameters.size,
|
constructor.typeParameters.size,
|
||||||
constructor.valueParameters.size
|
constructor.valueParameters.size
|
||||||
)
|
)
|
||||||
@@ -267,31 +300,25 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
|
|
||||||
statements.add(instanceVal)
|
statements.add(instanceVal)
|
||||||
|
|
||||||
lambdaDeclaration.body = buildLambdaBody(instanceVal, lambdaDeclaration, invokeFun)
|
lambdaDeclaration.body = buildLambdaBody(instanceVal, lambdaDeclaration, lambdaInfo.invokeFun)
|
||||||
|
|
||||||
newDeclarations.add(lambdaClass)
|
newDeclarations.add(lambdaInfo.lambdaClass)
|
||||||
} else {
|
} else {
|
||||||
val fieldToParameterMapping = capturedFieldsToParametersMap(constructor, factoryFunction)
|
val fieldToParameterMapping = capturedFieldsToParametersMap(constructor, factoryFunction)
|
||||||
val oldToNewInvokeParametersMapping = mutableMapOf<IrValueParameterSymbol, IrValueParameterSymbol>()
|
val oldToNewInvokeParametersMapping = lambdaInfo.createOldToNewInvokeParametersMapping(lambdaDeclaration)
|
||||||
invokeFun.valueParameters.forEach {
|
|
||||||
oldToNewInvokeParametersMapping[it.symbol] = lambdaDeclaration.valueParameters[it.index].symbol
|
|
||||||
}
|
|
||||||
lambdaDeclaration.body =
|
lambdaDeclaration.body =
|
||||||
inlineLambdaBody(lambdaDeclaration, invokeFun, oldToNewInvokeParametersMapping, fieldToParameterMapping)
|
inlineLambdaBody(lambdaDeclaration, lambdaInfo.invokeFun, oldToNewInvokeParametersMapping, fieldToParameterMapping)
|
||||||
|
|
||||||
// lambdas could contain another lambdas and local classes in so let do not lose them
|
// lambdas can contain another lambdas and local classes in so let's not lose them
|
||||||
val lambdaInnerClasses =
|
newDeclarations.addAll(lambdaInfo.lambdaInnerClasses())
|
||||||
lambdaClass.declarations.filter { it is IrClass || (it is IrSimpleFunction && it.dispatchReceiverParameter == null) }
|
|
||||||
|
|
||||||
newDeclarations.addAll(lambdaInnerClasses)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val lambdaType = lambdaClass.superTypes.single { it.classifierOrNull === superClass.symbol }
|
val lambdaType = lambdaInfo.lambdaClass.superTypes.single { it.classifierOrNull === superClass.symbol }
|
||||||
val functionExpression = lambdaClass.run {
|
val functionExpression = lambdaInfo.lambdaClass.run {
|
||||||
IrFunctionExpressionImpl(startOffset, endOffset, lambdaType, lambdaDeclaration, JsStatementOrigins.CALLABLE_REFERENCE_CREATE)
|
IrFunctionExpressionImpl(startOffset, endOffset, lambdaType, lambdaDeclaration, JsStatementOrigins.CALLABLE_REFERENCE_CREATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nameGetter = context.mapping.reflectedNameAccessor[lambdaClass]
|
val nameGetter = context.mapping.reflectedNameAccessor[lambdaInfo.lambdaClass]
|
||||||
|
|
||||||
if (nameGetter != null || lambdaDeclaration.isSuspend) {
|
if (nameGetter != null || lambdaDeclaration.isSuspend) {
|
||||||
val tmpVar = JsIrBuilder.buildVar(functionExpression.type, factoryFunction, "l", initializer = functionExpression)
|
val tmpVar = JsIrBuilder.buildVar(functionExpression.type, factoryFunction, "l", initializer = functionExpression)
|
||||||
@@ -326,23 +353,48 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
statements.add(JsIrBuilder.buildReturn(factoryFunction.symbol, functionExpression, context.irBuiltIns.nothingType))
|
statements.add(JsIrBuilder.buildReturn(factoryFunction.symbol, functionExpression, context.irBuiltIns.nothingType))
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.irFactory.createBlockBody(lambdaClass.startOffset, lambdaClass.endOffset, statements)
|
return context.irFactory.createBlockBody(lambdaInfo.lambdaClass.startOffset, lambdaInfo.lambdaClass.endOffset, statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLambdaDeclaration(
|
||||||
|
invokeFun: IrSimpleFunction,
|
||||||
|
lambdaName: Name,
|
||||||
|
parent: IrDeclarationParent,
|
||||||
|
superInvokeFun: IrSimpleFunction
|
||||||
|
): IrSimpleFunction {
|
||||||
|
val anyNType = context.irBuiltIns.anyNType
|
||||||
|
val lambdaDeclaration = context.irFactory.buildFun {
|
||||||
|
startOffset = invokeFun.startOffset
|
||||||
|
endOffset = invokeFun.endOffset
|
||||||
|
// Since box/unbox is done on declaration side in case of suspend function use the specified type
|
||||||
|
returnType = if (invokeFun.isSuspend) invokeFun.returnType else anyNType
|
||||||
|
visibility = DescriptorVisibilities.LOCAL
|
||||||
|
name = lambdaName
|
||||||
|
isSuspend = invokeFun.isSuspend
|
||||||
|
}
|
||||||
|
|
||||||
|
lambdaDeclaration.parent = parent
|
||||||
|
|
||||||
|
lambdaDeclaration.valueParameters = superInvokeFun.valueParameters.mapIndexed { id, vp ->
|
||||||
|
vp.copyTo(lambdaDeclaration, type = anyNType, name = invokeFun.valueParameters[id].name)
|
||||||
|
}
|
||||||
|
return lambdaDeclaration
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFactoryFunction(
|
private fun buildFactoryFunction(
|
||||||
lambdaClass: IrClass,
|
ctorToFactoryMap: MutableMap<IrConstructorSymbol, IrSimpleFunctionSymbol>,
|
||||||
ctorToFactoryMap: MutableMap<IrConstructorSymbol, IrSimpleFunctionSymbol>
|
lambdaInfo: LambdaInfo
|
||||||
): List<IrDeclaration> {
|
): List<IrDeclaration> {
|
||||||
val newDeclarations = mutableListOf<IrDeclaration>()
|
val newDeclarations = mutableListOf<IrDeclaration>()
|
||||||
val constructor = lambdaClass.declarations.single { it is IrConstructor } as IrConstructor
|
val constructor = lambdaInfo.lambdaClass.constructors.single()
|
||||||
|
|
||||||
val factoryDeclaration = context.irFactory.stageController.restrictTo(lambdaClass) {
|
val factoryDeclaration = context.irFactory.stageController.restrictTo(lambdaInfo.lambdaClass) {
|
||||||
context.irFactory.buildFun {
|
context.irFactory.buildFun {
|
||||||
startOffset = lambdaClass.startOffset
|
startOffset = lambdaInfo.lambdaClass.startOffset
|
||||||
endOffset = lambdaClass.endOffset
|
endOffset = lambdaInfo.lambdaClass.endOffset
|
||||||
visibility = lambdaClass.visibility
|
visibility = lambdaInfo.lambdaClass.visibility
|
||||||
returnType = lambdaClass.defaultType
|
returnType = lambdaInfo.lambdaClass.defaultType
|
||||||
name = lambdaClass.name
|
name = lambdaInfo.lambdaClass.name
|
||||||
origin = JsStatementOrigins.FACTORY_ORIGIN
|
origin = JsStatementOrigins.FACTORY_ORIGIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,7 +407,7 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
factoryDeclaration.body = buildFactoryBody(factoryDeclaration, lambdaClass, newDeclarations)
|
factoryDeclaration.body = buildFactoryBody(factoryDeclaration, newDeclarations, lambdaInfo)
|
||||||
|
|
||||||
newDeclarations.add(factoryDeclaration)
|
newDeclarations.add(factoryDeclaration)
|
||||||
ctorToFactoryMap[constructor.symbol] = factoryDeclaration.symbol
|
ctorToFactoryMap[constructor.symbol] = factoryDeclaration.symbol
|
||||||
@@ -363,6 +415,38 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
return newDeclarations
|
return newDeclarations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces a contextless lambda class with a free function.
|
||||||
|
*/
|
||||||
|
private fun liftLambda(
|
||||||
|
ctorToFreeFunctionMap: MutableMap<IrConstructorSymbol, IrSimpleFunctionSymbol>,
|
||||||
|
lambdaInfo: LambdaInfo
|
||||||
|
): List<IrDeclaration> {
|
||||||
|
val constructor = lambdaInfo.lambdaClass.constructors.single()
|
||||||
|
val newDeclarations = mutableListOf<IrDeclaration>()
|
||||||
|
val freeFunctionDeclaration = createLambdaDeclaration(
|
||||||
|
lambdaInfo.invokeFun,
|
||||||
|
lambdaInfo.lambdaClass.name,
|
||||||
|
lambdaInfo.lambdaClass.parent,
|
||||||
|
lambdaInfo.superInvokeFun
|
||||||
|
)
|
||||||
|
|
||||||
|
freeFunctionDeclaration.body = inlineLambdaBody(
|
||||||
|
freeFunctionDeclaration,
|
||||||
|
lambdaInfo.invokeFun,
|
||||||
|
lambdaInfo.createOldToNewInvokeParametersMapping(freeFunctionDeclaration),
|
||||||
|
emptyMap()
|
||||||
|
)
|
||||||
|
|
||||||
|
newDeclarations.add(freeFunctionDeclaration)
|
||||||
|
|
||||||
|
// lambdas can contain another lambdas and local classes in so let's not lose them
|
||||||
|
newDeclarations.addAll(lambdaInfo.lambdaInnerClasses())
|
||||||
|
|
||||||
|
ctorToFreeFunctionMap[constructor.symbol] = freeFunctionDeclaration.symbol
|
||||||
|
|
||||||
|
return newDeclarations
|
||||||
|
}
|
||||||
|
|
||||||
private fun setDynamicProperty(r: IrValueSymbol, property: String, value: IrExpression): IrStatement {
|
private fun setDynamicProperty(r: IrValueSymbol, property: String, value: IrExpression): IrStatement {
|
||||||
return IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.unitType, IrDynamicOperator.EQ).apply {
|
return IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.unitType, IrDynamicOperator.EQ).apply {
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// CHECK_CASES_COUNT: function=bar1_u51tkt$ count=3 TARGET_BACKENDS=JS
|
// CHECK_CASES_COUNT: function=bar1_u51tkt$ count=3 TARGET_BACKENDS=JS
|
||||||
// CHECK_IF_COUNT: function=bar1_u51tkt$ count=0 TARGET_BACKENDS=JS
|
// CHECK_IF_COUNT: function=bar1_u51tkt$ count=0 TARGET_BACKENDS=JS
|
||||||
// CHECK_CASES_COUNT: function=A$bar2$lambda count=3 TARGET_BACKENDS=JS
|
// CHECK_CASES_COUNT: function=A$bar2$lambda count=3 TARGET_BACKENDS=JS
|
||||||
// CHECK_CASES_COUNT: function=A$bar2$lambda count=0 IGNORED_BACKENDS=JS
|
// CHECK_CASES_COUNT: function=A$bar2$lambda count=4 IGNORED_BACKENDS=JS
|
||||||
// CHECK_IF_COUNT: function=A$bar2$lambda count=0
|
// CHECK_IF_COUNT: function=A$bar2$lambda count=0
|
||||||
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|||||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ inline fun inlineFun(crossinline inlineLambda: () -> String = { "OK" }, noinline
|
|||||||
|
|
||||||
// FILE: 2.kt
|
// FILE: 2.kt
|
||||||
// CHECK_CALLED_IN_SCOPE: function=inlineFun$lambda_0 scope=box TARGET_BACKENDS=JS
|
// CHECK_CALLED_IN_SCOPE: function=inlineFun$lambda_0 scope=box TARGET_BACKENDS=JS
|
||||||
// CHECK_CALLED_IN_SCOPE: function=box$lambda scope=box IGNORED_BACKENDS=JS
|
// HAS_NO_CAPTURED_VARS: function=box except=box$lambda IGNORED_BACKENDS=JS
|
||||||
import test.*
|
import test.*
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ fun call(lambda: () -> String ) = lambda()
|
|||||||
|
|
||||||
// FILE: 2.kt
|
// FILE: 2.kt
|
||||||
// CHECK_CALLED_IN_SCOPE: function=inlineFun$lambda scope=box TARGET_BACKENDS=JS
|
// CHECK_CALLED_IN_SCOPE: function=inlineFun$lambda scope=box TARGET_BACKENDS=JS
|
||||||
// CHECK_CALLED_IN_SCOPE: function=box$lambda scope=box IGNORED_BACKENDS=JS
|
// HAS_NO_CAPTURED_VARS: function=box except=box$lambda;call IGNORED_BACKENDS=JS
|
||||||
// CHECK_CALLED_IN_SCOPE: function=call scope=box
|
// CHECK_CALLED_IN_SCOPE: function=call scope=box
|
||||||
import test.*
|
import test.*
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -1,7 +1,9 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1284
|
// EXPECTED_REACHABLE_NODES: 1284
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
// CHECK_CALLED_IN_SCOPE: scope=multiplyBy2 function=multiplyBy2$lambda
|
// CHECK_FUNCTION_EXISTS: multiplyBy2$lambda
|
||||||
|
// CHECK_CALLED_IN_SCOPE: scope=multiplyBy2 function=multiplyBy2$lambda TARGET_BACKENDS=JS
|
||||||
|
// HAS_NO_CAPTURED_VARS: function=multiplyBy2 except=multiplyBy2$lambda
|
||||||
// CHECK_NOT_CALLED_IN_SCOPE: scope=multiplyBy2 function=multiplyBy2$lambda_0
|
// CHECK_NOT_CALLED_IN_SCOPE: scope=multiplyBy2 function=multiplyBy2$lambda_0
|
||||||
// CHECK_NOT_CALLED_IN_SCOPE: scope=multiplyBy2 function=run
|
// CHECK_NOT_CALLED_IN_SCOPE: scope=multiplyBy2 function=run
|
||||||
|
|
||||||
@@ -19,4 +21,4 @@ fun box(): String {
|
|||||||
assertEquals(8, multiplyBy2(4))
|
assertEquals(8, multiplyBy2(4))
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -1,7 +1,9 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1284
|
// EXPECTED_REACHABLE_NODES: 1284
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
// CHECK_CALLED_IN_SCOPE: scope=multiplyBy2 function=multiplyBy2$lambda
|
// CHECK_FUNCTION_EXISTS: multiplyBy2$lambda
|
||||||
|
// CHECK_CALLED_IN_SCOPE: scope=multiplyBy2 function=multiplyBy2$lambda TARGET_BACKENDS=JS
|
||||||
|
// HAS_NO_CAPTURED_VARS: function=multiplyBy2 except=multiplyBy2$lambda
|
||||||
// CHECK_NOT_CALLED_IN_SCOPE: scope=multiplyBy2 function=run
|
// CHECK_NOT_CALLED_IN_SCOPE: scope=multiplyBy2 function=run
|
||||||
|
|
||||||
internal inline fun <T> run(noinline func: (T) -> T, arg: T): T {
|
internal inline fun <T> run(noinline func: (T) -> T, arg: T): T {
|
||||||
@@ -18,4 +20,4 @@ fun box(): String {
|
|||||||
assertEquals(8, multiplyBy2(4))
|
assertEquals(8, multiplyBy2(4))
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user