[JS IR BE] Properly handle primary constructors

- force primary constructor to be created by codegen
 - make sure initializer is invoked once in primary constructor
 - make one of coroutine constructor primary
 - remove hack from codegen
This commit is contained in:
Roman Artemev
2019-07-09 16:53:29 +03:00
committed by romanart
parent a112345418
commit 6ab46204cb
5 changed files with 73 additions and 15 deletions
@@ -392,7 +392,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
coroutineClass.defaultType,
isInline = false,
isExternal = false,
isPrimary = false
isPrimary = true
).apply {
d.bind(this)
parent = coroutineClass
@@ -275,9 +275,7 @@ class JsIrBackendContext(
}
val throwableConstructors by lazy { throwableClass.owner.declarations.filterIsInstance<IrConstructor>().map { it.symbol } }
val defaultThrowableCtor by lazy { throwableConstructors.single { it.owner.valueParameters.size == 0 } }
val defaultThrowableCtor by lazy { throwableConstructors.single { !it.owner.isPrimary && it.owner.valueParameters.size == 0 } }
private fun referenceOperators(): Map<Name, MutableMap<IrClassifierSymbol, IrSimpleFunctionSymbol>> {
val primitiveIrSymbols = irBuiltIns.primitiveIrTypes.map { it.classifierOrFail as IrClassSymbol }
@@ -260,11 +260,18 @@ private val propertiesLoweringPhase = makeJsModulePhase(
description = "Move fields and accessors out from its property"
)
private val primaryConstructorLoweringPhase = makeJsModulePhase(
::PrimaryConstructorLowering,
name = "PrimaryConstructorLowering",
description = "Creates primary constructor if it doesn't exist",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val initializersLoweringPhase = makeCustomJsModulePhase(
{ context, module -> InitializersLowering(context, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).lower(module) },
name = "InitializersLowering",
description = "Merge init block and field initializers into [primary] constructor",
prerequisite = setOf(enumClassConstructorLoweringPhase)
prerequisite = setOf(enumClassConstructorLoweringPhase, primaryConstructorLoweringPhase)
)
private val multipleCatchesLoweringPhase = makeJsModulePhase(
@@ -394,6 +401,7 @@ val jsPhases = namedIrModulePhase(
innerClassesLoweringPhase then
innerClassConstructorCallsLoweringPhase then
propertiesLoweringPhase then
primaryConstructorLoweringPhase then
initializersLoweringPhase then
// Common prefix ends
moveBodilessDeclarationsToSeparatePlacePhase then
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
// Create primary constructor if it doesn't exist
class PrimaryConstructorLowering(context: CommonBackendContext) : ClassLoweringPass {
private val unitType = context.irBuiltIns.unitType
override fun lower(irClass: IrClass) {
val constructors = irClass.declarations.filterIsInstance<IrConstructor>()
if (constructors.any { it.isPrimary }) return
val primary = createPrimaryConstructor(irClass)
val initializeTransformer = object : IrElementTransformerVoid() {
override fun visitDeclaration(declaration: IrDeclaration) = declaration // optimize visiting
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall) = expression.run {
IrDelegatingConstructorCallImpl(startOffset, endOffset, type, primary.symbol)
}
}
constructors.forEach { it.transformChildrenVoid(initializeTransformer) }
}
private object SYNTHETIC_PRIMARY_CONSTRUCTOR : IrDeclarationOriginImpl("SYNTHETIC_PRIMARY_CONSTRUCTOR")
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
val declaration = irClass.addConstructor {
origin = SYNTHETIC_PRIMARY_CONSTRUCTOR
isPrimary = true
visibility = Visibilities.PRIVATE
}
declaration.body = irClass.run {
IrBlockBodyImpl(startOffset, endOffset, listOf(IrInstanceInitializerCallImpl(startOffset, endOffset, symbol, unitType)))
}
return declaration
}
}
@@ -35,7 +35,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
fun generate(): JsStatement {
assert(!irClass.descriptor.isExpect)
maybeGeneratePrimaryConstructor()
val transformer = IrDeclarationToJsTransformer()
// Properties might be lowered out of classes
@@ -134,15 +133,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return null
}
private fun maybeGeneratePrimaryConstructor() {
if (!irClass.declarations.any { it is IrConstructor }) {
val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}")
func.name = className
classBlock.statements += func.makeStmt()
classModel.preDeclarationBlock.statements += generateInheritanceCode()
}
}
private fun generateInheritanceCode(): List<JsStatement> {
if (baseClass == null || baseClass.isAny()) {
return emptyList()