[JS IR] Add guard into init properties function

^KT-43222 fixed
This commit is contained in:
Ilya Goncharov
2020-10-27 16:36:01 +03:00
parent d752b7439e
commit 42e1a3280b
@@ -9,54 +9,113 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET 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.JsIrArithBuilder
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.IrSetField
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.ir.util.kotlinFqName
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
import org.jetbrains.kotlin.name.Name 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 { class PropertyLazyInitLowering(private val context: JsIrBackendContext) : FileLoweringPass {
private val irBuiltIns private val irBuiltIns
get() = context.irBuiltIns get() = context.irBuiltIns
private val calculator = JsIrArithBuilder(context)
private val irFactory
get() = context.irFactory
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
val initializers = PropertyInitializerMover(context).process(irFile) val initializers = PropertyInitializerMover(context).process(irFile)
val irFactory = context.irFactory
if (initializers.isNotEmpty()) { if (initializers.isEmpty()) return
irFactory.addFunction(irFile) {
name = Name.identifier("init properties ${irFile.kotlinFqName}") val fileName = irFile.name
returnType = irBuiltIns.unitType val initialisedField = irFactory.createInitialisationField(fileName)
origin = JsIrBuilder.SYNTHESIZED_DECLARATION .apply {
}.apply { irFile.declarations.add(this)
body = irFactory.createBlockBody( parent = irFile
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
initializers
.map { (field, expression) ->
createIrSetField(field, expression)
}
)
} }
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<IrField, IrExpression>,
initialisedField: IrField
) {
body = irFactory.createBlockBody(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
buildBodyWithIfGuard(initializers, initialisedField)
)
}
private fun buildBodyWithIfGuard(
initializers: Map<IrField, IrExpression>,
initialisedField: IrField
): List<IrWhen> {
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 { private fun createIrSetField(field: IrField, expression: IrExpression): IrSetField {
return IrSetFieldImpl( return JsIrBuilder.buildSetField(
field.startOffset, symbol = field.symbol,
field.endOffset, receiver = null,
field.symbol, value = expression,
null, type = expression.type
expression,
expression.type
) )
} }
} }
@@ -65,9 +124,9 @@ private class PropertyInitializerMover(
private val context: JsIrBackendContext private val context: JsIrBackendContext
) : IrElementTransformerVoid() { ) : IrElementTransformerVoid() {
private val fieldToInitializers = mutableListOf<Pair<IrField, IrExpression>>() private val fieldToInitializers = mutableMapOf<IrField, IrExpression>()
fun process(irFile: IrFile): List<Pair<IrField, IrExpression>> { fun process(irFile: IrFile): Map<IrField, IrExpression> {
irFile.transformChildrenVoid(this) irFile.transformChildrenVoid(this)
return fieldToInitializers return fieldToInitializers
} }
@@ -78,25 +137,14 @@ private class PropertyInitializerMover(
?.takeIf { !it.isConst } ?.takeIf { !it.isConst }
?.takeIf { !it.isDelegated } ?.takeIf { !it.isDelegated }
?.backingField ?.backingField
?.takeIf { it !in fieldToInitializers }
?.takeIf { it.initializer != null } ?.takeIf { it.initializer != null }
?.let { field -> ?.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) 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)
}
} }