diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt index 19a341cce8d..ec04f3bb0b2 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt @@ -9,54 +9,113 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass 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.JsIrArithBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.builders.declarations.addFunction -import org.jetbrains.kotlin.ir.declarations.IrField -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction -import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.builders.declarations.buildField +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression +import org.jetbrains.kotlin.ir.expressions.IrGetField import org.jetbrains.kotlin.ir.expressions.IrSetField -import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl -import org.jetbrains.kotlin.ir.util.kotlinFqName +import org.jetbrains.kotlin.ir.expressions.IrWhen import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.set class PropertyLazyInitLowering(private val context: JsIrBackendContext) : FileLoweringPass { private val irBuiltIns get() = context.irBuiltIns + private val calculator = JsIrArithBuilder(context) + + private val irFactory + get() = context.irFactory + override fun lower(irFile: IrFile) { val initializers = PropertyInitializerMover(context).process(irFile) - val irFactory = context.irFactory - if (initializers.isNotEmpty()) { - irFactory.addFunction(irFile) { - name = Name.identifier("init properties ${irFile.kotlinFqName}") - returnType = irBuiltIns.unitType - origin = JsIrBuilder.SYNTHESIZED_DECLARATION - }.apply { - body = irFactory.createBlockBody( - UNDEFINED_OFFSET, - UNDEFINED_OFFSET, - initializers - .map { (field, expression) -> - createIrSetField(field, expression) - } - ) + + if (initializers.isEmpty()) return + + val fileName = irFile.name + val initialisedField = irFactory.createInitialisationField(fileName) + .apply { + irFile.declarations.add(this) + parent = irFile } + + irFactory.addFunction(irFile) { + name = Name.identifier("init properties $fileName") + returnType = irBuiltIns.unitType + origin = JsIrBuilder.SYNTHESIZED_DECLARATION + }.apply { + buildPropertiesInitializationBody( + initializers, + initialisedField + ) } } + private fun IrFactory.createInitialisationField(fileName: String): IrField = + buildField { + name = Name.identifier("properties initialised $fileName") + type = irBuiltIns.booleanType + isStatic = true + isFinal = true + origin = JsIrBuilder.SYNTHESIZED_DECLARATION + } + + private fun IrSimpleFunction.buildPropertiesInitializationBody( + initializers: Map, + initialisedField: IrField + ) { + + body = irFactory.createBlockBody( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + buildBodyWithIfGuard(initializers, initialisedField) + ) + } + + private fun buildBodyWithIfGuard( + initializers: Map, + initialisedField: IrField + ): List { + val statements = initializers + .map { (field, expression) -> + createIrSetField(field, expression) + } + + val upGuard = createIrSetField( + initialisedField, + JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true) + ) + + return JsIrBuilder.buildIfElse( + type = irBuiltIns.unitType, + cond = calculator.not(createIrGetField(initialisedField)), + thenBranch = JsIrBuilder.buildComposite( + type = irBuiltIns.unitType, + statements = mutableListOf(upGuard).apply { addAll(statements) } + ) + ).let { listOf(it) } + } + + private fun createIrGetField(field: IrField): IrGetField { + return JsIrBuilder.buildGetField( + symbol = field.symbol, + receiver = null + ) + } + private fun createIrSetField(field: IrField, expression: IrExpression): IrSetField { - return IrSetFieldImpl( - field.startOffset, - field.endOffset, - field.symbol, - null, - expression, - expression.type + return JsIrBuilder.buildSetField( + symbol = field.symbol, + receiver = null, + value = expression, + type = expression.type ) } } @@ -65,9 +124,9 @@ private class PropertyInitializerMover( private val context: JsIrBackendContext ) : IrElementTransformerVoid() { - private val fieldToInitializers = mutableListOf>() + private val fieldToInitializers = mutableMapOf() - fun process(irFile: IrFile): List> { + fun process(irFile: IrFile): Map { irFile.transformChildrenVoid(this) return fieldToInitializers } @@ -78,25 +137,14 @@ private class PropertyInitializerMover( ?.takeIf { !it.isConst } ?.takeIf { !it.isDelegated } ?.backingField + ?.takeIf { it !in fieldToInitializers } ?.takeIf { it.initializer != null } ?.let { field -> - fieldToInitializers.add(field to field.initializer!!.expression) + fieldToInitializers[field] = field.initializer!!.expression } - fieldToInitializers.forEach { it.first.initializer = null } + fieldToInitializers.forEach { it.key.initializer = null } return super.visitSimpleFunction(declaration) } - - override fun visitFieldAccess(expression: IrFieldAccessExpression): IrExpression { - return super.visitFieldAccess(expression) - } - - override fun visitField(declaration: IrField): IrStatement { - return super.visitField(declaration) - } - - override fun visitBody(body: IrBody): IrBody { - return super.visitBody(body) - } } \ No newline at end of file