[JS IR BE] Support in callable references

* default params
 * vararg
Minor refactoring
This commit is contained in:
Roman Artemev
2018-10-27 19:32:04 +03:00
committed by romanart
parent fb709cae92
commit 12848d4eff
3 changed files with 133 additions and 117 deletions
@@ -52,6 +52,8 @@ val IrDeclaration.parentsWithSelf: Sequence<IrDeclarationParent>
val IrDeclaration.parents: Sequence<IrDeclarationParent> val IrDeclaration.parents: Sequence<IrDeclarationParent>
get() = parentsWithSelf.drop(1) get() = parentsWithSelf.drop(1)
object BOUND_VALUE_PARAMETER: IrDeclarationOriginImpl("BOUND_VALUE_PARAMETER")
class LocalDeclarationsLowering( class LocalDeclarationsLowering(
val context: BackendContext, val context: BackendContext,
val localNameProvider: LocalNameProvider = LocalNameProvider.DEFAULT, val localNameProvider: LocalNameProvider = LocalNameProvider.DEFAULT,
@@ -540,7 +542,7 @@ class LocalDeclarationsLowering(
val parameterDescriptor = WrappedValueParameterDescriptor() val parameterDescriptor = WrappedValueParameterDescriptor()
val p = capturedValue.owner val p = capturedValue.owner
IrValueParameterImpl( IrValueParameterImpl(
p.startOffset, p.endOffset, p.origin, IrValueParameterSymbolImpl(parameterDescriptor), p.startOffset, p.endOffset, BOUND_VALUE_PARAMETER, IrValueParameterSymbolImpl(parameterDescriptor),
suggestNameForCapturedValue(p), i, p.type, null, false, false suggestNameForCapturedValue(p), i, p.type, null, false, false
).also { ).also {
parameterDescriptor.bind(it) parameterDescriptor.bind(it)
@@ -105,11 +105,7 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
UnitMaterializationLowering(this).lower(moduleFragment) UnitMaterializationLowering(this).lower(moduleFragment)
EnumClassLowering(this).runOnFilesPostfix(moduleFragment) EnumClassLowering(this).runOnFilesPostfix(moduleFragment)
EnumUsageLowering(this).lower(moduleFragment) EnumUsageLowering(this).lower(moduleFragment)
VarargLowering(this).lower(moduleFragment)
LateinitLowering(this, true).lower(moduleFragment) LateinitLowering(this, true).lower(moduleFragment)
DefaultArgumentStubGenerator(this).runOnFilesPostfix(moduleFragment)
DefaultParameterInjector(this).runOnFilesPostfix(moduleFragment)
DefaultParameterCleaner(this).runOnFilesPostfix(moduleFragment)
SharedVariablesLowering(this).runOnFilesPostfix(moduleFragment) SharedVariablesLowering(this).runOnFilesPostfix(moduleFragment)
ReturnableBlockLowering(this).lower(moduleFragment) ReturnableBlockLowering(this).lower(moduleFragment)
LocalDelegatedPropertiesLowering().lower(moduleFragment) LocalDelegatedPropertiesLowering().lower(moduleFragment)
@@ -117,6 +113,11 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
InnerClassesLowering(this).runOnFilesPostfix(moduleFragment) InnerClassesLowering(this).runOnFilesPostfix(moduleFragment)
InnerClassConstructorCallsLowering(this).runOnFilesPostfix(moduleFragment) InnerClassConstructorCallsLowering(this).runOnFilesPostfix(moduleFragment)
SuspendFunctionsLowering(this).lower(moduleFragment) SuspendFunctionsLowering(this).lower(moduleFragment)
CallableReferenceLowering(this).lower(moduleFragment)
DefaultArgumentStubGenerator(this).runOnFilesPostfix(moduleFragment)
DefaultParameterInjector(this).runOnFilesPostfix(moduleFragment)
DefaultParameterCleaner(this).runOnFilesPostfix(moduleFragment)
VarargLowering(this).lower(moduleFragment)
PropertiesLowering().lower(moduleFragment) PropertiesLowering().lower(moduleFragment)
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilesPostfix(moduleFragment) InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilesPostfix(moduleFragment)
MultipleCatchesLowering(this).lower(moduleFragment) MultipleCatchesLowering(this).lower(moduleFragment)
@@ -129,7 +130,6 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
constructorRedirectorLowering.runOnFilesPostfix(moduleFragment) constructorRedirectorLowering.runOnFilesPostfix(moduleFragment)
} }
CallableReferenceLowering(this).lower(moduleFragment)
ClassReferenceLowering(this).lower(moduleFragment) ClassReferenceLowering(this).lower(moduleFragment)
PrimitiveCompanionLowering(this).lower(moduleFragment) PrimitiveCompanionLowering(this).lower(moduleFragment)
@@ -8,28 +8,36 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedTypeParameterDescriptor import org.jetbrains.kotlin.backend.common.descriptors.WrappedTypeParameterDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
import org.jetbrains.kotlin.backend.common.utils.isSubtypeOf
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.asString
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.toIrType import org.jetbrains.kotlin.ir.types.toIrType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
data class CallableReferenceKey( data class CallableReferenceKey(
val declaration: IrFunction, val declaration: IrFunction,
val hasDispatchReference: Boolean, val hasDispatchReceiver: Boolean,
val hasExtensionReceiver: Boolean val hasExtensionReceiver: Boolean,
val signature: String
) )
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface // TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
@@ -37,7 +45,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
private val callableNameConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KCALLABLE_NAME) private val callableNameConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KCALLABLE_NAME)
private val getterConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_GET) private val getterConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_GET)
private val setterConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_SET) private val setterConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_SET)
private val callableToGetterFunction = context.callableReferencesCache private val callableToFactoryFunction = context.callableReferencesCache
private val newDeclarations = mutableListOf<IrDeclaration>() private val newDeclarations = mutableListOf<IrDeclaration>()
private val implicitDeclarationFile = context.implicitDeclarationFile private val implicitDeclarationFile = context.implicitDeclarationFile
@@ -49,7 +57,12 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
} }
private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) = private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) =
CallableReferenceKey(declaration, reference.dispatchReceiver != null, reference.extensionReceiver != null) CallableReferenceKey(
declaration,
reference.dispatchReceiver != null,
reference.extensionReceiver != null,
reference.type.asString()
)
inner class CallableReferenceLowerTransformer : IrElementTransformerVoid() { inner class CallableReferenceLowerTransformer : IrElementTransformerVoid() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
@@ -57,7 +70,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
val declaration = expression.symbol.owner val declaration = expression.symbol.owner
if (declaration.origin == JsIrBackendContext.callableClosureOrigin) return expression if (declaration.origin == JsIrBackendContext.callableClosureOrigin) return expression
val key = makeCallableKey(declaration, expression) val key = makeCallableKey(declaration, expression)
val factory = callableToGetterFunction.getOrPut(key) { lowerKFunctionReference(declaration, expression) } val factory = callableToFactoryFunction.getOrPut(key) { lowerKFunctionReference(declaration, expression) }
return redirectToFunction(expression, factory) return redirectToFunction(expression, factory)
} }
@@ -65,14 +78,14 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
val declaration = expression.getter!!.owner val declaration = expression.getter!!.owner
val key = makeCallableKey(declaration, expression) val key = makeCallableKey(declaration, expression)
val factory = callableToGetterFunction.getOrPut(key) { lowerKPropertyReference(declaration, expression) } val factory = callableToFactoryFunction.getOrPut(key) { lowerKPropertyReference(declaration, expression) }
return redirectToFunction(expression, factory) return redirectToFunction(expression, factory)
} }
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression { override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
val key = makeCallableKey(expression.getter.owner, expression) val key = makeCallableKey(expression.getter.owner, expression)
val factory = callableToGetterFunction.getOrPut(key) { lowerLocalKPropertyReference(expression) } val factory = callableToFactoryFunction.getOrPut(key) { lowerLocalKPropertyReference(expression) }
return redirectToFunction(expression, factory) return redirectToFunction(expression, factory)
} }
} }
@@ -97,8 +110,8 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
} }
} }
private fun createFunctionClosureGetterName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KReferenceGet") private fun createFunctionFactoryName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KFunctionFactory")
private fun createPropertyClosureGetterName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KPropertyGet") private fun createPropertyFactoryName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KPropertyFactory")
private fun createClosureInstanceName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KReferenceClosure") private fun createClosureInstanceName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KReferenceClosure")
private fun createHelperFunctionName(declaration: IrDeclaration, suffix: String): String { private fun createHelperFunctionName(declaration: IrDeclaration, suffix: String): String {
@@ -130,7 +143,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
else -> TODO("Unexpected declaration type") else -> TODO("Unexpected declaration type")
} }
private fun lowerKFunctionReference(declaration: IrFunction, functionReference: IrFunctionReference ): IrSimpleFunction { private fun lowerKFunctionReference(declaration: IrFunction, functionReference: IrFunctionReference): IrSimpleFunction {
// transform // transform
// x = Foo::bar -> // x = Foo::bar ->
// x = Foo_bar_KreferenceGet(c1: closure$C1, c2: closure$C2) : KFunctionN<Foo, T2, ..., TN, TReturn> { // x = Foo_bar_KreferenceGet(c1: closure$C1, c2: closure$C2) : KFunctionN<Foo, T2, ..., TN, TReturn> {
@@ -145,13 +158,13 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
// KFunctionN<Foo, T2, ..., TN, TReturn>, arguments.size = N + 1 // KFunctionN<Foo, T2, ..., TN, TReturn>, arguments.size = N + 1
val refGetFunction = buildGetFunction(declaration, functionReference, createFunctionClosureGetterName(declaration)) val factoryFunction = buildFactoryFunction(declaration, functionReference, createFunctionFactoryName(declaration))
val refClosureFunction = buildClosureFunction(declaration, refGetFunction, functionReference) val closureFunction = buildClosureFunction(declaration, factoryFunction, functionReference, functionReference.type.arity)
val additionalDeclarations = generateGetterBodyWithGuard(refGetFunction) { val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
val irClosureReference = JsIrBuilder.buildFunctionReference(functionReference.type, refClosureFunction.symbol) val irClosureReference = JsIrBuilder.buildFunctionReference(functionReference.type, closureFunction.symbol)
val irVar = JsIrBuilder.buildVar(irClosureReference.type, refGetFunction, initializer = irClosureReference) val irVar = JsIrBuilder.buildVar(irClosureReference.type, factoryFunction, initializer = irClosureReference)
// TODO: fill other fields of callable reference (returnType, parameters, isFinal, etc.) // TODO: fill other fields of callable reference (returnType, parameters, isFinal, etc.)
val irSetName = JsIrBuilder.buildCall(context.intrinsics.jsSetJSField.symbol).apply { val irSetName = JsIrBuilder.buildCall(context.intrinsics.jsSetJSField.symbol).apply {
@@ -159,12 +172,12 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
putValueArgument(1, callableNameConst) putValueArgument(1, callableNameConst)
putValueArgument(2, JsIrBuilder.buildString(context.irBuiltIns.stringType, getReferenceName(declaration))) putValueArgument(2, JsIrBuilder.buildString(context.irBuiltIns.stringType, getReferenceName(declaration)))
} }
Pair(listOf(refClosureFunction, irVar, irSetName), irVar.symbol) Pair(listOf(closureFunction, irVar, irSetName), irVar.symbol)
} }
newDeclarations += additionalDeclarations + refGetFunction newDeclarations += additionalDeclarations + factoryFunction
return refGetFunction return factoryFunction
} }
private fun lowerKPropertyReference(getterDeclaration: IrSimpleFunction, propertyReference: IrPropertyReference): IrSimpleFunction { private fun lowerKPropertyReference(getterDeclaration: IrSimpleFunction, propertyReference: IrPropertyReference): IrSimpleFunction {
@@ -187,19 +200,20 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
// return $cache$ // return $cache$
// } // }
val getterName = createPropertyClosureGetterName(getterDeclaration.correspondingProperty!!) val arity = propertyReference.type.arity
val refGetFunction = buildGetFunction(propertyReference.getter!!.owner, propertyReference, getterName) val factoryName = createPropertyFactoryName(getterDeclaration.correspondingProperty!!)
val factoryFunction = buildFactoryFunction(propertyReference.getter!!.owner, propertyReference, factoryName)
val getterFunction = propertyReference.getter?.let { buildClosureFunction(it.owner, refGetFunction, propertyReference) }!! val getterFunction = propertyReference.getter?.let { buildClosureFunction(it.owner, factoryFunction, propertyReference, arity) }!!
val setterFunction = propertyReference.setter?.let { buildClosureFunction(it.owner, refGetFunction, propertyReference) } val setterFunction = propertyReference.setter?.let { buildClosureFunction(it.owner, factoryFunction, propertyReference, arity + 1) }
val additionalDeclarations = generateGetterBodyWithGuard(refGetFunction) { val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
val statements = mutableListOf<IrStatement>(getterFunction) val statements = mutableListOf<IrStatement>(getterFunction)
val getterFunctionType = context.builtIns.getFunction(getterFunction.valueParameters.size + 1) val getterFunctionType = context.builtIns.getFunction(getterFunction.valueParameters.size + 1)
val type = getterFunctionType.toIrType(symbolTable = context.symbolTable) val type = getterFunctionType.toIrType(symbolTable = context.symbolTable)
val irGetReference = JsIrBuilder.buildFunctionReference(type, getterFunction.symbol) val irGetReference = JsIrBuilder.buildFunctionReference(type, getterFunction.symbol)
val irVar = JsIrBuilder.buildVar(type, refGetFunction, initializer = irGetReference) val irVar = JsIrBuilder.buildVar(type, factoryFunction, initializer = irGetReference)
statements += irVar statements += irVar
@@ -228,8 +242,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
putValueArgument(0, JsIrBuilder.buildGetValue(irVar.symbol)) putValueArgument(0, JsIrBuilder.buildGetValue(irVar.symbol))
putValueArgument(1, callableNameConst) putValueArgument(1, callableNameConst)
putValueArgument( putValueArgument(
2, 2, JsIrBuilder.buildString(
JsIrBuilder.buildString(
context.irBuiltIns.stringType, context.irBuiltIns.stringType,
getReferenceName(getterDeclaration.correspondingProperty!!) getReferenceName(getterDeclaration.correspondingProperty!!)
) )
@@ -239,9 +252,9 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
Pair(statements, irVar.symbol) Pair(statements, irVar.symbol)
} }
newDeclarations += additionalDeclarations + refGetFunction newDeclarations += additionalDeclarations + factoryFunction
return refGetFunction return factoryFunction
} }
private fun lowerLocalKPropertyReference(propertyReference: IrLocalDelegatedPropertyReference): IrSimpleFunction { private fun lowerLocalKPropertyReference(propertyReference: IrLocalDelegatedPropertyReference): IrSimpleFunction {
@@ -259,18 +272,19 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
// return $cache$ // return $cache$
// } // }
val arity = propertyReference.type.arity
val declaration = propertyReference.delegate.owner val declaration = propertyReference.delegate.owner
val getterName = createPropertyClosureGetterName(declaration) val factoryName = createPropertyFactoryName(declaration)
val refGetFunction = buildGetFunction(propertyReference.getter.owner, propertyReference, getterName) val factoryFunction = buildFactoryFunction(propertyReference.getter.owner, propertyReference, factoryName)
val getterFunction = buildClosureFunction(context.irBuiltIns.throwIseFun, refGetFunction, propertyReference) val closureFunction = buildClosureFunction(context.irBuiltIns.throwIseFun, factoryFunction, propertyReference, arity)
val additionalDeclarations = generateGetterBodyWithGuard(refGetFunction) { val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
val statements = mutableListOf<IrStatement>(getterFunction) val statements = mutableListOf<IrStatement>(closureFunction)
val getterFunctionType = context.builtIns.getFunction(getterFunction.valueParameters.size + 1) val getterFunctionType = context.builtIns.getFunction(closureFunction.valueParameters.size + 1)
val type = getterFunctionType.toIrType(symbolTable = context.symbolTable) val type = getterFunctionType.toIrType(symbolTable = context.symbolTable)
val irGetReference = JsIrBuilder.buildFunctionReference(type, getterFunction.symbol) val irGetReference = JsIrBuilder.buildFunctionReference(type, closureFunction.symbol)
val irVar = JsIrBuilder.buildVar(type, refGetFunction, initializer = irGetReference) val irVar = JsIrBuilder.buildVar(type, factoryFunction, initializer = irGetReference)
val irVarSymbol = irVar.symbol val irVarSymbol = irVar.symbol
statements += irVar statements += irVar
@@ -290,13 +304,13 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
Pair(statements, irVarSymbol) Pair(statements, irVarSymbol)
} }
newDeclarations += additionalDeclarations + refGetFunction newDeclarations += additionalDeclarations + factoryFunction
return refGetFunction return factoryFunction
} }
private fun generateGetterBodyWithGuard( private fun generateFactoryBodyWithGuard(
getterFunction: IrSimpleFunction, factoryFunction: IrSimpleFunction,
builder: () -> Pair<List<IrStatement>, IrValueSymbol> builder: () -> Pair<List<IrStatement>, IrValueSymbol>
): List<IrDeclaration> { ): List<IrDeclaration> {
@@ -304,16 +318,16 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
val statements = mutableListOf<IrStatement>() val statements = mutableListOf<IrStatement>()
val returnValue: IrExpression val returnValue: IrExpression
val returnStatements: List<IrDeclaration> val returnStatements: List<IrDeclaration>
if (getterFunction.valueParameters.isEmpty()) { if (factoryFunction.valueParameters.isEmpty()) {
// compose cache for 'direct' closure // compose cache for 'direct' closure
// if ($cache$ === null) { // if ($cache$ === null) {
// $cache$ = <body> // $cache$ = <body>
// } // }
// //
val cacheName = "${getterFunction.name}_${Namer.KCALLABLE_CACHE_SUFFIX}" val cacheName = "${factoryFunction.name}_${Namer.KCALLABLE_CACHE_SUFFIX}"
val type = getterFunction.returnType val type = factoryFunction.returnType
val irNull = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) val irNull = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType)
val cacheVar = JsIrBuilder.buildVar(type, getterFunction.parent, cacheName, true, initializer = irNull) val cacheVar = JsIrBuilder.buildVar(type, factoryFunction.parent, cacheName, true, initializer = irNull)
val irCacheValue = JsIrBuilder.buildGetValue(cacheVar.symbol) val irCacheValue = JsIrBuilder.buildGetValue(cacheVar.symbol)
val irIfCondition = JsIrBuilder.buildCall(context.irBuiltIns.eqeqSymbol).apply { val irIfCondition = JsIrBuilder.buildCall(context.irBuiltIns.eqeqSymbol).apply {
@@ -337,17 +351,18 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
returnStatements = emptyList() returnStatements = emptyList()
} }
statements += JsIrBuilder.buildReturn(getterFunction.symbol, returnValue, context.irBuiltIns.nothingType) statements += JsIrBuilder.buildReturn(factoryFunction.symbol, returnValue, context.irBuiltIns.nothingType)
getterFunction.body = JsIrBuilder.buildBlockBody(statements) factoryFunction.body = JsIrBuilder.buildBlockBody(statements)
return returnStatements return returnStatements
} }
private fun generateSignatureForClosure( private fun generateSignatureForClosure(
callable: IrFunction, callable: IrFunction,
getter: IrFunction, factory: IrFunction,
closure: IrSimpleFunction, closure: IrSimpleFunction,
reference: IrCallableReference reference: IrCallableReference,
arity: Int
): List<IrValueParameter> { ): List<IrValueParameter> {
val result = mutableListOf<IrValueParameter>() val result = mutableListOf<IrValueParameter>()
@@ -357,23 +372,24 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
* } * }
* *
* val d = D() * val d = D()
* val reference = d::foo <= captures dispatch receiver * val reference = d::foo <= binds dispatch receiver
* *
* is translated into * is translated into
* *
* function foo_get(d) { <= getter signature(d) * function foo_get(d) { <= factory signature(d)
* return function (a, b, c) { <= result signature (a, b, c) * return function (a, b, c) { <= result signature (a, b, c)
* return d.foo(a, b, c) * return d.foo(a, b, c)
* } * }
* } * }
*/ */
var capturedParams = getter.valueParameters.size var capturedParams = factory.valueParameters.size
val functionSignature = reference.type.arguments.dropLast(1).map { (it as IrTypeProjection).type }.toList()
callable.dispatchReceiverParameter?.let { dispatch -> callable.dispatchReceiverParameter?.let { dispatch ->
if (reference.dispatchReceiver == null) { if (reference.dispatchReceiver == null) {
result.add(JsIrBuilder.buildValueParameter(dispatch.name, result.size, dispatch.type).also { it.parent = closure }) result.add(JsIrBuilder.buildValueParameter(dispatch.name, result.size, dispatch.type).also { it.parent = closure })
} else { } else {
// do not consider implicit receiver in result signature if it was captured // do not add dispatch receiver in result signature if it is bound
capturedParams-- capturedParams--
} }
} }
@@ -387,55 +403,38 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
} }
} }
// pass through value parameters for ((index, param) in (result.size until arity).zip(callable.valueParameters.drop(capturedParams))) {
for (i in capturedParams until callable.valueParameters.size) { val type = if (index < functionSignature.size) functionSignature[index] else param.type
val param = callable.valueParameters[i] val paramName = param.name.run { if (!isSpecial) identifier else "p$index" }
val paramName = param.name.run { if (!isSpecial) identifier else "p$i" } result += JsIrBuilder.buildValueParameter(paramName, result.size, type).also { it.parent = closure }
result += JsIrBuilder.buildValueParameter(paramName, result.size, param.type).also { it.parent = closure }
} }
return result return result
} }
private fun buildGetFunction(declaration: IrFunction, reference: IrCallableReference, getterName: String): IrSimpleFunction { private fun buildFactoryFunction(declaration: IrFunction, reference: IrCallableReference, getterName: String): IrSimpleFunction {
val callableType = reference.type
var kFunctionValueParamsCount = (callableType as? IrSimpleType)?.arguments?.size?.minus(1) ?: 0
if (declaration.dispatchReceiverParameter != null && reference.dispatchReceiver == null) {
kFunctionValueParamsCount--
}
if (declaration.extensionReceiverParameter != null && reference.extensionReceiver == null) {
kFunctionValueParamsCount--
}
assert(kFunctionValueParamsCount >= 0)
// The `getter` function takes only closure parameters // The `getter` function takes only closure parameters
val receivers = mutableListOf<IrValueParameter>() val receivers = mutableListOf<IrValueParameter>()
if (reference.dispatchReceiver != null) {
if (declaration.dispatchReceiverParameter != null) { reference.dispatchReceiver?.let {
receivers += declaration.dispatchReceiverParameter!! // in case outer `this` for inner constructors
} receivers += declaration.dispatchReceiverParameter ?: declaration.valueParameters[0]
}
if (reference.extensionReceiver != null) {
if (declaration.extensionReceiverParameter != null) {
receivers += declaration.extensionReceiverParameter!!
}
} }
val getterValueParameters = receivers + declaration.valueParameters.dropLast(kFunctionValueParamsCount) reference.extensionReceiver?.let {
receivers += declaration.extensionReceiverParameter!!
}
val boundValueParameters = receivers + declaration.valueParameters.filter { it.origin == BOUND_VALUE_PARAMETER }
val refGetDeclaration = JsIrBuilder.buildFunction(getterName, declaration.visibility) val factoryDeclaration = JsIrBuilder.buildFunction(getterName, declaration.visibility)
refGetDeclaration.parent = implicitDeclarationFile factoryDeclaration.parent = implicitDeclarationFile
refGetDeclaration.returnType = callableType factoryDeclaration.returnType = reference.type
for ((i, p) in getterValueParameters.withIndex()) { for ((i, p) in boundValueParameters.withIndex()) {
val descriptor = WrappedValueParameterDescriptor() val descriptor = WrappedValueParameterDescriptor()
refGetDeclaration.valueParameters += IrValueParameterImpl( factoryDeclaration.valueParameters += IrValueParameterImpl(
p.startOffset, p.startOffset,
p.endOffset, p.endOffset,
p.origin, p.origin,
@@ -448,7 +447,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
p.isNoinline p.isNoinline
).also { ).also {
descriptor.bind(it) descriptor.bind(it)
it.parent = refGetDeclaration it.parent = factoryDeclaration
} }
} }
@@ -457,7 +456,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
for (t in typeParameters) { for (t in typeParameters) {
val descriptor = WrappedTypeParameterDescriptor() val descriptor = WrappedTypeParameterDescriptor()
refGetDeclaration.typeParameters += IrTypeParameterImpl( factoryDeclaration.typeParameters += IrTypeParameterImpl(
t.startOffset, t.startOffset,
t.endOffset, t.endOffset,
t.origin, t.origin,
@@ -468,62 +467,77 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
t.variance t.variance
).also { ).also {
descriptor.bind(it) descriptor.bind(it)
it.parent = refGetDeclaration it.parent = factoryDeclaration
} }
} }
return refGetDeclaration return factoryDeclaration
} }
private val IrType.arguments get() = (this as? IrSimpleType)?.arguments ?: emptyList()
private val IrType.arity get() = arguments.size - 1
private fun buildClosureFunction( private fun buildClosureFunction(
declaration: IrFunction, declaration: IrFunction,
refGetFunction: IrSimpleFunction, factoryFunction: IrSimpleFunction,
reference: IrCallableReference reference: IrCallableReference,
arity: Int
): IrFunction { ): IrFunction {
val closureName = createClosureInstanceName(declaration) val closureName = createClosureInstanceName(declaration)
val refClosureDeclaration = val closureFunction =
JsIrBuilder.buildFunction(closureName, Visibilities.LOCAL, origin = JsIrBackendContext.callableClosureOrigin) JsIrBuilder.buildFunction(closureName, Visibilities.LOCAL, origin = JsIrBackendContext.callableClosureOrigin)
.also { it.parent = refGetFunction } .also { it.parent = factoryFunction }
// the params which are passed to closure // the params which are passed to closure
val closureParamDeclarations = generateSignatureForClosure(declaration, refGetFunction, refClosureDeclaration, reference) val boundParamSymbols = factoryFunction.valueParameters.map { it.symbol }
val closureParamSymbols = closureParamDeclarations.map { it.symbol } val unboundParamDeclarations = generateSignatureForClosure(declaration, factoryFunction, closureFunction, reference, arity)
val unboundParamSymbols = unboundParamDeclarations.map { it.symbol }
val returnType = declaration.returnType val returnType = declaration.returnType
refClosureDeclaration.valueParameters += closureParamDeclarations closureFunction.valueParameters += unboundParamDeclarations
refClosureDeclaration.returnType = returnType closureFunction.returnType = returnType
val irCall = JsIrBuilder.buildCall(declaration.symbol, type = returnType) val callTarget = context.ir.defaultParameterDeclarationsCache[declaration] ?: declaration
val irCall = JsIrBuilder.buildCall(callTarget.symbol, type = returnType)
var cp = 0 var cp = 0
var gp = 0 var gp = 0
if (declaration.dispatchReceiverParameter != null) { if (callTarget.dispatchReceiverParameter != null) {
val dispatchReceiverDeclaration = val dispatchReceiverDeclaration =
if (reference.dispatchReceiver != null) refGetFunction.valueParameters[gp++].symbol else closureParamSymbols[cp++] if (reference.dispatchReceiver != null) boundParamSymbols[gp++] else unboundParamSymbols[cp++]
irCall.dispatchReceiver = JsIrBuilder.buildGetValue(dispatchReceiverDeclaration) irCall.dispatchReceiver = JsIrBuilder.buildGetValue(dispatchReceiverDeclaration)
} }
if (declaration.extensionReceiverParameter != null) { if (callTarget.extensionReceiverParameter != null) {
val extensionReceiverDeclaration = val extensionReceiverDeclaration =
if (reference.extensionReceiver != null) refGetFunction.valueParameters[gp++].symbol else closureParamSymbols[cp++] if (reference.extensionReceiver != null) boundParamSymbols[gp++] else unboundParamSymbols[cp++]
irCall.extensionReceiver = JsIrBuilder.buildGetValue(extensionReceiverDeclaration) irCall.extensionReceiver = JsIrBuilder.buildGetValue(extensionReceiverDeclaration)
} }
var j = 0 var j = 0
for (i in gp until refGetFunction.valueParameters.size) { for (i in gp until boundParamSymbols.size) {
irCall.putValueArgument(j++, JsIrBuilder.buildGetValue(refGetFunction.valueParameters[i].symbol)) irCall.putValueArgument(j++, JsIrBuilder.buildGetValue(boundParamSymbols[i]))
} }
for (i in cp until closureParamSymbols.size) { for (i in cp until unboundParamSymbols.size) {
irCall.putValueArgument(j++, JsIrBuilder.buildGetValue(closureParamSymbols[i])) val closureParam = unboundParamSymbols[i].owner
val value = JsIrBuilder.buildGetValue(unboundParamSymbols[i])
val parameter = callTarget.valueParameters[j]
val argument = if (parameter.varargElementType?.let { closureParam.type.isSubtypeOf(it) } == true) {
// fun foo(x: X, y: vararg Y): Z
// val r: (X, Y) -> Z = ::foo
IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter.type, parameter.varargElementType!!, listOf(value))
} else value
irCall.putValueArgument(j++, argument)
} }
val irClosureReturn = JsIrBuilder.buildReturn(refClosureDeclaration.symbol, irCall, context.irBuiltIns.nothingType) val irClosureReturn = JsIrBuilder.buildReturn(closureFunction.symbol, irCall, context.irBuiltIns.nothingType)
refClosureDeclaration.body = JsIrBuilder.buildBlockBody(listOf(irClosureReturn)) closureFunction.body = JsIrBuilder.buildBlockBody(listOf(irClosureReturn))
return refClosureDeclaration return closureFunction
} }
} }