[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]
  };
```
This commit is contained in:
Roman Artemev
2021-09-21 13:08:51 +07:00
committed by TeamCityServer
parent 76e08356d8
commit fe4a25eba1
2 changed files with 37 additions and 4 deletions
@@ -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)
@@ -323,14 +323,12 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
val newDeclarations = mutableListOf<IrDeclaration>()
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
}