[JS IR BE] Secondary constructors lowering refactoring
* make it per-file
This commit is contained in:
+10
-4
@@ -23,15 +23,13 @@ import org.jetbrains.kotlin.ir.SourceManager
|
||||
import org.jetbrains.kotlin.ir.SourceRangeInfo
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
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.lower.inline.ModuleIndex
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.getPropertyDeclaration
|
||||
@@ -120,6 +118,7 @@ class JsIrBackendContext(
|
||||
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunction>()
|
||||
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
|
||||
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
|
||||
val secondaryConstructorToFactoryCache = mutableMapOf<IrConstructor, ConstructorPair>()
|
||||
|
||||
val coroutineGetContext: IrFunctionSymbol
|
||||
get() {
|
||||
@@ -241,6 +240,13 @@ class JsIrBackendContext(
|
||||
|
||||
val primitiveClassProperties = primitiveClassesObject.declarations.filterIsInstance<IrProperty>()
|
||||
|
||||
val throwableClass = symbolTable.referenceClass(
|
||||
getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable")))
|
||||
).owner
|
||||
val throwableConstructors = throwableClass.declarations.filterIsInstance<IrConstructor>()
|
||||
|
||||
val defaultThrowableCtor = throwableConstructors.single { it.valueParameters.size == 0 }
|
||||
|
||||
private fun referenceOperators() = OperatorNames.ALL.map { name ->
|
||||
// TODO to replace KotlinType with IrType we need right equals on IrType
|
||||
name to irBuiltIns.primitiveTypes.fold(mutableMapOf<KotlinType, IrFunctionSymbol>()) { m, t ->
|
||||
|
||||
@@ -246,15 +246,17 @@ private val TypeOperatorLoweringPhase = makeJsPhase(
|
||||
prerequisite = setOf(BridgesConstructionPhase, RemoveInlineFunctionsWithReifiedTypeParametersLoweringPhase)
|
||||
)
|
||||
|
||||
private val SecondaryCtorLoweringPhase = makeJsPhase(
|
||||
{ context, module ->
|
||||
SecondaryCtorLowering(context).run {
|
||||
constructorProcessorLowering.runOnFilesPostfix(module.files + context.dependencies.flatMap { it.files })
|
||||
constructorRedirectorLowering.lower(module)
|
||||
}
|
||||
},
|
||||
name = "SecondaryCtorLoweringPhase",
|
||||
description = "Generate static functions for each secondary constructor and replace usages",
|
||||
private val SecondaryConstructorLoweringPhase = makeJsPhase(
|
||||
{ context, module -> SecondaryConstructorLowering(context).lower(module) },
|
||||
name = "SecondaryConstructorLoweringPhase",
|
||||
description = "Generate static functions for each secondary constructor",
|
||||
prerequisite = setOf(InnerClassesLoweringPhase)
|
||||
)
|
||||
|
||||
private val SecondaryFactoryInjectorLoweringPhase = makeJsPhase(
|
||||
{ context, module -> SecondaryFactoryInjectorLowering(context).lower(module) },
|
||||
name = "SecondaryFactoryInjectorLoweringPhase",
|
||||
description = "Replace usage of secondary constructor with corresponding static function",
|
||||
prerequisite = setOf(InnerClassesLoweringPhase)
|
||||
)
|
||||
|
||||
@@ -355,7 +357,8 @@ val jsPhases = listOf(
|
||||
MultipleCatchesLoweringPhase,
|
||||
BridgesConstructionPhase,
|
||||
TypeOperatorLoweringPhase,
|
||||
SecondaryCtorLoweringPhase,
|
||||
SecondaryConstructorLoweringPhase,
|
||||
SecondaryFactoryInjectorLoweringPhase,
|
||||
InlineClassLoweringPhase,
|
||||
AutoboxingTransformerPhase,
|
||||
BlockDecomposerLoweringPhase,
|
||||
|
||||
+202
-221
@@ -5,10 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
|
||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
@@ -19,101 +19,106 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
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.transformDeclarationsFlat
|
||||
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
|
||||
|
||||
class SecondaryCtorLowering(val context: JsIrBackendContext) {
|
||||
data class ConstructorPair(val delegate: IrSimpleFunction, val stub: IrSimpleFunction)
|
||||
|
||||
data class ConstructorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol)
|
||||
class SecondaryConstructorLowering(val context: JsIrBackendContext) : ClassLoweringPass {
|
||||
|
||||
private val oldCtorToNewMap = mutableMapOf<IrConstructorSymbol, ConstructorPair>()
|
||||
private val oldCtorToNewMap = context.secondaryConstructorToFactoryCache
|
||||
|
||||
val constructorProcessorLowering = object : DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
irDeclarationContainer.declarations.filterIsInstance<IrClass>().forEach {
|
||||
if (!it.isInline) // Inline classes are lowered separately
|
||||
lowerClass(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun lower(irClass: IrClass) {
|
||||
if (irClass.isInline) return
|
||||
|
||||
val constructorRedirectorLowering = object : DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
if (irDeclarationContainer is IrClass) {
|
||||
if (!irDeclarationContainer.isInline) // Inline classes are lowered separately
|
||||
updateConstructorDeclarations(irDeclarationContainer)
|
||||
}
|
||||
for (it in irDeclarationContainer.declarations) {
|
||||
it.accept(CallsiteRedirectionTransformer(), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateConstructorDeclarations(irClass: IrClass) {
|
||||
irClass.transformDeclarationsFlat {
|
||||
irClass.declarations.transformFlat {
|
||||
if (it is IrConstructor) {
|
||||
oldCtorToNewMap[it.symbol]?.let { (newInit, newCreate) ->
|
||||
listOf(newInit.owner, newCreate.owner)
|
||||
}
|
||||
if (it.isPrimary) null else transformConstructor(it, irClass)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerClass(irClass: IrClass): List<IrSimpleFunction> {
|
||||
val className = irClass.name.asString()
|
||||
val oldConstructors = mutableListOf<IrConstructor>()
|
||||
val newConstructors = mutableListOf<IrSimpleFunction>()
|
||||
|
||||
for (declaration in irClass.declarations) {
|
||||
if (declaration is IrConstructor && !declaration.isPrimary) {
|
||||
// TODO delegate name generation
|
||||
val constructorName = "${className}_init"
|
||||
// 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
|
||||
// * Creation function which has same signature with original constructor,
|
||||
// creates new object via `Object.create` builtIn and passes it to corresponding `Init` function
|
||||
// In other words:
|
||||
// Foo::constructor(...) {
|
||||
// body
|
||||
// }
|
||||
// =>
|
||||
// Foo_init_$Init$(..., $this) {
|
||||
// body[ this = $this ]
|
||||
// return $this
|
||||
// }
|
||||
// Foo_init_$Create$(...) {
|
||||
// val t = Object.create(Foo.prototype);
|
||||
// return Foo_init_$Init$(..., t)
|
||||
// }
|
||||
val newInitConstructor = createInitConstructor(declaration, irClass, constructorName, irClass.defaultType)
|
||||
val newCreateConstructor = createCreateConstructor(declaration, newInitConstructor, constructorName, irClass.defaultType)
|
||||
|
||||
oldCtorToNewMap[declaration.symbol] = ConstructorPair(newInitConstructor.symbol, newCreateConstructor.symbol)
|
||||
|
||||
oldConstructors += declaration
|
||||
newConstructors += newInitConstructor
|
||||
newConstructors += newCreateConstructor
|
||||
}
|
||||
private fun transformConstructor(constructor: IrConstructor, irClass: IrClass): List<IrSimpleFunction> {
|
||||
val stubs = oldCtorToNewMap.getOrPut(constructor) {
|
||||
buildConstructorStubDeclarations(constructor, irClass)
|
||||
}
|
||||
|
||||
return newConstructors
|
||||
generateStubsBody(constructor, irClass, stubs)
|
||||
|
||||
return listOf(stubs.delegate, stubs.stub)
|
||||
}
|
||||
|
||||
private fun generateStubsBody(constructor: IrConstructor, irClass: IrClass, stubs: ConstructorPair) {
|
||||
// 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
|
||||
// * Creation function which has same signature with original constructor,
|
||||
// creates new object via `Object.create` builtIn and passes it to corresponding `Init` function
|
||||
// In other words:
|
||||
// Foo::constructor(...) {
|
||||
// body
|
||||
// }
|
||||
// =>
|
||||
// Foo_init_$Init$(..., $this) {
|
||||
// body[ this = $this ]
|
||||
// return $this
|
||||
// }
|
||||
// Foo_init_$Create$(...) {
|
||||
// val t = Object.create(Foo.prototype);
|
||||
// return Foo_init_$Init$(..., t)
|
||||
// }
|
||||
generateInitBody(constructor, irClass, stubs.delegate)
|
||||
generateFactoryBody(constructor, irClass, stubs.stub, stubs.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())
|
||||
}
|
||||
|
||||
for (i in 0 until stub.valueParameters.size) {
|
||||
call.putValueArgument(i, JsIrBuilder.buildGetValue(stub.valueParameters[i].symbol))
|
||||
}
|
||||
|
||||
call.putValueArgument(constructor.valueParameters.size, irCreateCall)
|
||||
}
|
||||
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 oldValueParameters = constructor.valueParameters + oldThisReceiver
|
||||
|
||||
// TODO: replace parameters as well
|
||||
delegate.body = JsIrBuilder.buildBlockBody(statements + retStmt).apply {
|
||||
transformChildrenVoid(ThisUsageReplaceTransformer(delegate.symbol, oldValueParameters.zip(delegate.valueParameters).toMap()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class ThisUsageReplaceTransformer(
|
||||
val function: IrFunctionSymbol,
|
||||
val symbolMapping: Map<IrValueParameter, IrValueParameter>
|
||||
) :
|
||||
IrElementTransformerVoid() {
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
val newThisSymbol = symbolMapping.values.last().symbol
|
||||
|
||||
@@ -125,154 +130,130 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) {
|
||||
IrGetValueImpl(expression.startOffset, expression.endOffset, newThisSymbol.owner.type, newThisSymbol)
|
||||
)
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression =
|
||||
if (expression.symbol.owner in symbolMapping) IrGetValueImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
symbolMapping[expression.symbol.owner]!!.symbol,
|
||||
expression.origin
|
||||
) else {
|
||||
expression
|
||||
}
|
||||
}
|
||||
|
||||
private fun createInitConstructor(
|
||||
declaration: IrConstructor,
|
||||
klass: IrClass,
|
||||
name: String,
|
||||
type: IrType
|
||||
): IrSimpleFunction {
|
||||
|
||||
val thisParam = JsIrBuilder.buildValueParameter("\$this", declaration.valueParameters.size, type)
|
||||
val oldThisReceiver = klass.thisReceiver!!
|
||||
val functionName = "${name}_\$Init\$"
|
||||
|
||||
return JsIrBuilder.buildFunction(
|
||||
functionName,
|
||||
type,
|
||||
declaration.parent,
|
||||
declaration.visibility,
|
||||
Modality.FINAL,
|
||||
declaration.isInline,
|
||||
declaration.isExternal
|
||||
).also {
|
||||
thisParam.run { parent = it }
|
||||
val retStmt = JsIrBuilder.buildReturn(it.symbol, JsIrBuilder.buildGetValue(thisParam.symbol), context.irBuiltIns.nothingType)
|
||||
val statements = (declaration.body!!.deepCopyWithSymbols(it) as IrStatementContainer).statements
|
||||
|
||||
it.copyTypeParametersFrom(declaration.parentAsClass)
|
||||
|
||||
val newValueParameters = declaration.valueParameters.map { p -> p.copyTo(it) }
|
||||
it.valueParameters += (newValueParameters + thisParam)
|
||||
|
||||
val oldValueParameters = declaration.valueParameters + oldThisReceiver
|
||||
|
||||
// TODO: replace parameters as well
|
||||
it.body = JsIrBuilder.buildBlockBody(statements + retStmt).apply {
|
||||
transformChildrenVoid(ThisUsageReplaceTransformer(it.symbol, oldValueParameters.zip(it.valueParameters).toMap()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCreateConstructor(
|
||||
declaration: IrConstructor,
|
||||
ctorImpl: IrSimpleFunction,
|
||||
name: String,
|
||||
type: IrType
|
||||
): IrSimpleFunction {
|
||||
|
||||
val functionName = "${name}_\$Create\$"
|
||||
|
||||
return JsIrBuilder.buildFunction(
|
||||
functionName,
|
||||
type,
|
||||
declaration.parent,
|
||||
declaration.visibility,
|
||||
Modality.FINAL,
|
||||
declaration.isInline,
|
||||
declaration.isExternal
|
||||
).also {
|
||||
it.copyTypeParametersFrom(declaration.parentAsClass)
|
||||
it.valueParameters += declaration.valueParameters.map { p -> p.copyTo(it) }
|
||||
|
||||
val createFunctionIntrinsic = context.intrinsics.jsObjectCreate
|
||||
val irCreateCall = JsIrBuilder.buildCall(createFunctionIntrinsic.symbol, type, listOf(type))
|
||||
val irDelegateCall = JsIrBuilder.buildCall(ctorImpl.symbol, type).also { call ->
|
||||
for (i in 0 until it.valueParameters.size) {
|
||||
call.putValueArgument(i, JsIrBuilder.buildGetValue(it.valueParameters[i].symbol))
|
||||
}
|
||||
// valueParameters.forEachIndexed { i, p -> it.putValueArgument(i, JsIrBuilder.buildGetValue(p.symbol)) }
|
||||
call.putValueArgument(declaration.valueParameters.size, irCreateCall)
|
||||
|
||||
// typeParameters.mapIndexed { i, t -> ctorImpl.typeParameters[i].descriptor -> }
|
||||
}
|
||||
val irReturn = JsIrBuilder.buildReturn(it.symbol, irDelegateCall, context.irBuiltIns.nothingType)
|
||||
|
||||
|
||||
it.body = JsIrBuilder.buildBlockBody(listOf(irReturn))
|
||||
}
|
||||
}
|
||||
|
||||
inner class CallsiteRedirectionTransformer : IrElementTransformer<IrFunction?> {
|
||||
|
||||
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration)
|
||||
|
||||
override fun visitCall(expression: IrCall, data: IrFunction?): IrElement {
|
||||
super.visitCall(expression, data)
|
||||
|
||||
val target = expression.symbol.owner
|
||||
|
||||
if (target is IrConstructor) {
|
||||
if (!target.isPrimary) {
|
||||
val ctor = oldCtorToNewMap[target.symbol]
|
||||
if (ctor != null) {
|
||||
return redirectCall(expression, ctor.stub)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement {
|
||||
super.visitDelegatingConstructorCall(expression, data)
|
||||
|
||||
val target = expression.symbol
|
||||
if (target.owner.isPrimary) {
|
||||
// nothing to do here
|
||||
return expression
|
||||
}
|
||||
|
||||
val fromPrimary = data!! is IrConstructor
|
||||
// TODO: what is `deserialized` constructor?
|
||||
val ctor = oldCtorToNewMap[target] ?: return expression
|
||||
val newCall = redirectCall(expression, ctor.delegate)
|
||||
|
||||
val readThis = if (fromPrimary) {
|
||||
val thisKlass = expression.symbol.owner.parent as IrClass
|
||||
val thisSymbol = thisKlass.thisReceiver!!.symbol
|
||||
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, thisSymbol)
|
||||
} else {
|
||||
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, data.valueParameters.last().symbol)
|
||||
}
|
||||
|
||||
newCall.putValueArgument(expression.valueArgumentsCount, readThis)
|
||||
|
||||
return newCall
|
||||
}
|
||||
|
||||
private fun redirectCall(
|
||||
call: IrFunctionAccessExpression,
|
||||
newTarget: IrSimpleFunctionSymbol
|
||||
) = IrCallImpl(call.startOffset, call.endOffset, call.type, newTarget).apply {
|
||||
|
||||
// TODO: Should constructors have type arguments
|
||||
// copyTypeArgumentsFrom(call)
|
||||
|
||||
for (i in 0 until call.valueArgumentsCount) {
|
||||
putValueArgument(i, call.getValueArgument(i))
|
||||
}
|
||||
}
|
||||
override fun visitGetValue(expression: IrGetValue) = symbolMapping[expression.symbol.owner]?.let {
|
||||
expression.run { IrGetValueImpl(startOffset, endOffset, type, it.symbol, origin) }
|
||||
} ?: expression
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrTypeParameter.toIrType() = IrSimpleTypeImpl(symbol, true, emptyList(), emptyList())
|
||||
|
||||
private fun buildInitDeclaration(constructor: IrConstructor, irClass: IrClass): IrSimpleFunction {
|
||||
val type = irClass.defaultType
|
||||
val constructorName = "${irClass.name}_init"
|
||||
val functionName = "${constructorName}_\$Init\$"
|
||||
|
||||
return JsIrBuilder.buildFunction(
|
||||
functionName,
|
||||
type,
|
||||
constructor.parent,
|
||||
constructor.visibility,
|
||||
Modality.FINAL,
|
||||
constructor.isInline,
|
||||
constructor.isExternal
|
||||
).also {
|
||||
it.copyTypeParametersFrom(constructor.parentAsClass)
|
||||
|
||||
constructor.valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) }
|
||||
it.valueParameters += JsIrBuilder.buildValueParameter("\$this", constructor.valueParameters.size, type).apply { parent = it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildFactoryDeclaration(constructor: IrConstructor, irClass: IrClass): IrSimpleFunction {
|
||||
val type = irClass.defaultType
|
||||
val constructorName = "${irClass.name}_init"
|
||||
val functionName = "${constructorName}_\$Create\$"
|
||||
|
||||
return JsIrBuilder.buildFunction(
|
||||
functionName,
|
||||
type,
|
||||
constructor.parent,
|
||||
constructor.visibility,
|
||||
Modality.FINAL,
|
||||
constructor.isInline,
|
||||
constructor.isExternal
|
||||
).also {
|
||||
it.copyTypeParametersFrom(constructor.parentAsClass)
|
||||
it.valueParameters += constructor.valueParameters.map { p -> p.copyTo(it) }
|
||||
}
|
||||
}
|
||||
|
||||
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 class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
|
||||
|
||||
private val oldCtorToNewMap = context.secondaryConstructorToFactoryCache
|
||||
private val defaultThrowableConstructor = context.defaultThrowableCtor
|
||||
|
||||
private val IrFunction.isSecondaryConstructorCall
|
||||
get() =
|
||||
this is IrConstructor && !isPrimary && this != defaultThrowableConstructor && !isExternal && !parentAsClass.isInline
|
||||
|
||||
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration)
|
||||
|
||||
override fun visitCall(expression: IrCall, data: IrFunction?): IrElement {
|
||||
super.visitCall(expression, data)
|
||||
|
||||
val target = expression.symbol.owner
|
||||
return if (target.isSecondaryConstructorCall) {
|
||||
val constructor = target as IrConstructor
|
||||
val ctor = oldCtorToNewMap.getOrPut(constructor) {
|
||||
buildConstructorStubDeclarations(constructor, constructor.parentAsClass)
|
||||
}
|
||||
replaceSecondaryConstructorWithFactoryFunction(expression, ctor.stub.symbol, data)
|
||||
} else expression
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement {
|
||||
super.visitDelegatingConstructorCall(expression, data)
|
||||
|
||||
val target = expression.symbol.owner
|
||||
|
||||
return if (target.isSecondaryConstructorCall) {
|
||||
val klass = target.parentAsClass
|
||||
val ctor = oldCtorToNewMap.getOrPut(target) { buildConstructorStubDeclarations(target, klass) }
|
||||
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, ctor.delegate.symbol, data)
|
||||
|
||||
val readThis = expression.run {
|
||||
if (data!! is IrConstructor) {
|
||||
IrGetValueImpl(startOffset, endOffset, type, klass.thisReceiver!!.symbol)
|
||||
} else {
|
||||
IrGetValueImpl(startOffset, endOffset, type, data.valueParameters.last().symbol)
|
||||
}
|
||||
}
|
||||
|
||||
newCall.apply { putValueArgument(expression.valueArgumentsCount, readThis) }
|
||||
} else expression
|
||||
}
|
||||
|
||||
private fun replaceSecondaryConstructorWithFactoryFunction(
|
||||
call: IrFunctionAccessExpression,
|
||||
newTarget: IrSimpleFunctionSymbol,
|
||||
enclosing: IrFunction?
|
||||
) = IrCallImpl(call.startOffset, call.endOffset, call.type, newTarget).apply {
|
||||
|
||||
// There are 3 possible cases how Type Parameters should be passed
|
||||
if (call is IrCall) {
|
||||
// if it was just a normal constructor cal via `IrCall` just copy arguments as is
|
||||
copyTypeArgumentsFrom(call)
|
||||
} else {
|
||||
// if it is `IrDelegatingConstructorCall` figure out from constructor is called and extract parameters from here
|
||||
// Since `IrConstructor` doesn't have TypeParameters we take them from its class
|
||||
val typeParameters = enclosing?.let { if (it is IrConstructor) it.parentAsClass.typeParameters else it.typeParameters }!!
|
||||
for (i in 0 until typeParameters.size) {
|
||||
putTypeArgument(i, typeParameters[i].toIrType())
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until call.valueArgumentsCount) {
|
||||
putValueArgument(i, call.getValueArgument(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -44,12 +44,10 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
|
||||
private val causeName get() = JsIrBuilder.buildString(stringType, "cause")
|
||||
private val nameName get() = JsIrBuilder.buildString(stringType, "name")
|
||||
|
||||
private val throwableClass = context.symbolTable.referenceClass(
|
||||
context.getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable")))
|
||||
).owner
|
||||
private val throwableConstructors = throwableClass.declarations.filterIsInstance<IrConstructor>()
|
||||
private val throwableClass = context.throwableClass
|
||||
private val throwableConstructors = context.throwableConstructors
|
||||
|
||||
private val defaultCtor = throwableConstructors.single { it.valueParameters.size == 0 }
|
||||
private val defaultCtor = context.defaultThrowableCtor
|
||||
private val toString =
|
||||
throwableClass.declarations.filterIsInstance<IrSimpleFunction>().single { it.name == Name.identifier("toString") }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user