Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.ir.backend.js.lower.InteropCallableReferenceLowering.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2018 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.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyToWithoutSuperTypes
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
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.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val newDeclarations = mutableListOf()
private lateinit var implicitDeclarationFile: IrFile //= context.implicitDeclarationFile
override fun lower(irBody: IrBody, container: IrDeclaration) {
newDeclarations.clear()
implicitDeclarationFile = container.file // TODO
irBody.transformChildrenVoid(CallableReferenceLowerTransformer())
implicitDeclarationFile.declarations += newDeclarations
}
inner class CallableReferenceLowerTransformer : IrElementTransformerVoid() {
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
expression.transformChildrenVoid(this)
if (expression.origin === JsStatementOrigins.CALLABLE_REFERENCE_CREATE) {
return transformToJavaScriptFunction(expression)
}
return expression
}
}
private fun transformToJavaScriptFunction(expression: IrConstructorCall): IrExpression {
// TODO: perform inlining
val factory = buildFactoryFunction(expression)
newDeclarations += factory
val newCall = expression.run {
IrCallImpl(startOffset, endOffset, type, factory.symbol, 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 buildLambdaBody(instance: IrVariable, lambdaDeclaration: IrSimpleFunction, invokeFun: IrSimpleFunction): IrBlockBody {
val invokeExpression = IrCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
invokeFun.returnType,
invokeFun.symbol,
0,
invokeFun.valueParameters.size,
JsStatementOrigins.EXPLICIT_INVOKE,
null
)
fun getValue(d: IrValueDeclaration): IrExpression = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, d.symbol)
invokeExpression.dispatchReceiver = getValue(instance)
for ((i, vp) in lambdaDeclaration.valueParameters.withIndex()) {
invokeExpression.putValueArgument(i, getValue(vp))
}
return context.irFactory.createBlockBody(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
listOf(
IrReturnImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.irBuiltIns.nothingType,
lambdaDeclaration.symbol,
invokeExpression
)
)
)
}
private fun buildFactoryBody(factoryFunction: IrSimpleFunction, expression: IrConstructorCall): IrBlockBody {
val constructor = expression.symbol.owner
val lambdaClass = constructor.parentAsClass
val invokeFun = lambdaClass.declarations.filterIsInstance().single { it.name.asString() == "invoke" }
val superInvokeFun = invokeFun.overriddenSymbols.single { it.owner.isSuspend == invokeFun.isSuspend }.owner
val lambdaName = Name.identifier("${lambdaClass.name.asString()}\$lambda")
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 superInvokeFun.returnType
visibility = DescriptorVisibilities.LOCAL
name = lambdaName
isSuspend = invokeFun.isSuspend
}
lambdaDeclaration.parent = factoryFunction
lambdaDeclaration.valueParameters = superInvokeFun.valueParameters.map { it.copyTo(lambdaDeclaration) }
val statements = ArrayList(4)
val instanceVal = JsIrBuilder.buildVar(expression.type, factoryFunction, "i").apply {
initializer = expression.run {
val newCtorCall = IrConstructorCallImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, constructorTypeArgumentsCount, valueArgumentsCount, origin)
// TODO: forward type arguments
assert(expression.dispatchReceiver == null)
assert(expression.extensionReceiver == null)
for ((i, vp) in factoryFunction.valueParameters.withIndex()) {
newCtorCall.putValueArgument(i, IrGetValueImpl(startOffset, endOffset, vp.type, vp.symbol))
}
newCtorCall
}
}
statements.add(instanceVal)
lambdaDeclaration.body = buildLambdaBody(instanceVal, lambdaDeclaration, invokeFun)
val functionExpression =
expression.run { IrFunctionExpressionImpl(startOffset, endOffset, type, lambdaDeclaration, expression.origin!!) }
val nameGetter = context.mapping.reflectedNameAccessor[lambdaClass]
if (nameGetter != null || lambdaDeclaration.isSuspend) {
val tmpVar = JsIrBuilder.buildVar(functionExpression.type, factoryFunction, "l", initializer = functionExpression)
statements.add(tmpVar)
if (nameGetter != null) {
statements.add(setDynamicProperty(tmpVar.symbol, Namer.KCALLABLE_NAME,
IrCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
nameGetter.returnType,
nameGetter.symbol,
0,
0,
JsStatementOrigins.CALLABLE_REFERENCE_INVOKE
).apply {
dispatchReceiver = JsIrBuilder.buildGetValue(instanceVal.symbol)
}
))
}
if (lambdaDeclaration.isSuspend) {
statements.add(
setDynamicProperty(
tmpVar.symbol, Namer.KCALLABLE_ARITY,
IrConstImpl.int(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.irBuiltIns.intType,
lambdaDeclaration.valueParameters.size
)
)
)
}
statements.add(JsIrBuilder.buildReturn(factoryFunction.symbol, JsIrBuilder.buildGetValue(tmpVar.symbol), context.irBuiltIns.nothingType))
} else {
statements.add(JsIrBuilder.buildReturn(factoryFunction.symbol, functionExpression, context.irBuiltIns.nothingType))
}
return context.irFactory.createBlockBody(expression.startOffset, expression.endOffset, statements)
}
private fun buildFactoryFunction(expression: IrConstructorCall): IrSimpleFunction {
val constructor = expression.symbol.owner
val lambdaClass = constructor.parentAsClass
val factoryName = Name.identifier("${lambdaClass.name.asString()}\$factory")
val factoryDeclaration = context.irFactory.buildFun {
startOffset = expression.startOffset
endOffset = expression.endOffset
visibility = lambdaClass.visibility
returnType = expression.type
name = factoryName
origin = JsStatementOrigins.FACTORY_ORIGIN
}
factoryDeclaration.parent = implicitDeclarationFile
factoryDeclaration.valueParameters = constructor.valueParameters.map { it.copyTo(factoryDeclaration) }
factoryDeclaration.typeParameters = constructor.typeParameters.map {
it.copyToWithoutSuperTypes(factoryDeclaration).also { tp ->
// TODO: make sure it is done well
tp.superTypes += it.superTypes
}
}
factoryDeclaration.body = buildFactoryBody(factoryDeclaration, expression)
return factoryDeclaration
}
private fun setDynamicProperty(r: IrValueSymbol, property: String, value: IrExpression): IrStatement {
return IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.unitType, IrDynamicOperator.EQ).apply {
receiver = IrDynamicMemberExpressionImpl(startOffset, endOffset, context.dynamicType, property, JsIrBuilder.buildGetValue(r))
arguments += value
}
}
}