From fe4a25eba1894e155464df597a50c909e0abf7a2 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Tue, 21 Sep 2021 13:08:51 +0700 Subject: [PATCH] [JS IR] Use context-dependent names for lambdas Partially fix KT-46525 Now the code like ``` fun foo(a: () -> Unit) {} interface A { fun run() } fun main() { foo { println() } val a = object : A { override fun run() { TODO() } } } ``` is being compiled into ``` function foo(a) { } function A() { } A.$metadata$ = { simpleName: 'A', kind: 'interface', interfaces: [] }; function main() { foo(main$lambda()); var a = new _no_name_provided_(); } function main$lambda() { return function () { println(); }; } // TODO function _no_name_provided_() { } _no_name_provided_.$metadata$ = { kind: 'class', interfaces: [A] }; ``` --- .../js/lower/CallableReferenceLowering.kt | 37 ++++++++++++++++++- .../lower/InteropCallableReferenceLowering.kt | 4 +- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt index 91e02b6bc71..38667b7db1e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt @@ -123,6 +123,41 @@ class CallableReferenceLowering(private val context: CommonBackendContext) : Bod private val superFunctionInterface = reference.type.classOrNull?.owner ?: error("Expected functional type") private val isKReference = superFunctionInterface.name.identifier[0] == 'K' + private fun StringBuilder.collectNamesForLambda(d: IrDeclarationWithName) { + val parent = d.parent + + if (parent is IrPackageFragment) { + append(d.name.asString()) + return + } + + collectNamesForLambda(parent as IrDeclarationWithName) + + if (d is IrAnonymousInitializer) return + + fun IrDeclaration.isLambdaFun(): Boolean = origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA + + when { + d.isLambdaFun() -> { + append('$') + if (d is IrSimpleFunction && d.isSuspend) append('s') + append("lambda") + } + d.name == SpecialNames.NO_NAME_PROVIDED -> append("\$o") + else -> { + append('$') + append(d.name.asString()) + } + } + } + + private fun makeContextDependentName(): Name { + val sb = StringBuilder() + sb.collectNamesForLambda(function) + if (!isLambda) sb.append("\$ref") + return Name.identifier(sb.toString()) + } + private fun buildReferenceClass(): IrClass { return context.irFactory.buildClass { setSourceRange(reference) @@ -130,7 +165,7 @@ class CallableReferenceLowering(private val context: CommonBackendContext) : Bod // A callable reference results in a synthetic class, while a lambda is not synthetic. // We don't produce GENERATED_SAM_IMPLEMENTATION, which is always synthetic. origin = if (isKReference || !isLambda) FUNCTION_REFERENCE_IMPL else LAMBDA_IMPL - name = SpecialNames.NO_NAME_PROVIDED + name = makeContextDependentName() }.apply { superTypes = listOf(superClass, reference.type) // if (samSuperType == null) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt index b033358857d..7690183912b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt @@ -323,14 +323,12 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo val newDeclarations = mutableListOf() val constructor = lambdaClass.declarations.single { it is IrConstructor } as IrConstructor - val factoryName = Name.identifier("${lambdaClass.name.asString()}\$factory") - val factoryDeclaration = context.irFactory.buildFun { startOffset = lambdaClass.startOffset endOffset = lambdaClass.endOffset visibility = lambdaClass.visibility returnType = lambdaClass.defaultType - name = factoryName + name = lambdaClass.name origin = JsStatementOrigins.FACTORY_ORIGIN }