[K/N] Support pre-creating for objects with only constant properties
This commit is contained in:
+16
-13
@@ -20,28 +20,31 @@ import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Native
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable
|
||||
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrReturn
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||
|
||||
private fun IrConstructor.isAnyConstructorDelegation(context: Context): Boolean {
|
||||
val statements = this.body?.statements ?: return false
|
||||
if (statements.size != 2) return false
|
||||
val lastStatement = statements[1]
|
||||
if (lastStatement !is IrReturn ||
|
||||
(lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false
|
||||
val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false
|
||||
val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false
|
||||
return constructor.constructedClass.isAny()
|
||||
private fun IrConstructor.isAutogeneratedSimpleConstructor(context: Context): Boolean {
|
||||
if (!this.isPrimary) return false
|
||||
val statements = this.body?.statements ?: return false
|
||||
if (statements.size < 2) return false
|
||||
val lastStatement = statements.last()
|
||||
if (lastStatement !is IrReturn ||
|
||||
(lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false
|
||||
val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false
|
||||
val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false
|
||||
if (!constructor.constructedClass.isAny()) return false
|
||||
return statements.asSequence().take(statements.size - 1).drop(1).all {
|
||||
it is IrBlock && it.origin == IrStatementOrigin.INITIALIZE_FIELD
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: shall we memoize that property?
|
||||
internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean {
|
||||
if (!context.shouldOptimize()) return false
|
||||
if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true
|
||||
val fields = context.getLayoutBuilder(this).fields
|
||||
return fields.isEmpty() && this.constructors.all { it.isAnyConstructorDelegation(context) }
|
||||
return fields.all { (it.correspondingPropertySymbol?.owner?.isConst ?: false) && (it.initializer?.expression is IrConst<*>) } &&
|
||||
this.constructors.all { it.isAutogeneratedSimpleConstructor(context) }
|
||||
}
|
||||
|
||||
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
@@ -1262,6 +1262,12 @@ task localClass_localHierarchy(type: KonanLocalTest) {
|
||||
source = "codegen/localClass/localHierarchy.kt"
|
||||
}
|
||||
|
||||
standaloneTest("objectDeclaration_globalConstants") {
|
||||
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
|
||||
flags = ["-opt", "-Xopt-in=kotlin.native.internal.InternalForKotlinNative", "-tr"]
|
||||
source = "codegen/objectDeclaration/globalConstants.kt"
|
||||
}
|
||||
|
||||
task localClass_objectExpressionInProperty(type: KonanLocalTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/localClass/objectExpressionInProperty.kt"
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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 codegen.objectDeclaration.globalConstants
|
||||
|
||||
import kotlin.test.*
|
||||
import kotlin.native.internal.*
|
||||
|
||||
object EmptyClass {}
|
||||
|
||||
@Test fun checkEmptyClass() {
|
||||
assertTrue(EmptyClass.isPermanent())
|
||||
}
|
||||
|
||||
|
||||
object ClassWithConstants {
|
||||
const val A = 1
|
||||
const val B = 2L
|
||||
const val C = 3.0
|
||||
const val D = 4.0f
|
||||
const val E = 5.toShort()
|
||||
const val F = 6.toByte()
|
||||
const val G = "8"
|
||||
}
|
||||
|
||||
@Test fun checkInit() {
|
||||
assertTrue(ClassWithConstants.isPermanent())
|
||||
assertEquals(1, ClassWithConstants.A)
|
||||
assertEquals(2, ClassWithConstants.B)
|
||||
assertEquals(3.0, ClassWithConstants.C)
|
||||
assertEquals(4.0f, ClassWithConstants.D)
|
||||
assertEquals(5, ClassWithConstants.E)
|
||||
assertEquals(6, ClassWithConstants.F)
|
||||
assertEquals("8", ClassWithConstants.G)
|
||||
|
||||
assertEquals(1, (ClassWithConstants::A)())
|
||||
assertEquals(2, (ClassWithConstants::B)())
|
||||
assertEquals(3.0, (ClassWithConstants::C)())
|
||||
assertEquals(4.0f, (ClassWithConstants::D)())
|
||||
assertEquals(5, (ClassWithConstants::E)())
|
||||
assertEquals(6, (ClassWithConstants::F)())
|
||||
assertEquals("8", (ClassWithConstants::G)())
|
||||
}
|
||||
|
||||
|
||||
var ClassWithConstructorInitialized = 0
|
||||
|
||||
object ClassWithConstructor {
|
||||
init {
|
||||
ClassWithConstructorInitialized += 1
|
||||
}
|
||||
const val A = 1;
|
||||
}
|
||||
|
||||
@Test fun checkConstructor() {
|
||||
assertEquals(0, ClassWithConstructorInitialized)
|
||||
assertEquals(1, ClassWithConstructor.A)
|
||||
assertEquals(1, ClassWithConstructorInitialized)
|
||||
assertEquals(1, ClassWithConstructor.A)
|
||||
assertEquals(1, ClassWithConstructorInitialized)
|
||||
assertFalse(ClassWithConstructor.isPermanent())
|
||||
}
|
||||
|
||||
object ClassWithField {
|
||||
val x = 4
|
||||
}
|
||||
|
||||
@Test fun checkField() {
|
||||
assertFalse(ClassWithField.isPermanent())
|
||||
}
|
||||
|
||||
object ClassWithComputedField {
|
||||
val x : Int
|
||||
get() = 4
|
||||
}
|
||||
|
||||
@Test fun checkComputedField() {
|
||||
assertTrue(ClassWithComputedField.isPermanent())
|
||||
}
|
||||
Reference in New Issue
Block a user