org.jetbrains.kotlin.ir.backend.js.lower.ExportedDefaultParameterStub.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2020 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.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
import org.jetbrains.kotlin.ir.backend.js.utils.hasStableJsName
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
private fun IrConstructorCall.isAnnotation(name: FqName): Boolean {
return symbol.owner.parentAsClass.fqNameWhenAvailable == name
}
class ExportedDefaultParameterStub(val context: JsIrBackendContext) : DeclarationTransformer {
private fun IrBuilderWithScope.createDefaultResolutionExpression(
fromParameter: IrValueParameter,
toParameter: IrValueParameter,
): IrExpression? {
return fromParameter.defaultValue?.let { defaultValue ->
irIfThenElse(
toParameter.type,
irEqeqeq(
irGet(toParameter, context.irBuiltIns.anyNType),
irCall([email protected])
),
defaultValue.expression,
irGet(toParameter)
)
}
}
private fun IrConstructor.introduceDefaultResolution(): IrConstructor {
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
val variables = mutableMapOf()
val defaultResolutionStatements = valueParameters.mapNotNull { valueParameter ->
irBuilder.createDefaultResolutionExpression(valueParameter, valueParameter)?.let { initializer ->
JsIrBuilder.buildVar(
valueParameter.type,
this@introduceDefaultResolution,
name = valueParameter.name.asString(),
initializer = initializer
).also {
variables[valueParameter] = it
}
}
}
if (variables.isNotEmpty()) {
body?.transformChildren(VariableRemapper(variables), null)
body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += defaultResolutionStatements
statements += body?.statements ?: emptyList()
}
}
return also {
valueParameters.forEach {
if (it.defaultValue != null) {
it.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER
}
}
}
}
override fun transformFlat(declaration: IrDeclaration): List? {
if (declaration !is IrFunction) {
return null
}
if (!declaration.hasStableJsName(context)) {
return null
}
if (!declaration.valueParameters.any { it.defaultValue != null }) {
return null
}
if (declaration is IrConstructor) {
return listOf(declaration.introduceDefaultResolution())
}
val exportedDefaultStubFun = context.irFactory.buildFun {
updateFrom(declaration)
name = declaration.name
origin = JsIrBuilder.SYNTHESIZED_DECLARATION
}
if (declaration.isExported(context)) {
context.additionalExportedDeclarations.add(exportedDefaultStubFun)
}
exportedDefaultStubFun.parent = declaration.parent
exportedDefaultStubFun.copyParameterDeclarationsFrom(declaration)
exportedDefaultStubFun.returnType = declaration.returnType.remapTypeParameters(declaration, exportedDefaultStubFun)
exportedDefaultStubFun.valueParameters.forEach {
if (it.defaultValue != null) {
it.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER
}
it.defaultValue = null
}
declaration.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT
val irBuilder = context.createIrBuilder(exportedDefaultStubFun.symbol, exportedDefaultStubFun.startOffset, exportedDefaultStubFun.endOffset)
exportedDefaultStubFun.body = irBuilder.irBlockBody(exportedDefaultStubFun) {
+irReturn(irCall(declaration).apply {
passTypeArgumentsFrom(declaration)
dispatchReceiver = exportedDefaultStubFun.dispatchReceiverParameter?.let { irGet(it) }
extensionReceiver = exportedDefaultStubFun.extensionReceiverParameter?.let { irGet(it) }
declaration.valueParameters.forEachIndexed { index, irValueParameter ->
val exportedParameter = exportedDefaultStubFun.valueParameters[index]
val value = createDefaultResolutionExpression(irValueParameter, exportedParameter) ?: irGet(exportedParameter)
putValueArgument(index, value)
}
})
}
val (exportAnnotations, irrelevantAnnotations) = declaration.annotations.map { it.deepCopyWithSymbols(declaration as? IrDeclarationParent) }
.partition {
it.isAnnotation(JsAnnotations.jsExportFqn) || (it.isAnnotation(JsAnnotations.jsNameFqn))
}
declaration.annotations = irrelevantAnnotations
exportedDefaultStubFun.annotations = exportAnnotations
return listOf(exportedDefaultStubFun, declaration)
}
}