[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.SourceRangeInfo
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceKey
|
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.lower.inline.ModuleIndex
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
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.types.impl.IrDynamicTypeImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
import org.jetbrains.kotlin.ir.util.getPropertyDeclaration
|
import org.jetbrains.kotlin.ir.util.getPropertyDeclaration
|
||||||
@@ -120,6 +118,7 @@ class JsIrBackendContext(
|
|||||||
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunction>()
|
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunction>()
|
||||||
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
|
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
|
||||||
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
|
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
|
||||||
|
val secondaryConstructorToFactoryCache = mutableMapOf<IrConstructor, ConstructorPair>()
|
||||||
|
|
||||||
val coroutineGetContext: IrFunctionSymbol
|
val coroutineGetContext: IrFunctionSymbol
|
||||||
get() {
|
get() {
|
||||||
@@ -241,6 +240,13 @@ class JsIrBackendContext(
|
|||||||
|
|
||||||
val primitiveClassProperties = primitiveClassesObject.declarations.filterIsInstance<IrProperty>()
|
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 ->
|
private fun referenceOperators() = OperatorNames.ALL.map { name ->
|
||||||
// TODO to replace KotlinType with IrType we need right equals on IrType
|
// TODO to replace KotlinType with IrType we need right equals on IrType
|
||||||
name to irBuiltIns.primitiveTypes.fold(mutableMapOf<KotlinType, IrFunctionSymbol>()) { m, t ->
|
name to irBuiltIns.primitiveTypes.fold(mutableMapOf<KotlinType, IrFunctionSymbol>()) { m, t ->
|
||||||
|
|||||||
@@ -246,15 +246,17 @@ private val TypeOperatorLoweringPhase = makeJsPhase(
|
|||||||
prerequisite = setOf(BridgesConstructionPhase, RemoveInlineFunctionsWithReifiedTypeParametersLoweringPhase)
|
prerequisite = setOf(BridgesConstructionPhase, RemoveInlineFunctionsWithReifiedTypeParametersLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val SecondaryCtorLoweringPhase = makeJsPhase(
|
private val SecondaryConstructorLoweringPhase = makeJsPhase(
|
||||||
{ context, module ->
|
{ context, module -> SecondaryConstructorLowering(context).lower(module) },
|
||||||
SecondaryCtorLowering(context).run {
|
name = "SecondaryConstructorLoweringPhase",
|
||||||
constructorProcessorLowering.runOnFilesPostfix(module.files + context.dependencies.flatMap { it.files })
|
description = "Generate static functions for each secondary constructor",
|
||||||
constructorRedirectorLowering.lower(module)
|
prerequisite = setOf(InnerClassesLoweringPhase)
|
||||||
}
|
)
|
||||||
},
|
|
||||||
name = "SecondaryCtorLoweringPhase",
|
private val SecondaryFactoryInjectorLoweringPhase = makeJsPhase(
|
||||||
description = "Generate static functions for each secondary constructor and replace usages",
|
{ context, module -> SecondaryFactoryInjectorLowering(context).lower(module) },
|
||||||
|
name = "SecondaryFactoryInjectorLoweringPhase",
|
||||||
|
description = "Replace usage of secondary constructor with corresponding static function",
|
||||||
prerequisite = setOf(InnerClassesLoweringPhase)
|
prerequisite = setOf(InnerClassesLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -355,7 +357,8 @@ val jsPhases = listOf(
|
|||||||
MultipleCatchesLoweringPhase,
|
MultipleCatchesLoweringPhase,
|
||||||
BridgesConstructionPhase,
|
BridgesConstructionPhase,
|
||||||
TypeOperatorLoweringPhase,
|
TypeOperatorLoweringPhase,
|
||||||
SecondaryCtorLoweringPhase,
|
SecondaryConstructorLoweringPhase,
|
||||||
|
SecondaryFactoryInjectorLoweringPhase,
|
||||||
InlineClassLoweringPhase,
|
InlineClassLoweringPhase,
|
||||||
AutoboxingTransformerPhase,
|
AutoboxingTransformerPhase,
|
||||||
BlockDecomposerLoweringPhase,
|
BlockDecomposerLoweringPhase,
|
||||||
|
|||||||
+190
-209
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
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.copyTo
|
||||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
|
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.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
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.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
|
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.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
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.deepCopyWithSymbols
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
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.IrElementTransformer
|
||||||
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
|
||||||
|
|
||||||
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(irClass: IrClass) {
|
||||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
if (irClass.isInline) return
|
||||||
irDeclarationContainer.declarations.filterIsInstance<IrClass>().forEach {
|
|
||||||
if (!it.isInline) // Inline classes are lowered separately
|
|
||||||
lowerClass(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val constructorRedirectorLowering = object : DeclarationContainerLoweringPass {
|
irClass.declarations.transformFlat {
|
||||||
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 {
|
|
||||||
if (it is IrConstructor) {
|
if (it is IrConstructor) {
|
||||||
oldCtorToNewMap[it.symbol]?.let { (newInit, newCreate) ->
|
if (it.isPrimary) null else transformConstructor(it, irClass)
|
||||||
listOf(newInit.owner, newCreate.owner)
|
|
||||||
}
|
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lowerClass(irClass: IrClass): List<IrSimpleFunction> {
|
private fun transformConstructor(constructor: IrConstructor, irClass: IrClass): List<IrSimpleFunction> {
|
||||||
val className = irClass.name.asString()
|
val stubs = oldCtorToNewMap.getOrPut(constructor) {
|
||||||
val oldConstructors = mutableListOf<IrConstructor>()
|
buildConstructorStubDeclarations(constructor, irClass)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
private class ThisUsageReplaceTransformer(
|
||||||
val function: IrFunctionSymbol,
|
val function: IrFunctionSymbol,
|
||||||
val symbolMapping: Map<IrValueParameter, IrValueParameter>
|
val symbolMapping: Map<IrValueParameter, IrValueParameter>
|
||||||
) :
|
) : IrElementTransformerVoid() {
|
||||||
IrElementTransformerVoid() {
|
|
||||||
|
|
||||||
val newThisSymbol = symbolMapping.values.last().symbol
|
val newThisSymbol = symbolMapping.values.last().symbol
|
||||||
|
|
||||||
@@ -125,154 +130,130 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) {
|
|||||||
IrGetValueImpl(expression.startOffset, expression.endOffset, newThisSymbol.owner.type, newThisSymbol)
|
IrGetValueImpl(expression.startOffset, expression.endOffset, newThisSymbol.owner.type, newThisSymbol)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression =
|
override fun visitGetValue(expression: IrGetValue) = symbolMapping[expression.symbol.owner]?.let {
|
||||||
if (expression.symbol.owner in symbolMapping) IrGetValueImpl(
|
expression.run { IrGetValueImpl(startOffset, endOffset, type, it.symbol, origin) }
|
||||||
expression.startOffset,
|
} ?: expression
|
||||||
expression.endOffset,
|
}
|
||||||
expression.type,
|
}
|
||||||
symbolMapping[expression.symbol.owner]!!.symbol,
|
|
||||||
expression.origin
|
private fun IrTypeParameter.toIrType() = IrSimpleTypeImpl(symbol, true, emptyList(), emptyList())
|
||||||
) else {
|
|
||||||
expression
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createInitConstructor(
|
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement {
|
||||||
declaration: IrConstructor,
|
super.visitDelegatingConstructorCall(expression, data)
|
||||||
klass: IrClass,
|
|
||||||
name: String,
|
|
||||||
type: IrType
|
|
||||||
): IrSimpleFunction {
|
|
||||||
|
|
||||||
val thisParam = JsIrBuilder.buildValueParameter("\$this", declaration.valueParameters.size, type)
|
val target = expression.symbol.owner
|
||||||
val oldThisReceiver = klass.thisReceiver!!
|
|
||||||
val functionName = "${name}_\$Init\$"
|
|
||||||
|
|
||||||
return JsIrBuilder.buildFunction(
|
return if (target.isSecondaryConstructorCall) {
|
||||||
functionName,
|
val klass = target.parentAsClass
|
||||||
type,
|
val ctor = oldCtorToNewMap.getOrPut(target) { buildConstructorStubDeclarations(target, klass) }
|
||||||
declaration.parent,
|
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, ctor.delegate.symbol, data)
|
||||||
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 readThis = expression.run {
|
||||||
|
if (data!! is IrConstructor) {
|
||||||
val newValueParameters = declaration.valueParameters.map { p -> p.copyTo(it) }
|
IrGetValueImpl(startOffset, endOffset, type, klass.thisReceiver!!.symbol)
|
||||||
it.valueParameters += (newValueParameters + thisParam)
|
} else {
|
||||||
|
IrGetValueImpl(startOffset, endOffset, type, data.valueParameters.last().symbol)
|
||||||
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
|
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement {
|
for (i in 0 until call.valueArgumentsCount) {
|
||||||
super.visitDelegatingConstructorCall(expression, data)
|
putValueArgument(i, call.getValueArgument(i))
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-5
@@ -44,12 +44,10 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
|
|||||||
private val causeName get() = JsIrBuilder.buildString(stringType, "cause")
|
private val causeName get() = JsIrBuilder.buildString(stringType, "cause")
|
||||||
private val nameName get() = JsIrBuilder.buildString(stringType, "name")
|
private val nameName get() = JsIrBuilder.buildString(stringType, "name")
|
||||||
|
|
||||||
private val throwableClass = context.symbolTable.referenceClass(
|
private val throwableClass = context.throwableClass
|
||||||
context.getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable")))
|
private val throwableConstructors = context.throwableConstructors
|
||||||
).owner
|
|
||||||
private val throwableConstructors = throwableClass.declarations.filterIsInstance<IrConstructor>()
|
|
||||||
|
|
||||||
private val defaultCtor = throwableConstructors.single { it.valueParameters.size == 0 }
|
private val defaultCtor = context.defaultThrowableCtor
|
||||||
private val toString =
|
private val toString =
|
||||||
throwableClass.declarations.filterIsInstance<IrSimpleFunction>().single { it.name == Name.identifier("toString") }
|
throwableClass.declarations.filterIsInstance<IrSimpleFunction>().single { it.name == Name.identifier("toString") }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user