[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.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<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 {
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<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)
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)
}
}