From 2d0535a713f05df378b5152506794ae4e97b6069 Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Fri, 30 Oct 2020 16:15:15 +0100 Subject: [PATCH] [JS_IR] Invoke companion init block while instantiating a class KT-40740, squashed rr/shagen/KT-40740-static-init-js-ir --- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 9 ++++- .../lower/InvokeStaticInitializersLowering.kt | 36 +++++++++++++++++++ .../AnonymousInitializerGenerator.kt | 4 +-- .../classes/classObjectAsStaticInitializer.kt | 18 ++++++---- 4 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InvokeStaticInitializersLowering.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 96b5ac3c756..3ea8f0c8a23 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -272,7 +272,6 @@ private val enumClassConstructorBodyLoweringPhase = makeBodyLoweringPhase( description = "Transform Enum Class into regular Class" ) - private val enumEntryInstancesLoweringPhase = makeDeclarationTransformerPhase( ::EnumEntryInstancesLowering, name = "EnumEntryInstancesLowering", @@ -647,6 +646,13 @@ private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase( description = "Create lazy object instance generator functions" ) +private val invokeStaticInitializersPhase = makeBodyLoweringPhase( + ::InvokeStaticInitializersLowering, + name = "IntroduceStaticInitializersLowering", + description = "Invoke companion object's initializers from companion object in object constructor", + prerequisite = setOf(objectDeclarationLoweringPhase) +) + private val objectUsageLoweringPhase = makeBodyLoweringPhase( ::ObjectUsageLowering, name = "ObjectUsageLowering", @@ -742,6 +748,7 @@ val loweringList = listOf( blockDecomposerLoweringPhase, constLoweringPhase, objectDeclarationLoweringPhase, + invokeStaticInitializersPhase, objectUsageLoweringPhase, captureStackTraceInThrowablesPhase, callsLoweringPhase, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InvokeStaticInitializersLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InvokeStaticInitializersLowering.kt new file mode 100644 index 00000000000..688a5965729 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InvokeStaticInitializersLowering.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2020 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.BodyLoweringPass +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.IrStatementContainer +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.util.companionObject +import org.jetbrains.kotlin.ir.util.constructedClass +import org.jetbrains.kotlin.ir.util.isEffectivelyExternal + +class InvokeStaticInitializersLowering(val context: JsIrBackendContext) : BodyLoweringPass { + override fun lower(irBody: IrBody, container: IrDeclaration) { + if (container !is IrConstructor) return + + val irClass = container.constructedClass + if (irClass.isEffectivelyExternal()) { + return + } + + val companionObject = irClass.companionObject() ?: return + + val instance = context.mapping.objectToGetInstanceFunction[companionObject] ?: return + + val getInstanceCall = IrCallImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, instance.symbol, 0, 0) + + (irBody as IrStatementContainer).statements.add(0, getInstanceCall) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AnonymousInitializerGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AnonymousInitializerGenerator.kt index 0e9789480cf..6561744afbe 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AnonymousInitializerGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AnonymousInitializerGenerator.kt @@ -16,8 +16,8 @@ package org.jetbrains.kotlin.psi2ir.generators +import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.psi.KtAnonymousInitializer import org.jetbrains.kotlin.psi.KtBlockExpression @@ -31,7 +31,7 @@ class AnonymousInitializerGenerator( fun generateAnonymousInitializerDeclaration( ktAnonymousInitializer: KtAnonymousInitializer, irClass: IrClass - ): IrDeclaration = + ): IrAnonymousInitializer = context.symbolTable.declareAnonymousInitializer( ktAnonymousInitializer.startOffsetSkippingComments, ktAnonymousInitializer.endOffset, IrDeclarationOrigin.DEFINED, irClass.descriptor diff --git a/compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt b/compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt index 33350be544d..aa92c3f0cf4 100644 --- a/compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt +++ b/compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt @@ -1,22 +1,28 @@ // IGNORE_BACKEND: NATIVE -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -var global = 0; +var global = "A" class C { + init { + global += "D" + } + companion object { init { - global = 1; + global += "B" + } + + init { + global += "C" } } } fun box(): String { - if (global != 0) { + if (global != "A") { return "fail1: global = $global" } val c = C() - if (global == 1) return "OK" else return "fail2: global = $global" + if (global == "ABCD") return "OK" else return "fail2: global = $global" }