ObjectLowering

This commit is contained in:
Anton Bannykh
2019-12-25 13:10:30 +03:00
committed by Anton Bannykh
parent ce625075f6
commit 76ff30b86e
4 changed files with 64 additions and 53 deletions
@@ -426,14 +426,14 @@ private val staticMembersLoweringPhase = makeJsModulePhase(
description = "Move static member declarations to top-level" description = "Move static member declarations to top-level"
) )
private val objectDeclarationLoweringPhase = makeCustomJsModulePhase( private val objectDeclarationLoweringPhase = makeJsModulePhase(
{ context, module -> ObjectDeclarationLowering(context, context.objectToGetInstanceFunction).lower(module) }, ::ObjectDeclarationLowering,
name = "ObjectDeclarationLowering", name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions" description = "Create lazy object instance generator functions"
) )
private val objectUsageLoweringPhase = makeCustomJsModulePhase( private val objectUsageLoweringPhase = makeJsModulePhase(
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) }, ::ObjectUsageLowering,
name = "ObjectUsageLowering", name = "ObjectUsageLowering",
description = "Transform IrGetObjectValue into instance generator call" description = "Transform IrGetObjectValue into instance generator call"
) )
@@ -15,4 +15,6 @@ class JsMapping : DefaultMapping() {
val originalInnerClassPrimaryConstructorByClass = newMapping<IrClass, IrConstructor>() val originalInnerClassPrimaryConstructorByClass = newMapping<IrClass, IrConstructor>()
val secondaryConstructorToDelegate = newMapping<IrConstructor, IrSimpleFunction>() val secondaryConstructorToDelegate = newMapping<IrConstructor, IrSimpleFunction>()
val secondaryConstructorToFactory = newMapping<IrConstructor, IrSimpleFunction>() val secondaryConstructorToFactory = newMapping<IrConstructor, IrSimpleFunction>()
val objectToGetInstanceFunction = newMapping<IrClass, IrSimpleFunction>()
val objectToInstanceField = newMapping<IrClass, IrField>()
} }
@@ -5,94 +5,103 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
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.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrBlockBody
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.IrGetObjectValue import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
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
class ObjectDeclarationLowering( class ObjectDeclarationLowering(
val context: CommonBackendContext, val context: JsCommonBackendContext
val objectToGetInstanceFunction: MutableMap<IrClassSymbol, IrSimpleFunction> ) : DeclarationTransformer {
) : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { declaration ->
if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
return@transformDeclarationsFlat null
val getInstanceFun = getOrCreateGetInstanceFunction(objectToGetInstanceFunction, declaration) private var IrClass.instanceField by context.mapping.objectToInstanceField
val instanceField = buildField { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
name = Name.identifier(declaration.name.asString() + "_instance") if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
type = declaration.defaultType.makeNullable() return null
isStatic = true
}.apply {
parent = declaration.parent
initializer = null // Initialized with 'undefined'
}
val primaryConstructor = declaration.primaryConstructor!!
val body = primaryConstructor.body as IrBlockBody
// Initialize instance field in the beginning of the constructor because it can be used inside the constructor later val getInstanceFun = context.getOrCreateGetInstanceFunction(declaration)
val initInstanceField = context.createIrBuilder(primaryConstructor.symbol).buildStatement(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
irSetField(null, instanceField, irGet(declaration.thisReceiver!!))
}
body.statements.add(0, initInstanceField)
getInstanceFun.body = context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) { val instanceField = buildField {
name = Name.identifier(declaration.name.asString() + "_instance")
type = declaration.defaultType.makeNullable()
isStatic = true
}.apply {
parent = declaration.parent
initializer = null // Initialized with 'undefined'
}
declaration.instanceField = instanceField
val primaryConstructor = declaration.primaryConstructor!! // TODO find a way to trigger this correctly
getInstanceFun.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) {
+irIfThen( +irIfThen(
irEqualsNull(irGetField(null, instanceField)), irEqualsNull(irGetField(null, instanceField)),
// Instance field initialized inside constructor // Instance field initialized inside constructor
irCallConstructor(primaryConstructor.symbol, emptyList()) irCallConstructor(primaryConstructor.symbol, emptyList())
) )
+irReturn(irGetField(null, instanceField)) +irReturn(irGetField(null, instanceField))
} }.statements
listOf(declaration, instanceField, getInstanceFun)
} }
return listOf(declaration, instanceField, getInstanceFun)
} }
} }
class ObjectUsageLowering( class ObjectUsageLowering(
val context: CommonBackendContext, val context: JsCommonBackendContext
val objectToGetInstanceFunction: MutableMap<IrClassSymbol, IrSimpleFunction> ) : BodyLoweringPass {
) : FileLoweringPass {
override fun lower(irFile: IrFile) { private var IrClass.instanceField by context.mapping.objectToInstanceField
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrConstructor && container.isPrimary) {
val irClass = container.parentAsClass
irClass.instanceField?.let { instanceField ->
// Initialize instance field in the beginning of the constructor because it can be used inside the constructor later
val initInstanceField = context.createIrBuilder(container.symbol).buildStatement(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
irSetField(null, instanceField, irGet(irClass.thisReceiver!!))
}
(irBody as IrBlockBody).statements.add(0, initInstanceField)
}
}
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
val obj: IrClass = expression.symbol.owner val obj: IrClass = expression.symbol.owner
if (obj.isEffectivelyExternal()) return expression if (obj.isEffectivelyExternal()) return expression
return JsIrBuilder.buildCall(getOrCreateGetInstanceFunction(objectToGetInstanceFunction, obj).symbol) return JsIrBuilder.buildCall(context.getOrCreateGetInstanceFunction(obj).symbol)
} }
}) })
} }
} }
fun getOrCreateGetInstanceFunction(objectToGetInstanceFunction: MutableMap<IrClassSymbol, IrSimpleFunction>, obj: IrClass) = fun JsCommonBackendContext.getOrCreateGetInstanceFunction(obj: IrClass) =
objectToGetInstanceFunction.getOrPut(obj.symbol) { mapping.objectToGetInstanceFunction.getOrPut(obj) {
JsIrBuilder.buildFunction( JsIrBuilder.buildFunction(
obj.name.asString() + "_getInstance", obj.name.asString() + "_getInstance",
returnType = obj.defaultType, returnType = obj.defaultType,
@@ -332,14 +332,14 @@ private val builtInsLoweringPhase = makeWasmModulePhase(
description = "Lower IR buildins" description = "Lower IR buildins"
) )
private val objectDeclarationLoweringPhase = makeCustomWasmModulePhase( private val objectDeclarationLoweringPhase = makeWasmModulePhase(
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) }, ::ObjectUsageLowering,
name = "ObjectDeclarationLowering", name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions" description = "Create lazy object instance generator functions"
) )
private val objectUsageLoweringPhase = makeCustomWasmModulePhase( private val objectUsageLoweringPhase = makeWasmModulePhase(
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) }, ::ObjectUsageLowering,
name = "ObjectUsageLowering", name = "ObjectUsageLowering",
description = "Transform IrGetObjectValue into instance generator call" description = "Transform IrGetObjectValue into instance generator call"
) )