[K/N] Call class static initializer in constructors

^KT-59058
This commit is contained in:
Pavel Kunyavskiy
2023-06-05 12:56:56 +02:00
committed by Space Team
parent c40bf720d9
commit 12455451fc
6 changed files with 46 additions and 28 deletions
@@ -1,7 +1,5 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS
// KT-59058
// IGNORE_BACKEND: NATIVE
var global = "A"
class C {
@@ -1,7 +1,5 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: NESTED_OBJECT_INIT
// KT-59058
// IGNORE_BACKEND: NATIVE
// WITH_STDLIB
// WITH_COROUTINES
import helpers.*
@@ -19,10 +19,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.hasNonConstInitializer
import org.jetbrains.kotlin.ir.util.simpleFunctions
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -96,18 +93,22 @@ internal class StaticInitializersLowering(val context: Context) : FileLoweringPa
)
else null
fun IrFunction.addInitializersCall() {
val body = body ?: return
val statements = (body as IrBlockBody).statements
context.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run {
// The order of calling initializers: first global, then thread-local.
// It is ok for a thread local top level property to reference a global, but not vice versa.
threadLocalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) }
globalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) }
}
}
container.simpleFunctions()
.filter { it.dispatchReceiverParameter == null }
.filterNot { it.origin == DECLARATION_ORIGIN_ENTRY_POINT }
.forEach {
val body = it.body ?: return@forEach
val statements = (body as IrBlockBody).statements
context.createIrBuilder(it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run {
// The order of calling initializers: first global, then thread-local.
// It is ok for a thread local top level property to reference a global, but not vice versa.
threadLocalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) }
globalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) }
}
}
.forEach { it.addInitializersCall() }
(container as? IrClass)?.constructors?.forEach { it.addInitializersCall() }
}
private fun buildInitFileFunction(container: IrDeclarationContainer, name: String, origin: IrDeclarationOrigin) = context.irFactory.buildFun {
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.backend.konan.DirectedGraphCondensationBuilder
import org.jetbrains.kotlin.backend.konan.DirectedGraphMultiNode
import org.jetbrains.kotlin.backend.konan.ir.actualCallee
import org.jetbrains.kotlin.backend.konan.ir.isVirtualCall
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
import org.jetbrains.kotlin.backend.konan.logMultiple
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.ir.IrElement
@@ -542,6 +544,23 @@ internal object StaticInitializersOptimization {
expression.transformChildren(this, data)
val callee = expression.actualCallee
/*
* There can be construction like `initInstance(constructorCall())` in IR
* Which can be transformed to `initInstance({ callClassStaticInits(); conctructorCall() })`
* which is not valid for code-gen, as it expects the intrinsic argument to be a constructor call.
*
* So we here fix it by postporcessing to `{ callClassStaticInitis(); initInstance(constructorCall()) }`
*/
if (tryGetIntrinsicType(expression) == IntrinsicType.INIT_INSTANCE) {
val constructorArg = expression.getValueArgument(1)
if (constructorArg is IrBlock) {
val realConstructorArg = constructorArg.statements.last() as IrConstructorCall
constructorArg.statements[constructorArg.statements.lastIndex] = expression.apply {
putValueArgument(1, realConstructorArg)
}
return constructorArg
}
}
val body = callee.body ?: return expression
val initializerCalls = (body as IrBlockBody).statements
.take(2) // The very first statements by construction.
@@ -18,14 +18,6 @@ fun getStackTrace() : List<String> {
open class Foo {
open fun foo(): List<String> { return emptyList() }
companion object {
var trace: List<String> = emptyList()
init {
trace = getStackTrace()
}
}
}
private class Bar : Foo() {
@@ -40,6 +32,16 @@ object Object {
}
}
class WithCompanion {
companion object {
var trace: List<String> = emptyList()
init {
trace = getStackTrace()
}
}
}
enum class E {
A {
init {
@@ -23,8 +23,8 @@ func testKotlin2Objc() throws {
}
func testCompanionObject() throws {
let trace = Foo.companion.trace
try assertTrue(trace[8].contains("objc2kotlin_kclass:Foo#companion"))
let trace = WithCompanion.companion.trace
try assertTrue(trace[8].contains("objc2kotlin_kclass:WithCompanion#companion"))
}
func testStandaloneObject() throws {