SecondaryCtorLowering

This commit is contained in:
Anton Bannykh
2019-12-24 21:39:01 +03:00
committed by Anton Bannykh
parent fb36432cb5
commit d9fbe8b951
3 changed files with 78 additions and 58 deletions
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.ir.SourceRangeInfo
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceKey
import org.jetbrains.kotlin.ir.backend.js.lower.ConstructorPair
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
@@ -144,7 +143,6 @@ class JsIrBackendContext(
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
val secondaryConstructorToFactoryCache = mutableMapOf<IrConstructor, ConstructorPair>()
val intrinsics = JsIntrinsics(irBuiltIns, this)
@@ -6,13 +6,13 @@
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.*
class JsMapping : DefaultMapping() {
val singletonFieldDescriptors = newMapping<IrClass, IrField>()
val outerThisFieldSymbols = newMapping<IrClass, IrField>()
val innerClassConstructors = newMapping<IrConstructor, IrConstructor>()
val originalInnerClassPrimaryConstructorByClass = newMapping<IrClass, IrConstructor>()
val secondaryConstructorToDelegate = newMapping<IrConstructor, IrSimpleFunction>()
val secondaryConstructorToFactory = newMapping<IrConstructor, IrSimpleFunction>()
}
@@ -5,18 +5,21 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement
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.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
@@ -26,38 +29,36 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
data class ConstructorPair(val delegate: IrSimpleFunction, val stub: IrSimpleFunction)
class SecondaryConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer {
class SecondaryConstructorLowering(val context: JsIrBackendContext) : ClassLoweringPass {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
private val oldCtorToNewMap = context.secondaryConstructorToFactoryCache
if (declaration is IrConstructor && !declaration.isPrimary) {
val irClass = declaration.parentAsClass
override fun lower(irClass: IrClass) {
if (irClass.isInline) return
if (irClass.isInline) return null
irClass.declarations.transformFlat {
if (it is IrConstructor) {
if (it.isPrimary) null else transformConstructor(it, irClass)
} else null
return transformConstructor(declaration, irClass)
}
return null
}
private fun transformConstructor(constructor: IrConstructor, irClass: IrClass): List<IrSimpleFunction> {
val stubs = oldCtorToNewMap.getOrPut(constructor) {
buildConstructorStubDeclarations(constructor, irClass)
}
val delegate = context.buildConstructorDelegate(constructor, irClass)
generateStubsBody(constructor, irClass, stubs)
val factory = context.buildConstructorFactory(constructor, irClass)
return listOf(stubs.delegate, stubs.stub)
generateStubsBody(constructor, irClass, delegate, factory)
return listOf(delegate, factory)
}
private fun generateStubsBody(constructor: IrConstructor, irClass: IrClass, stubs: ConstructorPair) {
private fun generateStubsBody(constructor: IrConstructor, irClass: IrClass, delegate: IrSimpleFunction, factory: IrSimpleFunction) {
// We should split secondary constructor into two functions,
// * Initializer which contains constructor's body and takes just created object as implicit param `$this`
// ** This function is also delegation constructor
@@ -76,41 +77,42 @@ class SecondaryConstructorLowering(val context: JsIrBackendContext) : ClassLower
// val t = Object.create(Foo.prototype);
// return Foo_init_$Init$(..., t)
// }
generateInitBody(constructor, irClass, stubs.delegate)
generateFactoryBody(constructor, irClass, stubs.stub, stubs.delegate)
generateInitBody(constructor, irClass, delegate)
generateFactoryBody(constructor, irClass, factory, delegate)
}
private fun generateFactoryBody(constructor: IrConstructor, irClass: IrClass, stub: IrSimpleFunction, delegate: IrSimpleFunction) {
val type = irClass.defaultType
val createFunctionIntrinsic = context.intrinsics.jsObjectCreate
val irCreateCall = JsIrBuilder.buildCall(createFunctionIntrinsic.symbol, type, listOf(type))
val irDelegateCall = JsIrBuilder.buildCall(delegate.symbol, type).also { call ->
for (i in 0 until stub.typeParameters.size) {
call.putTypeArgument(i, stub.typeParameters[i].toIrType())
}
stub.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
val type = irClass.defaultType
val createFunctionIntrinsic = context.intrinsics.jsObjectCreate
val irCreateCall = JsIrBuilder.buildCall(createFunctionIntrinsic.symbol, type, listOf(type))
val irDelegateCall = JsIrBuilder.buildCall(delegate.symbol, type).also { call ->
for (i in 0 until stub.typeParameters.size) {
call.putTypeArgument(i, stub.typeParameters[i].toIrType())
}
for (i in 0 until stub.valueParameters.size) {
call.putValueArgument(i, JsIrBuilder.buildGetValue(stub.valueParameters[i].symbol))
}
for (i in 0 until stub.valueParameters.size) {
call.putValueArgument(i, JsIrBuilder.buildGetValue(stub.valueParameters[i].symbol))
}
call.putValueArgument(constructor.valueParameters.size, irCreateCall)
call.putValueArgument(constructor.valueParameters.size, irCreateCall)
}
val irReturn = JsIrBuilder.buildReturn(stub.symbol, irDelegateCall, context.irBuiltIns.nothingType)
statements += irReturn
}
val irReturn = JsIrBuilder.buildReturn(stub.symbol, irDelegateCall, context.irBuiltIns.nothingType)
stub.body = JsIrBuilder.buildBlockBody(listOf(irReturn))
}
private fun generateInitBody(constructor: IrConstructor, irClass: IrClass, delegate: IrSimpleFunction) {
val thisParam = delegate.valueParameters.last()
val oldThisReceiver = irClass.thisReceiver!!
val retStmt = JsIrBuilder.buildReturn(delegate.symbol, JsIrBuilder.buildGetValue(thisParam.symbol), context.irBuiltIns.nothingType)
val statements = (constructor.body!!.deepCopyWithSymbols(delegate) as IrStatementContainer).statements
val constructorBody = constructor.body!!
val oldValueParameters = constructor.valueParameters + oldThisReceiver
// TODO: replace parameters as well
delegate.body = JsIrBuilder.buildBlockBody(statements + retStmt).apply {
delegate.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += (constructorBody.deepCopyWithSymbols(delegate) as IrStatementContainer).statements
statements += JsIrBuilder.buildReturn(delegate.symbol, JsIrBuilder.buildGetValue(thisParam.symbol), context.irBuiltIns.nothingType)
transformChildrenVoid(ThisUsageReplaceTransformer(delegate.symbol, oldValueParameters.zip(delegate.valueParameters).toMap()))
}
}
@@ -179,18 +181,40 @@ private fun buildFactoryDeclaration(constructor: IrConstructor, irClass: IrClass
}
}
private fun buildConstructorStubDeclarations(constructor: IrConstructor, klass: IrClass) =
ConstructorPair(buildInitDeclaration(constructor, klass), buildFactoryDeclaration(constructor, klass))
class SecondaryFactoryInjectorLowering(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.accept(CallsiteRedirectionTransformer(context), null)
private fun JsIrBackendContext.buildConstructorDelegate(constructor: IrConstructor, klass: IrClass): IrSimpleFunction {
return mapping.secondaryConstructorToDelegate.getOrPut(constructor) {
buildInitDeclaration(constructor, klass)
}
}
private class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
private fun JsIrBackendContext.buildConstructorFactory(constructor: IrConstructor, klass: IrClass): IrSimpleFunction {
return mapping.secondaryConstructorToFactory.getOrPut(constructor) {
buildFactoryDeclaration(constructor, klass)
}
}
class SecondaryFactoryInjectorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO Simplify? Is this needed at all?
var parentFunction: IrFunction? = container as? IrFunction
var declaration = container
while (parentFunction == null) {
val parent = declaration.parent
if (parent is IrFunction) {
parentFunction = parent
}
declaration = parent as? IrDeclaration ?: break
}
irBody.accept(CallsiteRedirectionTransformer(context), parentFunction)
}
}
private class CallsiteRedirectionTransformer(private val context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
private val oldCtorToNewMap = context.secondaryConstructorToFactoryCache
private val defaultThrowableConstructor = context.defaultThrowableCtor
private val IrConstructor.isSecondaryConstructorCall
@@ -204,10 +228,8 @@ private class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrEl
val target = expression.symbol.owner
return if (target.isSecondaryConstructorCall) {
val ctor = oldCtorToNewMap.getOrPut(target) {
buildConstructorStubDeclarations(target, target.parentAsClass)
}
replaceSecondaryConstructorWithFactoryFunction(expression, ctor.stub.symbol)
val factory = context.buildConstructorFactory(target, target.parentAsClass)
replaceSecondaryConstructorWithFactoryFunction(expression, factory.symbol)
} else expression
}
@@ -218,8 +240,8 @@ private class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrEl
return if (target.isSecondaryConstructorCall) {
val klass = target.parentAsClass
val ctor = oldCtorToNewMap.getOrPut(target) { buildConstructorStubDeclarations(target, klass) }
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, ctor.delegate.symbol)
val delegate = context.buildConstructorDelegate(target, klass)
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, delegate.symbol)
val readThis = expression.run {
if (data!! is IrConstructor) {