[K/N] Rework object and enum classes initialization
Now it works with common logic for all static scope not with custom one.
This commit is contained in:
committed by
Space Team
parent
7006eb938d
commit
d3adfec2fb
+8
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters
|
||||
import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction
|
||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
@@ -257,7 +258,13 @@ private class ExportedElement(val kind: ElementKind,
|
||||
// Produce instance getter if needed.
|
||||
if (isSingletonObject) {
|
||||
generateFunction(owner.codegen, owner.kGetObjectFuncType, "${cname}_instance") {
|
||||
val value = getObjectValue(irClass, ExceptionHandler.Caller, null)
|
||||
val value = call(
|
||||
owner.codegen.llvmFunction(context.getObjectClassInstanceFunction(irClass)),
|
||||
emptyList(),
|
||||
Lifetime.GLOBAL,
|
||||
ExceptionHandler.Caller,
|
||||
false,
|
||||
returnSlot)
|
||||
ret(value)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-6
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
||||
import org.jetbrains.kotlin.konan.target.Architecture
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.needSmallBinary
|
||||
import org.jetbrains.kotlin.library.SerializedIrModule
|
||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -50,15 +49,13 @@ internal class NativeMapping : DefaultMapping() {
|
||||
data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
|
||||
|
||||
val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
|
||||
val enumImplObjects = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrClass>()
|
||||
val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||
val enumEntriesMaps = mutableMapOf<IrClass, Map<Name, LoweredEnumEntryDescription>>()
|
||||
val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
|
||||
val notLoweredInlineFunctions = mutableMapOf<IrFunctionSymbol, IrFunction>()
|
||||
val companionObjectCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
val outerThisCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
val lateinitPropertyCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrProperty, IrSimpleFunction>()
|
||||
val enumValuesCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
val objectInstanceGetter = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
}
|
||||
|
||||
internal class Context(config: KonanConfig) : KonanBackendContext(config), ConfigChecks {
|
||||
@@ -95,8 +92,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config), Confi
|
||||
val innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) }
|
||||
val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) }
|
||||
val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) }
|
||||
val enumsSupport by lazy { EnumsSupport(mapping, ir.symbols, irBuiltIns, irFactory) }
|
||||
val cachesAbiSupport by lazy { CachesAbiSupport(mapping, ir.symbols, irFactory) }
|
||||
val enumsSupport by lazy { EnumsSupport(mapping, irBuiltIns, irFactory) }
|
||||
val cachesAbiSupport by lazy { CachesAbiSupport(mapping, irFactory) }
|
||||
|
||||
open class LazyMember<T>(val initializer: Context.() -> T) {
|
||||
operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this)
|
||||
|
||||
+6
@@ -250,6 +250,12 @@ internal val initializersPhase = makeKonanFileLoweringPhase(
|
||||
prerequisite = setOf(enumConstructorsPhase)
|
||||
)
|
||||
|
||||
internal val objectClassesPhase = makeKonanFileLoweringPhase(
|
||||
::ObjectClassLowering,
|
||||
name = "ObjectClasses",
|
||||
description = "Enum constructors lowering"
|
||||
)
|
||||
|
||||
internal val localFunctionsPhase = makeKonanFileOpPhase(
|
||||
op = { context, irFile ->
|
||||
LocalDelegatedPropertiesLowering().lower(irFile)
|
||||
|
||||
+1
@@ -292,6 +292,7 @@ internal val allLoweringsPhase = SameTypeNamedCompilerPhase(
|
||||
coroutinesPhase,
|
||||
typeOperatorPhase,
|
||||
expressionBodyTransformPhase,
|
||||
objectClassesPhase,
|
||||
constantInliningPhase,
|
||||
staticInitializersPhase,
|
||||
bridgesPhase,
|
||||
|
||||
+1
-1
@@ -1387,7 +1387,7 @@ private object IgnoredUnitArgumentPassing : ArgumentPassing {
|
||||
}
|
||||
|
||||
override fun CCallbackBuilder.receiveValue(): IrExpression {
|
||||
return bridgeBuilder.kotlinIrBuilder.irGetObject(irBuiltIns.unitClass)
|
||||
return bridgeBuilder.kotlinIrBuilder.irCall(symbols.theUnitInstance)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -498,8 +498,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
|
||||
}
|
||||
return declarations.mapNotNull {
|
||||
when (it) {
|
||||
is IrField -> it.takeIf { it.isReal }?.toFieldInfo()
|
||||
is IrProperty -> it.takeIf { it.isReal }?.backingField?.toFieldInfo()
|
||||
is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo()
|
||||
is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -232,6 +232,8 @@ internal class KonanSymbols(
|
||||
|
||||
val reinterpret = internalFunction("reinterpret")
|
||||
|
||||
val theUnitInstance = internalFunction("theUnitInstance")
|
||||
|
||||
val ieee754Equals = internalFunctions("ieee754Equals")
|
||||
|
||||
val equals = irBuiltIns.findBuiltInClassMemberFunctions(any, Name.identifier("equals")).single()
|
||||
@@ -313,8 +315,6 @@ internal class KonanSymbols(
|
||||
|
||||
val initInstance = internalFunction("initInstance")
|
||||
|
||||
val freeze = irBuiltIns.findFunctions(Name.identifier("freeze"), "kotlin", "native", "concurrent").single()
|
||||
|
||||
val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io")
|
||||
.single { it.descriptor.valueParameters.singleOrNull()?.type == (irBuiltIns as IrBuiltInsOverDescriptors).builtIns.stringType }
|
||||
|
||||
|
||||
+4
-123
@@ -23,28 +23,6 @@ import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||
|
||||
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.all { it.isConst } && this.constructors.all { it.isAutogeneratedSimpleConstructor(context) }
|
||||
}
|
||||
|
||||
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
fun llvmFunction(function: IrFunction): LlvmCallable =
|
||||
@@ -1260,109 +1238,12 @@ internal abstract class FunctionGenerationContext(
|
||||
return null
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getObjectValue(irClass: IrClass, exceptionHandler: ExceptionHandler,
|
||||
startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null,
|
||||
resultSlot: LLVMValueRef? = null
|
||||
startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null,
|
||||
resultSlot: LLVMValueRef? = null
|
||||
): LLVMValueRef {
|
||||
// TODO: could be processed the same way as other stateless objects.
|
||||
if (irClass.isUnit()) {
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
|
||||
if (irClass.isCompanion) {
|
||||
val parent = irClass.parent as IrClass
|
||||
if (parent.isObjCClass()) {
|
||||
// TODO: cache it too.
|
||||
return call(
|
||||
codegen.llvmFunction(context.ir.symbols.interopInterpretObjCPointer.owner),
|
||||
listOf(getObjCClass(parent, exceptionHandler)),
|
||||
Lifetime.GLOBAL,
|
||||
exceptionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val storageKind = irClass.storageKind(context)
|
||||
|
||||
val objectPtr = if (isExternal(irClass)) {
|
||||
when (storageKind) {
|
||||
// If thread local object is imported - access it via getter function.
|
||||
ObjectStorageKind.THREAD_LOCAL -> {
|
||||
val valueGetterName = irClass.threadLocalObjectStorageGetterSymbolName
|
||||
val valueGetterFunction = LLVMGetNamedFunction(llvm.module, valueGetterName)
|
||||
?: addLlvmFunctionWithDefaultAttributes(
|
||||
context,
|
||||
llvm.module,
|
||||
valueGetterName,
|
||||
functionType(kObjHeaderPtrPtr, false)
|
||||
)
|
||||
call(valueGetterFunction,
|
||||
listOf(),
|
||||
resultLifetime = Lifetime.GLOBAL,
|
||||
exceptionHandler = exceptionHandler)
|
||||
}
|
||||
|
||||
// If global object is imported - import it's storage directly.
|
||||
ObjectStorageKind.PERMANENT, ObjectStorageKind.SHARED -> {
|
||||
val llvmType = irClass.defaultType.toLLVMType(llvm)
|
||||
importGlobal(
|
||||
irClass.globalObjectStorageSymbolName,
|
||||
llvmType,
|
||||
origin = irClass.llvmSymbolOrigin
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Local globals and thread locals storage info is stored in our map.
|
||||
val singleton = llvmDeclarations.forSingleton(irClass)
|
||||
val instanceAddress = singleton.instanceStorage
|
||||
instanceAddress.getAddress(this)
|
||||
}
|
||||
|
||||
when (storageKind) {
|
||||
ObjectStorageKind.SHARED ->
|
||||
// If current file used a shared object, make file's (de)initializer function deinit it.
|
||||
llvm.globalSharedObjects += objectPtr
|
||||
ObjectStorageKind.THREAD_LOCAL ->
|
||||
// If current file used locally defined TLS objects, make file's (de)initializer function
|
||||
// init and deinit TLS.
|
||||
// Note: for exported TLS objects a getter is generated in a file they're defined in. Which
|
||||
// adds TLS init and deinit to that file's (de)initializer function.
|
||||
if (!isExternal(irClass))
|
||||
llvm.fileUsesThreadLocalObjects = true
|
||||
ObjectStorageKind.PERMANENT -> { /* Do nothing, no need to free such an instance. */ }
|
||||
}
|
||||
|
||||
if (storageKind == ObjectStorageKind.PERMANENT) {
|
||||
return loadSlot(objectPtr, false)
|
||||
}
|
||||
val bbInit = basicBlock("label_init", startLocationInfo, endLocationInfo)
|
||||
val bbExit = basicBlock("label_continue", startLocationInfo, endLocationInfo)
|
||||
val objectVal = loadSlot(objectPtr, false, memoryOrder = LLVMAtomicOrdering.LLVMAtomicOrderingAcquire)
|
||||
val objectInitialized = icmpUGt(ptrToInt(objectVal, codegen.intPtrType), codegen.immOneIntPtrType)
|
||||
val bbCurrent = currentBlock
|
||||
condBr(objectInitialized, bbExit, bbInit)
|
||||
|
||||
positionAtEnd(bbInit)
|
||||
val typeInfo = codegen.typeInfoForAllocation(irClass)
|
||||
val defaultConstructor = irClass.constructors.single { it.valueParameters.size == 0 }
|
||||
val ctor = codegen.llvmFunction(defaultConstructor).llvmValue
|
||||
val initFunction =
|
||||
if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) {
|
||||
llvm.initSingletonFunction
|
||||
} else {
|
||||
llvm.initThreadLocalSingleton
|
||||
}
|
||||
val args = listOf(objectPtr, typeInfo, ctor)
|
||||
val newValue = call(initFunction, args, Lifetime.GLOBAL, exceptionHandler, resultSlot = resultSlot)
|
||||
val bbInitResult = currentBlock
|
||||
br(bbExit)
|
||||
|
||||
positionAtEnd(bbExit)
|
||||
val valuePhi = phi(irClass.defaultType.toLLVMType(llvm))
|
||||
addPhiIncoming(valuePhi, bbCurrent to objectVal, bbInitResult to newValue)
|
||||
|
||||
return valuePhi
|
||||
error("Should be lowered out: ${irClass.render()} while generating ${irFunction?.dump()}")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-3
@@ -481,8 +481,6 @@ internal class Llvm(private val context: Context, val module: LLVMModuleRef) : R
|
||||
|
||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
||||
val initThreadLocalSingleton = importRtFunction("InitThreadLocalSingleton")
|
||||
val initSingletonFunction = importRtFunction("InitSingleton")
|
||||
val initAndRegisterGlobalFunction = importRtFunction("InitAndRegisterGlobal")
|
||||
val updateHeapRefFunction = importRtFunction("UpdateHeapRef")
|
||||
val updateStackRefFunction = importRtFunction("UpdateStackRef")
|
||||
@@ -564,7 +562,6 @@ internal class Llvm(private val context: Context, val module: LLVMModuleRef) : R
|
||||
val compilerUsedGlobals = mutableListOf<LLVMValueRef>()
|
||||
val irStaticInitializers = mutableListOf<IrStaticInitializer>()
|
||||
val otherStaticInitializers = mutableListOf<LLVMValueRef>()
|
||||
var fileUsesThreadLocalObjects = false
|
||||
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
|
||||
val initializersGenerationState = InitializersGenerationState()
|
||||
val boxCacheGlobals = mutableMapOf<BoxCache, StaticData.Global>()
|
||||
|
||||
+2
@@ -66,6 +66,7 @@ internal enum class IntrinsicType {
|
||||
IMMUTABLE_BLOB,
|
||||
INIT_INSTANCE,
|
||||
IS_EXPERIMENTAL_MM,
|
||||
THE_UNIT_INSTANCE,
|
||||
// Enums
|
||||
ENUM_VALUES,
|
||||
ENUM_VALUE_OF,
|
||||
@@ -241,6 +242,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
||||
IntrinsicType.IDENTITY -> emitIdentity(args)
|
||||
IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args)
|
||||
IntrinsicType.IS_EXPERIMENTAL_MM -> emitIsExperimentalMM()
|
||||
IntrinsicType.THE_UNIT_INSTANCE -> theUnitInstanceRef.llvm
|
||||
IntrinsicType.GET_CONTINUATION,
|
||||
IntrinsicType.RETURN_IF_SUSPENDED,
|
||||
IntrinsicType.INTEROP_BITS_TO_FLOAT,
|
||||
|
||||
+27
-67
@@ -46,12 +46,6 @@ internal enum class FieldStorageKind {
|
||||
THREAD_LOCAL
|
||||
}
|
||||
|
||||
internal enum class ObjectStorageKind {
|
||||
PERMANENT,
|
||||
THREAD_LOCAL,
|
||||
SHARED
|
||||
}
|
||||
|
||||
// TODO: maybe unannotated singleton objects shall be accessed from main thread only as well?
|
||||
internal fun IrField.storageKind(context: Context): FieldStorageKind {
|
||||
// TODO: Is this correct?
|
||||
@@ -77,12 +71,6 @@ internal fun IrField.needsGCRegistration(context: Context) =
|
||||
(hasNonConstInitializer || // which are initialized from heap object
|
||||
!isFinal) // or are not final
|
||||
|
||||
internal fun IrClass.storageKind(context: Context): ObjectStorageKind = when {
|
||||
this.annotations.hasAnnotation(KonanFqNames.threadLocal) &&
|
||||
context.config.threadsAreAllowed -> ObjectStorageKind.THREAD_LOCAL
|
||||
this.hasConstStateAndNoSideEffects(context) -> ObjectStorageKind.PERMANENT
|
||||
else -> ObjectStorageKind.SHARED
|
||||
}
|
||||
|
||||
internal fun IrField.isGlobalNonPrimitive(context: Context) = when {
|
||||
type.computePrimitiveBinaryTypeOrNull() != null -> false
|
||||
@@ -883,44 +871,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.kind.isSingleton && !declaration.isUnit()) {
|
||||
val singleton = context.generationState.llvmDeclarations.forSingleton(declaration)
|
||||
val access = singleton.instanceStorage
|
||||
if (access is GlobalAddressAccess) {
|
||||
// Global objects are kept in a data segment and can be accessed by any module (if exported) and also
|
||||
// they need to be initialized statically.
|
||||
LLVMSetInitializer(access.getAddress(null), if (declaration.storageKind(context) == ObjectStorageKind.PERMANENT)
|
||||
llvm.staticData.createConstKotlinObject(declaration,
|
||||
*computeFields(declaration)).llvm else codegen.kNullObjHeaderPtr)
|
||||
} else {
|
||||
// Thread local objects are kept in a special map, so they need a getter function to be accessible
|
||||
// by other modules.
|
||||
val isObjCCompanion = declaration.isCompanion && declaration.parentAsClass.isObjCClass()
|
||||
// If can be exported and can be instantiated.
|
||||
if (declaration.isExported() && !isObjCCompanion &&
|
||||
declaration.constructors.singleOrNull() { it.valueParameters.size == 0 } != null) {
|
||||
val valueGetterName = declaration.threadLocalObjectStorageGetterSymbolName
|
||||
generateFunction(codegen,
|
||||
functionType(codegen.kObjHeaderPtrPtr, false),
|
||||
valueGetterName) {
|
||||
val value = access.getAddress(this)
|
||||
ret(value)
|
||||
}
|
||||
// Getter uses TLS object, so need to ensure that this file's (de)initializer function
|
||||
// inits and deinits TLS.
|
||||
llvm.fileUsesThreadLocalObjects = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeFields(declaration: IrClass): Array<ConstValue> {
|
||||
val fields = context.getLayoutBuilder(declaration).fields
|
||||
return Array(fields.size) { index ->
|
||||
val initializer = fields[index].irField!!.initializer!!.expression as IrConst<*>
|
||||
evaluateConst(initializer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
@@ -987,7 +937,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
is IrVararg -> return evaluateVararg (value)
|
||||
is IrBreak -> return evaluateBreak (value)
|
||||
is IrContinue -> return evaluateContinue (value)
|
||||
is IrGetObjectValue -> return evaluateGetObjectValue (value, resultSlot)
|
||||
is IrGetObjectValue -> return evaluateGetObjectValue (value)
|
||||
is IrFunctionReference -> return evaluateFunctionReference (value)
|
||||
is IrSuspendableExpression ->
|
||||
return evaluateSuspendableExpression (value, resultSlot)
|
||||
@@ -1012,14 +962,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateGetObjectValue(value: IrGetObjectValue, resultSlot: LLVMValueRef?): LLVMValueRef =
|
||||
functionGenerationContext.getObjectValue(
|
||||
value.symbol.owner,
|
||||
currentCodeContext.exceptionHandler,
|
||||
value.startLocation,
|
||||
value.endLocation,
|
||||
resultSlot
|
||||
)
|
||||
private fun evaluateGetObjectValue(value: IrGetObjectValue): LLVMValueRef {
|
||||
error("Should be lowered out: ${value.symbol.owner.render()}")
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1798,7 +1743,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
)
|
||||
if (context.config.threadsAreAllowed && value.symbol.owner.storageKind(context) == FieldStorageKind.GLOBAL)
|
||||
functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler)
|
||||
if (value.symbol.owner.shouldBeFrozen(context))
|
||||
if (value.symbol.owner.shouldBeFrozen(context) && value.origin != ObjectClassLowering.IrStatementOriginFieldPreInit)
|
||||
functionGenerationContext.freeze(valueToAssign, currentCodeContext.exceptionHandler)
|
||||
functionGenerationContext.storeAny(valueToAssign, globalAddress, false)
|
||||
}
|
||||
@@ -1909,14 +1854,29 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
val fields = if (value.constructor.owner.isConstantConstructorIntrinsic) {
|
||||
intrinsicGenerator.evaluateConstantConstructorFields(value, value.valueArguments.map { evaluateConstantValue(it) })
|
||||
} else {
|
||||
context.getLayoutBuilder(constructedClass).fields.map { field ->
|
||||
val index = value.constructor.owner.valueParameters
|
||||
.indexOfFirst { it.name.toString() == field.name }
|
||||
.takeIf { it >= 0 }
|
||||
?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}")
|
||||
evaluateConstantValue(value.valueArguments[index])
|
||||
val fields = context.getLayoutBuilder(constructedClass).fields
|
||||
val valueParameters = value.constructor.owner.valueParameters.associateBy { it.name.toString() }
|
||||
fields.map { field ->
|
||||
if (field.isConst) {
|
||||
val init = field.irField!!.initializer?.expression
|
||||
require(field.name !in valueParameters) {
|
||||
"Constant field ${field.name} of class ${constructedClass.name} shouldn't be a constructor parameter"
|
||||
}
|
||||
when (init) {
|
||||
is IrConst<*> -> evaluateConst(init)
|
||||
is IrConstantValue -> evaluateConstantValue(init)
|
||||
null -> error("Constant field ${field.name} of class ${constructedClass.name} should have initializer")
|
||||
else -> error("Unexpected constant initializer type: ${init::class}")
|
||||
}
|
||||
} else {
|
||||
val index = valueParameters[field.name]?.index
|
||||
?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}")
|
||||
evaluateConstantValue(value.valueArguments[index])
|
||||
}
|
||||
}.also {
|
||||
require(it.size == value.valueArguments.size) { "Bad statically initialized object: too many fields" }
|
||||
require(it.size == value.valueArguments.size + fields.count { it.isConst }) {
|
||||
"Bad statically initialized object of class ${constructedClass.name}: too many fields"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-37
@@ -52,9 +52,6 @@ internal class LlvmDeclarations(private val unique: Map<UniqueKind, UniqueLlvmDe
|
||||
fun forStaticField(field: IrField) = (field.metadata as? CodegenStaticFieldMetadata)?.llvm ?:
|
||||
error(field.descriptor.toString())
|
||||
|
||||
fun forSingleton(irClass: IrClass) = forClass(irClass).singletonDeclarations ?:
|
||||
error(irClass.descriptor.toString())
|
||||
|
||||
fun forUnique(kind: UniqueKind) = unique[kind] ?: error("No unique $kind")
|
||||
|
||||
}
|
||||
@@ -64,11 +61,8 @@ internal class ClassLlvmDeclarations(
|
||||
val typeInfoGlobal: StaticData.Global,
|
||||
val writableTypeInfoGlobal: StaticData.Global?,
|
||||
val typeInfo: ConstPointer,
|
||||
val singletonDeclarations: SingletonLlvmDeclarations?,
|
||||
val objCDeclarations: KotlinObjCClassLlvmDeclarations?)
|
||||
|
||||
internal class SingletonLlvmDeclarations(val instanceStorage: AddressAccess)
|
||||
|
||||
internal class KotlinObjCClassLlvmDeclarations(
|
||||
val classInfoGlobal: StaticData.Global,
|
||||
val bodyOffsetGlobal: StaticData.Global
|
||||
@@ -219,12 +213,6 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
if (declaration.isUnit() || declaration.isKotlinArray())
|
||||
createUniqueDeclarations(declaration, typeInfoPtr, bodyType)
|
||||
|
||||
val singletonDeclarations = if (declaration.kind.isSingleton) {
|
||||
createSingletonDeclarations(declaration)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val objCDeclarations = if (declaration.isKotlinObjCClass()) {
|
||||
createKotlinObjCClassDeclarations(declaration)
|
||||
} else {
|
||||
@@ -245,8 +233,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
it.setZeroInitializer()
|
||||
}
|
||||
|
||||
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
|
||||
singletonDeclarations, objCDeclarations)
|
||||
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr, objCDeclarations)
|
||||
}
|
||||
|
||||
private fun createUniqueDeclarations(
|
||||
@@ -264,28 +251,6 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSingletonDeclarations(irClass: IrClass): SingletonLlvmDeclarations? {
|
||||
if (irClass.isUnit()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val storageKind = irClass.storageKind(context)
|
||||
val threadLocal = storageKind == ObjectStorageKind.THREAD_LOCAL
|
||||
val isExported = irClass.isExported()
|
||||
val symbolName = if (isExported) {
|
||||
irClass.globalObjectStorageSymbolName
|
||||
} else {
|
||||
"kobjref:" + qualifyInternalName(irClass)
|
||||
}
|
||||
val instanceAddress = if (threadLocal) {
|
||||
addKotlinThreadLocal(symbolName, irClass.defaultType.toLLVMType(llvm))
|
||||
} else {
|
||||
addKotlinGlobal(symbolName, irClass.defaultType.toLLVMType(llvm), isExported)
|
||||
}
|
||||
|
||||
return SingletonLlvmDeclarations(instanceAddress)
|
||||
}
|
||||
|
||||
private fun createKotlinObjCClassDeclarations(irClass: IrClass): KotlinObjCClassLlvmDeclarations {
|
||||
val internalName = qualifyInternalName(irClass)
|
||||
|
||||
@@ -312,7 +277,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
super.visitField(declaration)
|
||||
|
||||
val containingClass = declaration.parent as? IrClass
|
||||
if (containingClass != null) {
|
||||
if (containingClass != null && !declaration.isStatic) {
|
||||
if (!containingClass.requiresRtti()) return
|
||||
val classDeclarations = (containingClass.metadata as? CodegenClassMetadata)?.llvm
|
||||
?: error(containingClass.descriptor.toString())
|
||||
|
||||
+4
-10
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isAny
|
||||
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering.Companion.isLoweredFunctionReference
|
||||
import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -463,17 +464,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
|
||||
val associatedObjectTableRecords = associatedObjects.map { (key, value) ->
|
||||
val associatedObjectGetter = generateFunction(
|
||||
CodeGenerator(context),
|
||||
functionType(kObjHeaderPtr, false, kObjHeaderPtrPtr),
|
||||
""
|
||||
) {
|
||||
ret(getObjectValue(value, ExceptionHandler.Caller, startLocationInfo = null))
|
||||
}.also {
|
||||
LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage)
|
||||
}
|
||||
val function = context.getObjectClassInstanceFunction(value)
|
||||
val llvmFunction = context.generationState.llvmDeclarations.forFunction(function).llvmValue
|
||||
|
||||
Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(associatedObjectGetter))
|
||||
Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(llvmFunction))
|
||||
}
|
||||
|
||||
return staticData.placeGlobalConstArray(
|
||||
|
||||
+18
-8
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
@@ -995,7 +996,8 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
||||
target: IrFunction?,
|
||||
baseMethod: IrFunction,
|
||||
methodBridge: MethodBridge,
|
||||
isVirtual: Boolean = false
|
||||
isVirtual: Boolean = false,
|
||||
customBridgeSuffix: String? = null,
|
||||
) = if (target == null) {
|
||||
generateAbstractObjCImp(methodBridge, baseMethod)
|
||||
} else {
|
||||
@@ -1003,7 +1005,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
||||
methodBridge,
|
||||
isDirect = !isVirtual,
|
||||
baseMethod = baseMethod,
|
||||
bridgeSuffix = (if (isVirtual) "virtual_" else "") + target.computeSymbolName()
|
||||
bridgeSuffix = customBridgeSuffix ?: ((if (isVirtual) "virtual_" else "") + target.computeSymbolName())
|
||||
) { args, resultLifetime, exceptionHandler ->
|
||||
if (target is IrConstructor && target.constructedClass.isAbstract()) {
|
||||
callFromBridge(
|
||||
@@ -1913,12 +1915,20 @@ private fun ObjCExportCodeGenerator.createObjectInstanceAdapter(
|
||||
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
|
||||
assert(objectClass.kind == ClassKind.OBJECT)
|
||||
assert(!objectClass.isUnit())
|
||||
val bridgeSuffix = "${owner.computeTypeInfoSymbolName()}#$selector"
|
||||
return generateObjCToKotlinSyntheticGetter(selector, bridgeSuffix) {
|
||||
initRuntimeIfNeeded() // For instance methods it gets called when allocating.
|
||||
val value = getObjectValue(objectClass, startLocationInfo = null, exceptionHandler = ExceptionHandler.Caller)
|
||||
autoreleaseAndRet(kotlinReferenceToRetainedObjC(value))
|
||||
}
|
||||
|
||||
val methodBridge = MethodBridge(
|
||||
returnBridge = MethodBridge.ReturnValue.Mapped(ReferenceBridge),
|
||||
receiver = MethodBridgeReceiver.Static,
|
||||
valueParameters = emptyList()
|
||||
)
|
||||
|
||||
val function = context.getObjectClassInstanceFunction(objectClass)
|
||||
val imp = generateObjCImp(
|
||||
function, function, methodBridge,
|
||||
isVirtual = false,
|
||||
customBridgeSuffix = "${owner.computeTypeInfoSymbolName()}#$selector")
|
||||
|
||||
return objCToKotlinMethodAdapter(selector, methodBridge, imp)
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.createEnumEntryAdapter(
|
||||
|
||||
+3
-3
@@ -239,7 +239,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
||||
|
||||
val field = expression.symbol.owner
|
||||
val parentClass = field.parentClassOrNull
|
||||
return if (parentClass == null || !parentClass.isInlined())
|
||||
return if (parentClass == null || !parentClass.isInlined() || field.isStatic)
|
||||
expression
|
||||
else {
|
||||
builder.at(expression)
|
||||
@@ -254,7 +254,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||
super.visitSetField(expression)
|
||||
|
||||
return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true) {
|
||||
return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true && !expression.symbol.owner.isStatic) {
|
||||
// Happens in one of the cases:
|
||||
// 1. In primary constructor of the inlined class. Makes no sense, "has no effect", can be removed.
|
||||
// The constructor will be lowered and used.
|
||||
@@ -442,7 +442,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
||||
lateinit var thisVar: IrValueDeclaration
|
||||
|
||||
fun IrBuilderWithScope.genReturnValue(): IrExpression = if (irConstructor.isPrimary) {
|
||||
irGetObject(irBuiltIns.unitClass)
|
||||
irCall(symbols.theUnitInstance)
|
||||
} else {
|
||||
irGet(thisVar)
|
||||
}
|
||||
|
||||
+1
-80
@@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.NativeMapping
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
|
||||
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
@@ -22,8 +20,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetField
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -39,26 +35,11 @@ internal object INTERNAL_ABI_ORIGIN : IrDeclarationOriginImpl("INTERNAL_ABI")
|
||||
* In case of compiler caches, this means that it is not accessible as Lazy IR
|
||||
* and we have to explicitly add an external declaration.
|
||||
*/
|
||||
internal class CachesAbiSupport(mapping: NativeMapping, symbols: KonanSymbols, private val irFactory: IrFactory) {
|
||||
private val companionObjectAccessors = mapping.companionObjectCacheAccessors
|
||||
internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: IrFactory) {
|
||||
private val outerThisAccessors = mapping.outerThisCacheAccessors
|
||||
private val lateinitPropertyAccessors = mapping.lateinitPropertyCacheAccessors
|
||||
private val enumValuesAccessors = mapping.enumValuesCacheAccessors
|
||||
private val lateInitFieldToNullableField = mapping.lateInitFieldToNullableField
|
||||
private val array = symbols.array
|
||||
|
||||
fun getCompanionObjectAccessor(irClass: IrClass): IrSimpleFunction {
|
||||
require(irClass.isCompanion) { "Expected a companion object but was: ${irClass.render()}" }
|
||||
return companionObjectAccessors.getOrPut(irClass) {
|
||||
irFactory.buildFun {
|
||||
name = getMangledNameFor("globalAccessor", irClass)
|
||||
origin = INTERNAL_ABI_ORIGIN
|
||||
returnType = irClass.defaultType
|
||||
}.apply {
|
||||
parent = irClass.getPackageFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getOuterThisAccessor(irClass: IrClass): IrSimpleFunction {
|
||||
require(irClass.isInner) { "Expected an inner class but was: ${irClass.render()}" }
|
||||
@@ -103,19 +84,6 @@ internal class CachesAbiSupport(mapping: NativeMapping, symbols: KonanSymbols, p
|
||||
}
|
||||
}
|
||||
|
||||
fun getEnumValuesAccessor(irClass: IrClass): IrSimpleFunction {
|
||||
require(irClass.isEnumClass) { "Expected a enum class but was: ${irClass.render()}" }
|
||||
return enumValuesAccessors.getOrPut(irClass) {
|
||||
irFactory.buildFun {
|
||||
name = getMangledNameFor("getValues", irClass)
|
||||
returnType = array.typeWith(irClass.defaultType)
|
||||
origin = INTERNAL_ABI_ORIGIN
|
||||
}.apply {
|
||||
parent = irClass.getPackageFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate name for declaration that will be a part of internal ABI.
|
||||
*/
|
||||
@@ -143,15 +111,6 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass,
|
||||
|
||||
if (declaration.isLocal) return
|
||||
|
||||
if (declaration.isCompanion) {
|
||||
val function = cachesAbiSupport.getCompanionObjectAccessor(declaration)
|
||||
context.createIrBuilder(function.symbol).apply {
|
||||
function.body = irBlockBody {
|
||||
+irReturn(irGetObjectValue(declaration.defaultType, declaration.symbol))
|
||||
}
|
||||
}
|
||||
data.add(function)
|
||||
}
|
||||
|
||||
if (declaration.isInner) {
|
||||
val function = cachesAbiSupport.getOuterThisAccessor(declaration)
|
||||
@@ -165,16 +124,6 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass,
|
||||
}
|
||||
data.add(function)
|
||||
}
|
||||
|
||||
if (declaration.isEnumClass) {
|
||||
val function = cachesAbiSupport.getEnumValuesAccessor(declaration)
|
||||
context.createIrBuilder(function.symbol).run {
|
||||
function.body = irBlockBody {
|
||||
+irReturn(with(this@ExportCachesAbiVisitor.context.enumsSupport) { irGetValuesField(declaration) })
|
||||
}
|
||||
}
|
||||
data.add(function)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty, data: MutableList<IrFunction>) {
|
||||
@@ -198,29 +147,12 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass,
|
||||
|
||||
internal class ImportCachesAbiTransformer(val context: Context) : FileLoweringPass, IrElementTransformerVoid() {
|
||||
private val cachesAbiSupport = context.cachesAbiSupport
|
||||
private val enumsSupport = context.enumsSupport
|
||||
private val llvmImports = context.generationState.llvmImports
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val irClass = expression.symbol.owner
|
||||
if (!irClass.isCompanion || context.llvmModuleSpecification.containsDeclaration(irClass)) {
|
||||
return expression
|
||||
}
|
||||
val parent = irClass.parentAsClass
|
||||
if (parent.isObjCClass()) {
|
||||
// Access to Obj-C metaclass is done via intrinsic.
|
||||
return expression
|
||||
}
|
||||
val accessor = cachesAbiSupport.getCompanionObjectAccessor(irClass)
|
||||
llvmImports.add(irClass.llvmSymbolOrigin)
|
||||
return irCall(expression.startOffset, expression.endOffset, accessor, emptyList())
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
@@ -249,17 +181,6 @@ internal class ImportCachesAbiTransformer(val context: Context) : FileLoweringPa
|
||||
}
|
||||
}
|
||||
|
||||
field.origin == DECLARATION_ORIGIN_ENUM -> {
|
||||
val enumClass = irClass?.parentClassOrNull
|
||||
require(enumClass != null) { "Unexpected usage of enum VALUES field" }
|
||||
require(enumClass.isEnumClass) { "Expected a enum class: ${enumClass.render()}" }
|
||||
require(enumsSupport.getImplObject(enumClass) == irClass) { "Expected a enum's impl object: ${irClass.render()}" }
|
||||
require(field == enumsSupport.getValuesField(irClass)) { "Expected VALUES field: ${field.render()}" }
|
||||
val accessor = cachesAbiSupport.getEnumValuesAccessor(enumClass)
|
||||
llvmImports.add(enumClass.llvmSymbolOrigin)
|
||||
return irCall(expression.startOffset, expression.endOffset, accessor, emptyList())
|
||||
}
|
||||
|
||||
else -> expression
|
||||
}
|
||||
}
|
||||
|
||||
+115
-160
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.getOrPut
|
||||
import org.jetbrains.kotlin.backend.common.lower.EnumWhenLowering
|
||||
@@ -12,11 +13,10 @@ import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.MemoryModel
|
||||
import org.jetbrains.kotlin.backend.konan.NativeMapping
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.ir.KonanNameConventions
|
||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -44,17 +43,29 @@ internal data class LoweredEnumEntryDescription(val ordinal: Int, val getterId:
|
||||
|
||||
internal class EnumsSupport(
|
||||
mapping: NativeMapping,
|
||||
symbols: KonanSymbols,
|
||||
private val irBuiltIns: IrBuiltIns,
|
||||
private val irFactory: IrFactory,
|
||||
) {
|
||||
private val enumImplObjects = mapping.enumImplObjects
|
||||
private val enumValueGetters = mapping.enumValueGetters
|
||||
private val enumEntriesMaps = mapping.enumEntriesMaps
|
||||
private val array = symbols.array
|
||||
private val enumEntries = symbols.enumEntriesInterface
|
||||
private val genericValueOfSymbol = symbols.valueOfForEnum
|
||||
private val genericValuesSymbol = symbols.valuesForEnum
|
||||
|
||||
private val valuesFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||
private val valueOfFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||
|
||||
fun getValuesFunction(enumClass: IrClass) = valuesFunctions.getOrPut(enumClass) {
|
||||
enumClass.simpleFunctions().single {
|
||||
it.name == valuesFunctionName && it.dispatchReceiverParameter == null
|
||||
}
|
||||
}
|
||||
fun saveValuesFunction(enumClass: IrClass) { getValuesFunction(enumClass) }
|
||||
|
||||
fun getValueOfFunction(enumClass: IrClass) = valueOfFunctions.getOrPut(enumClass) {
|
||||
enumClass.simpleFunctions().single {
|
||||
it.name == valueOfFunctionName && it.dispatchReceiverParameter == null
|
||||
}
|
||||
}
|
||||
fun saveValueOfFunction(enumClass: IrClass) { getValueOfFunction(enumClass) }
|
||||
|
||||
|
||||
fun enumEntriesMap(enumClass: IrClass): Map<Name, LoweredEnumEntryDescription> {
|
||||
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
|
||||
@@ -70,41 +81,6 @@ internal class EnumsSupport(
|
||||
}
|
||||
}
|
||||
|
||||
fun getImplObject(enumClass: IrClass): IrClass {
|
||||
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
|
||||
return enumImplObjects.getOrPut(enumClass) {
|
||||
irFactory.buildClass {
|
||||
startOffset = enumClass.startOffset
|
||||
endOffset = enumClass.endOffset
|
||||
origin = DECLARATION_ORIGIN_ENUM
|
||||
name = implObjectName
|
||||
kind = ClassKind.OBJECT
|
||||
}.apply {
|
||||
superTypes = listOf(irBuiltIns.anyType)
|
||||
parent = enumClass
|
||||
|
||||
addChild(irFactory.buildField {
|
||||
startOffset = enumClass.startOffset
|
||||
endOffset = enumClass.endOffset
|
||||
origin = DECLARATION_ORIGIN_ENUM
|
||||
name = valuesFieldName
|
||||
type = array.typeWith(enumClass.defaultType)
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
isFinal = true
|
||||
})
|
||||
addChild(irFactory.buildField {
|
||||
startOffset = enumClass.startOffset
|
||||
endOffset = enumClass.endOffset
|
||||
origin = DECLARATION_ORIGIN_ENUM
|
||||
name = entriesFieldName
|
||||
type = enumEntries.typeWith(enumClass.defaultType)
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
isFinal = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getValueGetter(enumClass: IrClass): IrFunction {
|
||||
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
|
||||
return enumValueGetters.getOrPut(enumClass) {
|
||||
@@ -126,36 +102,9 @@ internal class EnumsSupport(
|
||||
}
|
||||
}
|
||||
|
||||
fun getValuesField(implObject: IrClass) = implObject.fields.single { it.name == valuesFieldName }
|
||||
fun getEntriesField(implObject: IrClass) = implObject.fields.single { it.name == entriesFieldName }
|
||||
|
||||
fun IrBuilderWithScope.irGetValuesField(enumClass: IrClass): IrExpression {
|
||||
val implObject = getImplObject(enumClass)
|
||||
val valuesField = getValuesField(implObject)
|
||||
return irGetField(irGetObject(implObject.symbol), valuesField)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irEnumValues(enumClass: IrClass) : IrExpression =
|
||||
irCall(genericValuesSymbol, listOf(enumClass.defaultType)).apply {
|
||||
putValueArgument(0, irGetValuesField(enumClass))
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irEnumValueOf(enumClass: IrClass, value: IrExpression) : IrExpression =
|
||||
irCall(genericValueOfSymbol, listOf(enumClass.defaultType)).apply {
|
||||
putValueArgument(0, value)
|
||||
putValueArgument(1, irGetValuesField(enumClass))
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irEnumEntries(enumClass: IrClass) : IrExpression {
|
||||
val implObject = getImplObject(enumClass)
|
||||
val entriesField = getEntriesField(implObject)
|
||||
return irGetField(irGetObject(implObject.symbol), entriesField)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val valuesFieldName = "VALUES".synthesizedName
|
||||
val entriesFieldName = "ENTRIES".synthesizedName
|
||||
val implObjectName = "OBJECT".synthesizedName
|
||||
val valuesFunctionName = Name.identifier("values")
|
||||
val valueOfFunctionName = Name.identifier("valueOf")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +119,6 @@ internal class NativeEnumWhenLowering constructor(context: Context) : EnumWhenLo
|
||||
|
||||
internal class EnumUsageLowering(val context: Context) : IrElementTransformer<IrBuilderWithScope?>, FileLoweringPass {
|
||||
private val enumsSupport = context.enumsSupport
|
||||
private val symbols = context.ir.symbols
|
||||
private val arrayGet = symbols.arrayGet[symbols.array]!!
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
visitFile(irFile, data = null)
|
||||
@@ -206,19 +153,21 @@ internal class EnumUsageLowering(val context: Context) : IrElementTransformer<Ir
|
||||
|
||||
require(irClass.kind == ClassKind.ENUM_CLASS)
|
||||
|
||||
return with(enumsSupport) {
|
||||
if (intrinsicType == IntrinsicType.ENUM_VALUES)
|
||||
data.irEnumValues(irClass)
|
||||
else
|
||||
data.irEnumValueOf(irClass, expression.getValueArgument(0)!!)
|
||||
return when (intrinsicType) {
|
||||
IntrinsicType.ENUM_VALUES -> data.irCall(enumsSupport.getValuesFunction(irClass))
|
||||
IntrinsicType.ENUM_VALUE_OF -> data.irCall(enumsSupport.getValueOfFunction(irClass)).apply {
|
||||
putValueArgument(0, expression.getValueArgument(0)!!)
|
||||
}
|
||||
else -> TODO("Unsupported intrinsic type ${intrinsicType}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name) = with (enumsSupport) {
|
||||
irCall(getValueGetter(enumClass).symbol, enumClass.defaultType).apply {
|
||||
putValueArgument(0, irInt(enumEntriesMap(enumClass).getValue(name).getterId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name) =
|
||||
irCall(arrayGet, enumClass.defaultType).apply {
|
||||
dispatchReceiver = with(enumsSupport) { irGetValuesField(enumClass) }
|
||||
putValueArgument(0, irInt(enumsSupport.enumEntriesMap(enumClass).getValue(name).getterId))
|
||||
}
|
||||
}
|
||||
|
||||
internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
@@ -228,7 +177,6 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
private val createEnumEntries = symbols.createEnumEntries
|
||||
private val initInstance = symbols.initInstance
|
||||
private val arrayGet = symbols.array.owner.functions.single { it.name == KonanNameConventions.getWithoutBoundCheck }.symbol
|
||||
private val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
@@ -242,14 +190,37 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
}
|
||||
|
||||
private inner class EnumClassTransformer(val irClass: IrClass) {
|
||||
private val implObject = enumsSupport.getImplObject(irClass)
|
||||
private val valuesField = enumsSupport.getValuesField(implObject)
|
||||
private val entriesField = enumsSupport.getEntriesField(implObject)
|
||||
private val valuesField = context.irFactory.buildField {
|
||||
startOffset = irClass.startOffset
|
||||
endOffset = irClass.endOffset
|
||||
origin = DECLARATION_ORIGIN_ENUM
|
||||
name = "VALUES".synthesizedName
|
||||
type = context.irBuiltIns.arrayClass.typeWith(irClass.defaultType)
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
isFinal = true
|
||||
isStatic = true
|
||||
}
|
||||
private val entriesField = context.irFactory.buildField {
|
||||
startOffset = irClass.startOffset
|
||||
endOffset = irClass.endOffset
|
||||
origin = DECLARATION_ORIGIN_ENUM
|
||||
name = "ENTRIES".synthesizedName
|
||||
type = symbols.enumEntriesInterface.typeWith(irClass.defaultType)
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
isFinal = true
|
||||
isStatic = true
|
||||
}
|
||||
|
||||
// also saves this in enumSupport before removing them from list
|
||||
private val enumEntriesMap = enumsSupport.enumEntriesMap(irClass)
|
||||
|
||||
|
||||
fun run() {
|
||||
val enumEntries = transformEnumBody()
|
||||
defineImplObject(enumEntries)
|
||||
// These fields are inserted into begining to be initialized before companion object
|
||||
// The values field should be initialized first, so we need to insert entries field first
|
||||
defineEntriesField()
|
||||
defineValuesField(enumEntries)
|
||||
defineValueGetter()
|
||||
}
|
||||
|
||||
@@ -263,23 +234,31 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
declaration.correspondingClass = null
|
||||
listOfNotNull(correspondingClass)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
enumsSupport.saveValuesFunction(irClass)
|
||||
enumsSupport.saveValueOfFunction(irClass)
|
||||
irClass.simpleFunctions().forEach { declaration ->
|
||||
val body = declaration.body
|
||||
if (body is IrSyntheticBody) {
|
||||
declaration.body = when (body.kind) {
|
||||
IrSyntheticBodyKind.ENUM_VALUEOF -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) {
|
||||
+irReturn(with(enumsSupport) { irEnumValueOf(irClass, irGet(declaration.valueParameters[0])) })
|
||||
+irReturn(irCall(symbols.valueOfForEnum, listOf(irClass.defaultType)).apply {
|
||||
putValueArgument(0, irGet(declaration.valueParameters[0]))
|
||||
putValueArgument(1, irGetField(null, valuesField))
|
||||
})
|
||||
}
|
||||
|
||||
IrSyntheticBodyKind.ENUM_VALUES -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) {
|
||||
+irReturn(with(enumsSupport) { irEnumValues(irClass) })
|
||||
+irReturn(irCall(symbols.valuesForEnum, listOf(irClass.defaultType)).apply {
|
||||
putValueArgument(0, irGetField(null, valuesField))
|
||||
})
|
||||
}
|
||||
|
||||
IrSyntheticBodyKind.ENUM_ENTRIES -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) {
|
||||
+irReturn(with(enumsSupport) { irEnumEntries(irClass) })
|
||||
+irReturn(irGetField(null, entriesField))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,7 +271,7 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
context.createIrBuilder(valueGetter.symbol).run {
|
||||
valueGetter.body = irBlockBody(valueGetter) {
|
||||
+irReturn(irCall(arrayGet, irClass.defaultType).apply {
|
||||
dispatchReceiver = with(enumsSupport) { irGetValuesField(irClass) }
|
||||
dispatchReceiver = irGetField(null, valuesField)
|
||||
putValueArgument(0, irGet(valueGetter.valueParameters[0]))
|
||||
})
|
||||
}
|
||||
@@ -300,49 +279,18 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
irClass.declarations.add(valueGetter)
|
||||
}
|
||||
|
||||
private fun defineImplObject(enumEntries: List<IrEnumEntry>) {
|
||||
implObject.createParameterDeclarations()
|
||||
|
||||
implObject.addSimpleDelegatingConstructor(constructorOfAny, context.irBuiltIns, true /* TODO: why primary? */)
|
||||
implObject.addFakeOverrides(context.typeSystem)
|
||||
irClass.declarations.add(implObject)
|
||||
|
||||
ImplConstructorBuilder(implObject.constructors.single()).build(enumEntries)
|
||||
}
|
||||
|
||||
private inner class ImplConstructorBuilder(val constructor: IrConstructor) {
|
||||
private val irBuilder = context.createIrBuilder(constructor.symbol, constructor.startOffset, constructor.endOffset)
|
||||
|
||||
fun build(enumEntries: List<IrEnumEntry>) {
|
||||
val statements = (constructor.body as IrBlockBody).statements
|
||||
|
||||
// The initialization order is the following:
|
||||
// - first all enum entries in their declaration order
|
||||
// - then companion object if it exists
|
||||
statements += buildValuesFieldInitializer(enumEntries)
|
||||
// Split allocation and constructors calling because enum entries can reference one another.
|
||||
statements += callEnumEntriesConstructors(enumEntries)
|
||||
statements += buildEntriesFieldInitializer()
|
||||
|
||||
// Needed for legacy MM targets that do not support threads.
|
||||
if (this@EnumClassLowering.context.memoryModel != MemoryModel.EXPERIMENTAL) {
|
||||
statements += irBuilder.irBlock {
|
||||
irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(valuesField.type)).apply {
|
||||
extensionReceiver = irGet(implObject.thisReceiver!!)
|
||||
}
|
||||
}
|
||||
private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall =
|
||||
irCall(initInstance).apply {
|
||||
putValueArgument(0, instance)
|
||||
putValueArgument(1, constructor)
|
||||
}
|
||||
|
||||
irClass.companionObject()?.let { statements += irBuilder.irGetObject(it.symbol) }
|
||||
}
|
||||
private fun defineValuesField(enumEntries: List<IrEnumEntry>) {
|
||||
irClass.declarations.add(0, valuesField)
|
||||
valuesField.parent = irClass
|
||||
val irBuilder = context.createIrBuilder(valuesField.symbol, irClass.startOffset, irClass.endOffset)
|
||||
|
||||
private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall =
|
||||
irCall(initInstance).apply {
|
||||
putValueArgument(0, instance)
|
||||
putValueArgument(1, constructor)
|
||||
}
|
||||
|
||||
private fun buildValuesFieldInitializer(enumEntries: List<IrEnumEntry>) = irBuilder.run {
|
||||
valuesField.initializer = irBuilder.irExprBody(irBuilder.irBlock {
|
||||
val irValuesInitializer = this@EnumClassLowering.context.createArrayOfExpression(
|
||||
startOffset, endOffset,
|
||||
irClass.defaultType,
|
||||
@@ -363,11 +311,22 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
irCall(createUninitializedInstance, listOf(entryClass.defaultType))
|
||||
}
|
||||
)
|
||||
irSetField(irGet(implObject.thisReceiver!!), valuesField, irValuesInitializer)
|
||||
val instances = irTemporary(irValuesInitializer)
|
||||
+irSetField(null, valuesField, irGet(instances), origin = ObjectClassLowering.IrStatementOriginFieldPreInit)
|
||||
callEnumEntriesConstructors(instances, enumEntries)
|
||||
+irGet(instances)
|
||||
}).also {
|
||||
it.setDeclarationsParent(valuesField)
|
||||
}
|
||||
valuesField.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner)
|
||||
}
|
||||
|
||||
private fun buildEntriesFieldInitializer() = irBuilder.irBlock {
|
||||
val irValuesArray = irTemporary(irGetField(irGet(implObject.thisReceiver!!), valuesField))
|
||||
fun defineEntriesField() {
|
||||
irClass.declarations.add(0, entriesField)
|
||||
entriesField.parent = irClass
|
||||
val irBuilder = context.createIrBuilder(entriesField.symbol, irClass.startOffset, irClass.endOffset)
|
||||
entriesField.initializer = irBuilder.irExprBody(irBuilder.irBlock {
|
||||
val irValuesArray = irTemporary(irGetField(null, valuesField))
|
||||
val irEntriesArray = this@EnumClassLowering.context.createArrayOfExpression(
|
||||
startOffset, endOffset,
|
||||
irClass.defaultType,
|
||||
@@ -380,34 +339,30 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
||||
}
|
||||
}
|
||||
)
|
||||
val irEntriesInitializer = irCall(createEnumEntries, listOf(irClass.defaultType)).apply {
|
||||
+irCall(createEnumEntries, listOf(irClass.defaultType)).apply {
|
||||
putValueArgument(0, irEntriesArray)
|
||||
}
|
||||
+irSetField(irGet(implObject.thisReceiver!!), entriesField, irEntriesInitializer)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun callEnumEntriesConstructors(enumEntries: List<IrEnumEntry>) = irBuilder.irBlock {
|
||||
val receiver = implObject.thisReceiver!!
|
||||
val instances = irTemporary(irGetField(irGet(receiver), valuesField))
|
||||
enumEntries.forEach {
|
||||
val instance = irCall(arrayGet).apply {
|
||||
dispatchReceiver = irGet(instances)
|
||||
putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId))
|
||||
private fun IrBlockBuilder.callEnumEntriesConstructors(instances: IrVariable, enumEntries: List<IrEnumEntry>) {
|
||||
enumEntries.forEach {
|
||||
val instance = irCall(arrayGet).apply {
|
||||
dispatchReceiver = irGet(instances)
|
||||
putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId))
|
||||
}
|
||||
val initializer = it.initializerExpression!!.expression
|
||||
when {
|
||||
initializer is IrConstructorCall -> +irInitInstanceCall(instance, initializer)
|
||||
|
||||
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> {
|
||||
val statements = initializer.statements
|
||||
val constructorCall = statements.last() as IrConstructorCall
|
||||
statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall)
|
||||
+initializer
|
||||
}
|
||||
val initializer = it.initializerExpression!!.expression
|
||||
initializer.setDeclarationsParent(constructor)
|
||||
when {
|
||||
initializer is IrConstructorCall -> +irInitInstanceCall(instance, initializer)
|
||||
|
||||
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> {
|
||||
val statements = initializer.statements
|
||||
val constructorCall = statements.last() as IrConstructorCall
|
||||
statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall)
|
||||
+initializer
|
||||
}
|
||||
|
||||
else -> error("Unexpected initializer: $initializer")
|
||||
}
|
||||
else -> error("Unexpected initializer: $initializer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.getOrPut
|
||||
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isAny
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isUnit
|
||||
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addField
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.util.irCall
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal fun Context.getObjectClassInstanceFunction(clazz: IrClass) = mapping.objectInstanceGetter.getOrPut(clazz) {
|
||||
when {
|
||||
clazz.isUnit() -> ir.symbols.theUnitInstance.owner
|
||||
clazz.isCompanion -> {
|
||||
require((clazz.parent as? IrClass)?.isExternalObjCClass() != true) { "External objc Classes can't be used this way"}
|
||||
irFactory.buildFun {
|
||||
name = "companion".synthesizedName
|
||||
returnType = clazz.defaultType
|
||||
}.apply {
|
||||
parent = clazz.parent
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
irFactory.buildFun {
|
||||
name = "instance".synthesizedName
|
||||
returnType = clazz.defaultType
|
||||
}.apply {
|
||||
parent = clazz
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
|
||||
val symbols = context.ir.symbols
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transform(object: IrBuildingTransformer(context) {
|
||||
override fun visitClass(declaration: IrClass) : IrDeclaration {
|
||||
super.visitClass(declaration)
|
||||
if (declaration.isObject && !declaration.isCompanion && !declaration.isUnit()) {
|
||||
processObjectClass(
|
||||
declaration,
|
||||
declaration,
|
||||
instanceFieldName
|
||||
)
|
||||
}
|
||||
declaration.declarations.singleOrNull { (it as? IrClass)?.isCompanion == true }?.let {
|
||||
processObjectClass(
|
||||
it as IrClass,
|
||||
declaration,
|
||||
companionFieldName
|
||||
)
|
||||
}
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue) : IrExpression {
|
||||
builder.at(expression)
|
||||
val singleton = expression.symbol.owner
|
||||
return if (singleton.isCompanion && (singleton.parent as IrClass).isExternalObjCClass())
|
||||
builder.irGetObjCClassCompanion(singleton)
|
||||
else
|
||||
builder.irCall(context.getObjectClassInstanceFunction(singleton))
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irGetObjCClassCompanion(declaration: IrClass): IrExpression {
|
||||
require(declaration.isCompanion && (declaration.parent as IrClass).isObjCClass())
|
||||
return irCall(symbols.interopInterpretObjCPointer, listOf(declaration.defaultType)).apply {
|
||||
putValueArgument(0, irCall(symbols.interopGetObjCClass, listOf((declaration.parent as IrClass).defaultType)))
|
||||
}
|
||||
}
|
||||
|
||||
fun processObjectClass(
|
||||
declaration: IrClass,
|
||||
classToAdd: IrClass,
|
||||
fieldName: Name
|
||||
) {
|
||||
val function = context.getObjectClassInstanceFunction(declaration)
|
||||
classToAdd.declarations.add(function)
|
||||
|
||||
val primaryConstructor = declaration.constructors.single { it.isPrimary }
|
||||
require(primaryConstructor.valueParameters.isEmpty())
|
||||
val instanceField = classToAdd.addField {
|
||||
name = fieldName
|
||||
isFinal = true
|
||||
isStatic = true
|
||||
type = declaration.defaultType
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
}.also { field ->
|
||||
val builder = context.createIrBuilder(field.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
|
||||
val initializer = if (declaration.isCompanion && classToAdd.isObjCClass()) {
|
||||
builder.irGetObjCClassCompanion(declaration)
|
||||
} else {
|
||||
if (declaration.hasConstStateAndNoSideEffects() && !declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) {
|
||||
builder.irConstantObject(
|
||||
declaration,
|
||||
emptyList()
|
||||
)
|
||||
} else {
|
||||
builder.irBlock {
|
||||
// we need to make object available for rereading from the same thread while initializing
|
||||
+irSetField(null, field, irCall(symbols.createUninitializedInstance, listOf(declaration.defaultType)), origin = IrStatementOriginFieldPreInit)
|
||||
+irCall(symbols.initInstance).apply {
|
||||
putValueArgument(0, irGetField(null, field))
|
||||
putValueArgument(1, irCallConstructor(primaryConstructor.symbol, emptyList()))
|
||||
}
|
||||
+irGetField(null, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
field.initializer = builder.irExprBody(initializer)
|
||||
if (declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) {
|
||||
field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.threadLocal.owner)
|
||||
} else if (declaration.annotations.hasAnnotation(KonanFqNames.sharedImmutable) || context.memoryModel != MemoryModel.EXPERIMENTAL){
|
||||
field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner)
|
||||
}
|
||||
}
|
||||
function.body = context.createIrBuilder(function.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
val value = irGetField(null, instanceField)
|
||||
+irReturn(value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrConstructor.isAutogeneratedSimpleConstructor(): Boolean {
|
||||
if (!this.isPrimary) return false
|
||||
if (valueParameters.isNotEmpty()) return false
|
||||
val statements = this.body?.statements ?: return false
|
||||
if (statements.isEmpty()) 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().drop(1).all {
|
||||
it is IrBlock && it.origin == IrStatementOrigin.INITIALIZE_FIELD
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.hasConstStateAndNoSideEffects(): Boolean {
|
||||
if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true
|
||||
val fields = context.getLayoutBuilder(this).fields
|
||||
return fields.all { it.isConst } && this.constructors.all { it.isAutogeneratedSimpleConstructor() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
val companionFieldName = "companionField".synthesizedName
|
||||
val instanceFieldName = "instanceField".synthesizedName
|
||||
|
||||
}
|
||||
|
||||
// This is a hack to avoid early freezing. Should be removed when freezing is removed
|
||||
object IrStatementOriginFieldPreInit : IrStatementOriginImpl("FIELD_PRE_INIT")
|
||||
|
||||
}
|
||||
+3
-5
@@ -9,13 +9,11 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.irGetObject
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.types.isNullableNothing
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
@@ -38,7 +36,7 @@ internal class ReturnsInsertionLowering(val context: Context) : FileLoweringPass
|
||||
body as IrBlockBody
|
||||
context.createIrBuilder(declaration.symbol, declaration.endOffset, declaration.endOffset).run {
|
||||
if (declaration is IrConstructor || declaration.returnType == context.irBuiltIns.unitType) {
|
||||
body.statements += irReturn(irGetObject(symbols.unit))
|
||||
body.statements += irReturn(irCall(symbols.theUnitInstance, context.irBuiltIns.unitType))
|
||||
} else if (declaration.returnType.isNullable()) {
|
||||
// this is a workaround for KT-42832
|
||||
val typeOperatorCall = body.statements.lastOrNull() as? IrTypeOperatorCall
|
||||
@@ -56,7 +54,7 @@ internal class ReturnsInsertionLowering(val context: Context) : FileLoweringPass
|
||||
if (expression.inlineFunctionSymbol?.owner?.returnType == context.irBuiltIns.unitType) {
|
||||
val offset = (expression.statements.lastOrNull() ?: expression).endOffset
|
||||
context.createIrBuilder(expression.symbol, offset, offset).run {
|
||||
expression.statements += irReturn(irGetObject(symbols.unit))
|
||||
expression.statements += irReturn(irCall(symbols.theUnitInstance, context.irBuiltIns.unitType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.konan.ConfigChecks
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENTRY_POINT
|
||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.FieldStorageKind
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.needsGCRegistration
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.storageKind
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall
|
||||
import org.jetbrains.kotlin.backend.konan.util.IntArrayList
|
||||
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
|
||||
import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
@@ -106,7 +107,7 @@ internal object DevirtualizationAnalysis {
|
||||
|
||||
context.getLayoutBuilder(declaration).associatedObjects.values.forEach {
|
||||
assert(it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" }
|
||||
associatedObjectConstructors += moduleDFG.symbolTable.mapFunction(it.constructors.single())
|
||||
associatedObjectConstructors += moduleDFG.symbolTable.mapFunction(context.getObjectClassInstanceFunction(it))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-11
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.optimizations
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.atMostOne
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
@@ -21,7 +20,6 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.builders.irBlock
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -427,15 +425,7 @@ internal object StaticInitializersOptimization {
|
||||
}
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet {
|
||||
val objectClass = expression.symbol.owner
|
||||
val constructor = objectClass.constructors.toList().atMostOne()
|
||||
if (constructor != null) {
|
||||
updateResultForFunction(constructor, data)
|
||||
} else {
|
||||
require(objectClass.isExternal || objectClass is IrLazyClass) { "No constructor for ${objectClass.render()}" }
|
||||
}
|
||||
// Don't update [data] as the constructor might not be called here (the object might be initialized already).
|
||||
return data
|
||||
error("IrGetObjectValue should be lowered away at this point")
|
||||
}
|
||||
|
||||
private fun processCall(expression: IrFunctionAccessExpression, actualCallee: IrFunction, data: BitSet): BitSet {
|
||||
|
||||
+2
-2
@@ -24,12 +24,12 @@ func testKotlin2Objc() throws {
|
||||
|
||||
func testCompanionObject() throws {
|
||||
let trace = Foo.companion.trace
|
||||
try assertTrue(trace[6].contains("objc2kotlin_kclass:Foo#companion"))
|
||||
try assertTrue(trace[8].contains("objc2kotlin_kclass:Foo#companion"))
|
||||
}
|
||||
|
||||
func testStandaloneObject() throws {
|
||||
let trace = Object.shared.trace
|
||||
try assertTrue(trace[6].contains("objc2kotlin_kclass:Object#shared"))
|
||||
try assertTrue(trace[8].contains("objc2kotlin_kclass:Object#shared"))
|
||||
}
|
||||
|
||||
func testEnumEntry() throws {
|
||||
|
||||
@@ -31,8 +31,6 @@ touchType(KRefSharedHolder)
|
||||
|
||||
touchFunction(AllocInstance)
|
||||
touchFunction(AllocArrayInstance)
|
||||
touchFunction(InitThreadLocalSingleton)
|
||||
touchFunction(InitSingleton)
|
||||
touchFunction(InitAndRegisterGlobal)
|
||||
touchFunction(UpdateHeapRef)
|
||||
touchFunction(UpdateStackRef)
|
||||
|
||||
@@ -2373,104 +2373,6 @@ OBJ_GETTER(allocArrayInstance, const TypeInfo* type_info, int32_t elements) {
|
||||
RETURN_OBJ(container.GetPlace()->obj());
|
||||
}
|
||||
|
||||
template <bool Strict>
|
||||
OBJ_GETTER(initThreadLocalSingleton,
|
||||
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
ObjHeader* value = *location;
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = allocInstance<Strict>(typeInfo, OBJ_RESULT);
|
||||
updateHeapRef<Strict>(location, object);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
return object;
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateReturnRef(OBJ_RESULT, nullptr);
|
||||
ZeroHeapRef(location);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool Strict>
|
||||
OBJ_GETTER(initSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
#if KONAN_NO_THREADS
|
||||
ObjHeader* value = *location;
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT);
|
||||
UpdateHeapRef(location, object);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
FreezeSubgraph(object);
|
||||
return object;
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
if (Strict)
|
||||
FreezeSubgraph(object);
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateReturnRef(OBJ_RESULT, nullptr);
|
||||
ZeroHeapRef(location);
|
||||
throw;
|
||||
}
|
||||
#endif // KONAN_NO_EXCEPTIONS
|
||||
#else // KONAN_NO_THREADS
|
||||
// Search from the top of the stack.
|
||||
for (auto it = memoryState->initializingSingletons.rbegin(); it != memoryState->initializingSingletons.rend(); ++it) {
|
||||
if (it->first == location) {
|
||||
RETURN_OBJ(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
ObjHeader* initializing = kInitializingSingleton;
|
||||
|
||||
// Spin lock.
|
||||
ObjHeader* value = nullptr;
|
||||
while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing);
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT);
|
||||
memoryState->initializingSingletons.push_back(std::make_pair(location, object));
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
if (Strict)
|
||||
FreezeSubgraph(object);
|
||||
UpdateHeapRef(location, object);
|
||||
synchronize();
|
||||
memoryState->initializingSingletons.pop_back();
|
||||
return object;
|
||||
#else // KONAN_NO_EXCEPTIONS
|
||||
try {
|
||||
ctor(object);
|
||||
if (Strict)
|
||||
FreezeSubgraph(object);
|
||||
UpdateHeapRef(location, object);
|
||||
synchronize();
|
||||
memoryState->initializingSingletons.pop_back();
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateReturnRef(OBJ_RESULT, nullptr);
|
||||
zeroHeapRef(location);
|
||||
memoryState->initializingSingletons.pop_back();
|
||||
synchronize();
|
||||
throw;
|
||||
}
|
||||
#endif // KONAN_NO_EXCEPTIONS
|
||||
#endif // KONAN_NO_THREADS
|
||||
}
|
||||
|
||||
/**
|
||||
* We keep thread affinity and reference value based cookie in the atomic references, so that
|
||||
* repeating read operation of the same value do not lead to the repeating rememberNewContainer() operation.
|
||||
@@ -3398,20 +3300,6 @@ OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* typeInfo, int32_t elements
|
||||
RETURN_RESULT_OF(allocArrayInstance<false>, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initThreadLocalSingleton<true>, location, typeInfo, ctor);
|
||||
}
|
||||
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initThreadLocalSingleton<false>, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initSingleton<true>, location, typeInfo, ctor);
|
||||
}
|
||||
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(initSingleton<false>, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
void RUNTIME_NOTHROW InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
||||
RuntimeCheck(false, "Global registration is impossible in legacy MM");
|
||||
}
|
||||
|
||||
@@ -309,11 +309,6 @@ OBJ_GETTER(AllocInstanceRelaxed, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
||||
OBJ_GETTER(AllocArrayInstanceStrict, const TypeInfo* type_info, int32_t elements);
|
||||
OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* type_info, int32_t elements);
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object);
|
||||
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
||||
|
||||
@@ -25,8 +25,6 @@ void ensureUsed(Ret (*f)(Args...)) {
|
||||
void EnsureDeclarationsEmitted() {
|
||||
ensureUsed(AllocInstance);
|
||||
ensureUsed(AllocArrayInstance);
|
||||
ensureUsed(InitThreadLocalSingleton);
|
||||
ensureUsed(InitSingleton);
|
||||
ensureUsed(InitAndRegisterGlobal);
|
||||
ensureUsed(UpdateHeapRef);
|
||||
ensureUsed(UpdateStackRef);
|
||||
|
||||
@@ -83,6 +83,7 @@ void VLog(Level level, std::initializer_list<const char*> tags, const char* form
|
||||
|
||||
inline constexpr const char* kTagGC = "gc";
|
||||
inline constexpr const char* kTagMM = "mm";
|
||||
inline constexpr const char* kTagTLS = "tls";
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
|
||||
@@ -195,9 +195,6 @@ OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
||||
|
||||
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements);
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
// `initialValue` may be `nullptr`, which signifies that the appropriate initial value was already
|
||||
// set by static initialization.
|
||||
|
||||
@@ -53,6 +53,7 @@ class IntrinsicType {
|
||||
const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB"
|
||||
const val INIT_INSTANCE = "INIT_INSTANCE"
|
||||
const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM"
|
||||
const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE"
|
||||
|
||||
// Enums
|
||||
const val ENUM_VALUES = "ENUM_VALUES"
|
||||
|
||||
@@ -30,3 +30,6 @@ import kotlin.native.internal.IntrinsicType
|
||||
|
||||
// Reinterprets this value from T to R having the same binary representation (e.g. to unwrap inline class).
|
||||
@TypedIntrinsic(IntrinsicType.IDENTITY) @PublishedApi external internal fun <T, R> T.reinterpret(): R
|
||||
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.THE_UNIT_INSTANCE) @ExportForCompiler external internal fun theUnitInstance(): Unit
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "InitializationScheme.hpp"
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectOps.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
OBJ_GETTER(mm::InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||
if (auto* value = *location) {
|
||||
// Initialized by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
auto* value = mm::AllocateObject(threadData, typeInfo, OBJ_RESULT);
|
||||
mm::SetHeapRef(location, value);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(value);
|
||||
#else
|
||||
try {
|
||||
ctor(value);
|
||||
} catch (...) {
|
||||
mm::SetStackRef(OBJ_RESULT, nullptr);
|
||||
mm::SetHeapRef(location, nullptr);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
|
||||
OBJ_GETTER(mm::InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||
auto& initializingSingletons = threadData->initializingSingletons();
|
||||
|
||||
// Search from the top of the stack.
|
||||
for (auto it = initializingSingletons.rbegin(); it != initializingSingletons.rend(); ++it) {
|
||||
if (it->first == location) {
|
||||
RETURN_OBJ(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
ObjHeader* initializing = kInitializingSingleton;
|
||||
|
||||
// Spin lock.
|
||||
ObjHeader* value = nullptr;
|
||||
{
|
||||
ThreadStateGuard guard(ThreadState::kNative);
|
||||
while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing) {
|
||||
}
|
||||
}
|
||||
if (value != nullptr) {
|
||||
// Initialized by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
auto* object = mm::AllocateObject(threadData, typeInfo, OBJ_RESULT);
|
||||
initializingSingletons.push_back(std::make_pair(location, object));
|
||||
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
} catch (...) {
|
||||
mm::SetStackRef(OBJ_RESULT, nullptr);
|
||||
mm::SetHeapRefAtomic(location, nullptr);
|
||||
initializingSingletons.pop_back();
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
mm::GlobalsRegistry::Instance().RegisterStorageForGlobal(threadData, location);
|
||||
mm::SetHeapRefAtomic(location, object);
|
||||
initializingSingletons.pop_back();
|
||||
return object;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_MM_INITIALIZATION_SCHEME_H
|
||||
#define RUNTIME_MM_INITIALIZATION_SCHEME_H
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
namespace kotlin {
|
||||
namespace mm {
|
||||
|
||||
class ThreadData;
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
} // namespace mm
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_MM_INITIALIZATION_SCHEME_H
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "InitializationScheme.hpp"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ObjectTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "Types.h"
|
||||
#include "std_support/Vector.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
struct EmptyPayload {
|
||||
using Field = ObjHeader* EmptyPayload::*;
|
||||
static constexpr std::array<Field, 0> kFields{};
|
||||
};
|
||||
|
||||
class InitSingletonTest : public testing::Test {
|
||||
public:
|
||||
InitSingletonTest() {
|
||||
globalConstructor_ = &constructor_;
|
||||
}
|
||||
|
||||
~InitSingletonTest() {
|
||||
globalConstructor_ = nullptr;
|
||||
mm::GlobalData::Instance().gc().ClearForTests();
|
||||
mm::GlobalData::Instance().globalsRegistry().ClearForTests();
|
||||
}
|
||||
|
||||
testing::MockFunction<void(ObjHeader*)>& constructor() { return constructor_; }
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, mm::ThreadData& threadData) {
|
||||
RETURN_RESULT_OF(mm::InitThreadLocalSingleton, &threadData, location, type_.typeInfo(), constructorImpl);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, mm::ThreadData& threadData) {
|
||||
RETURN_RESULT_OF(mm::InitSingleton, &threadData, location, type_.typeInfo(), constructorImpl);
|
||||
}
|
||||
|
||||
private:
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> constructor_;
|
||||
test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
|
||||
|
||||
static testing::MockFunction<void(ObjHeader*)>* globalConstructor_;
|
||||
|
||||
static void constructorImpl(ObjHeader* object) { globalConstructor_->Call(object); }
|
||||
};
|
||||
|
||||
// static
|
||||
testing::MockFunction<void(ObjHeader*)>* InitSingletonTest::globalConstructor_ = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(InitSingletonTest, InitThreadLocalSingleton) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
ObjHeader* valueAtConstructor = nullptr;
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce(
|
||||
[&location, &stackLocation, &valueAtConstructor](ObjHeader* value) {
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
valueAtConstructor = value;
|
||||
});
|
||||
ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(valueAtConstructor, location);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitThreadLocalSingletonTwice) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader previousValue;
|
||||
ObjHeader* location = &previousValue;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(0);
|
||||
ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(value, &previousValue);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitThreadLocalSingletonFail) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
constexpr int kException = 42;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; });
|
||||
try {
|
||||
InitThreadLocalSingleton(&location, threadData, &stackLocation);
|
||||
ASSERT_TRUE(false); // Cannot be reached.
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
}
|
||||
EXPECT_THAT(stackLocation, nullptr);
|
||||
EXPECT_THAT(location, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingleton) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
ObjHeader* valueAtConstructor = nullptr;
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce(
|
||||
[&location, &stackLocation, &valueAtConstructor](ObjHeader* value) {
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(location, kInitializingSingleton);
|
||||
valueAtConstructor = value;
|
||||
});
|
||||
ObjHeader* value = InitSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(valueAtConstructor, location);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonTwice) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader previousValue;
|
||||
ObjHeader* location = &previousValue;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(0);
|
||||
ObjHeader* value = InitSingleton(&location, threadData, &stackLocation);
|
||||
EXPECT_THAT(value, stackLocation);
|
||||
EXPECT_THAT(value, location);
|
||||
EXPECT_THAT(value, &previousValue);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonFail) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
ObjHeader* location = nullptr;
|
||||
ObjHeader* stackLocation = nullptr;
|
||||
constexpr int kException = 42;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; });
|
||||
try {
|
||||
InitSingleton(&location, threadData, &stackLocation);
|
||||
ASSERT_TRUE(false); // Cannot be reached.
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
}
|
||||
EXPECT_THAT(stackLocation, nullptr);
|
||||
EXPECT_THAT(location, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonRecursive) {
|
||||
RunInNewThread([this](mm::ThreadData& threadData) {
|
||||
// The first singleton. Its constructor depends on the second singleton.
|
||||
ObjHeader* location1 = nullptr;
|
||||
ObjHeader* stackLocation1 = nullptr;
|
||||
// The second singleton. Its constructor depends on the first singleton.
|
||||
ObjHeader* location2 = nullptr;
|
||||
ObjHeader* stackLocation2 = nullptr;
|
||||
|
||||
EXPECT_CALL(constructor(), Call(_))
|
||||
.Times(2) // called only once for each singleton.
|
||||
.WillRepeatedly([this, &location1, &stackLocation1, &location2, &stackLocation2, &threadData](ObjHeader* value) {
|
||||
if (value == stackLocation1) {
|
||||
ObjHeader* result = InitSingleton(&location2, threadData, &stackLocation2);
|
||||
EXPECT_THAT(result, stackLocation2);
|
||||
EXPECT_THAT(result, location2);
|
||||
EXPECT_THAT(result, testing::Not(testing::Truly(isNullOrMarker)));
|
||||
} else {
|
||||
ObjHeader* result = InitSingleton(&location1, threadData, &stackLocation1);
|
||||
EXPECT_THAT(result, stackLocation1);
|
||||
EXPECT_THAT(result, testing::Ne(location1));
|
||||
EXPECT_THAT(location1, kInitializingSingleton);
|
||||
}
|
||||
});
|
||||
ObjHeader* value = InitSingleton(&location1, threadData, &stackLocation1);
|
||||
EXPECT_THAT(value, stackLocation1);
|
||||
EXPECT_THAT(value, location1);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonConcurrent) {
|
||||
constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<size_t> readyCount(0);
|
||||
std_support::vector<ScopedThread> threads;
|
||||
ObjHeader* location = nullptr;
|
||||
std_support::vector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
||||
std_support::vector<ObjHeader*> actual(kThreadCount, nullptr);
|
||||
|
||||
for (size_t i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([this, i, &location, &stackLocations, &actual, &readyCount, &canStart]() {
|
||||
ScopedMemoryInit init;
|
||||
auto* threadData = init.memoryState()->GetThreadData();
|
||||
++readyCount;
|
||||
while (!canStart) {
|
||||
}
|
||||
actual[i] = InitSingleton(&location, *threadData, &stackLocations[i]);
|
||||
threadData->Publish();
|
||||
});
|
||||
}
|
||||
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
// Constructor is called exactly once.
|
||||
EXPECT_CALL(constructor(), Call(_));
|
||||
canStart = true;
|
||||
threads.clear();
|
||||
testing::Mock::VerifyAndClearExpectations(&constructor());
|
||||
|
||||
EXPECT_THAT(location, testing::Not(testing::Truly(isNullOrMarker)));
|
||||
EXPECT_THAT(stackLocations, testing::Each(location));
|
||||
EXPECT_THAT(actual, testing::Each(location));
|
||||
}
|
||||
|
||||
TEST_F(InitSingletonTest, InitSingletonConcurrentFailing) {
|
||||
constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<size_t> readyCount(0);
|
||||
std_support::vector<ScopedThread> threads;
|
||||
constexpr int kException = 42;
|
||||
ObjHeader* location = nullptr;
|
||||
std_support::vector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
||||
|
||||
for (size_t i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([this, i, &location, &stackLocations, &readyCount, &canStart]() {
|
||||
ScopedMemoryInit init;
|
||||
auto* threadData = init.memoryState()->GetThreadData();
|
||||
++readyCount;
|
||||
while (!canStart) {
|
||||
}
|
||||
try {
|
||||
InitSingleton(&location, *threadData, &stackLocations[i]);
|
||||
ASSERT_TRUE(false); // Cannot be reached.
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
}
|
||||
threadData->Publish();
|
||||
});
|
||||
}
|
||||
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
// Constructor is called exactly `kThreadCount` times.
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(kThreadCount).WillRepeatedly([]() { throw kException; });
|
||||
canStart = true;
|
||||
threads.clear();
|
||||
testing::Mock::VerifyAndClearExpectations(&constructor());
|
||||
|
||||
EXPECT_THAT(location, nullptr);
|
||||
EXPECT_THAT(stackLocations, testing::Each(nullptr));
|
||||
}
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "Freezing.hpp"
|
||||
#include "GC.hpp"
|
||||
#include "GlobalsRegistry.hpp"
|
||||
#include "InitializationScheme.hpp"
|
||||
#include "KAssert.h"
|
||||
#include "Natives.h"
|
||||
#include "ObjectOps.hpp"
|
||||
@@ -146,18 +145,6 @@ extern "C" OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elem
|
||||
RETURN_RESULT_OF(mm::AllocateArray, threadData, typeInfo, static_cast<uint32_t>(elements));
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
|
||||
RETURN_RESULT_OF(mm::InitThreadLocalSingleton, threadData, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
|
||||
RETURN_RESULT_OF(mm::InitSingleton, threadData, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
extern "C" RUNTIME_NOTHROW void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
*/
|
||||
|
||||
#include "ThreadLocalStorage.hpp"
|
||||
#include "Logging.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept {
|
||||
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
||||
RuntimeAssert(size >= 0, "Size cannot be negative");
|
||||
RuntimeLogDebug({kTagTLS}, "Add record key = %p, size = %d\n", key, size);
|
||||
auto it = map_.find(key);
|
||||
if (it != map_.end()) {
|
||||
RuntimeAssert(it->second.size == size, "Attempt to add TLS record with the same key, but different size");
|
||||
@@ -20,12 +22,14 @@ void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept {
|
||||
}
|
||||
|
||||
void mm::ThreadLocalStorage::Commit() noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Committed");
|
||||
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
||||
storage_.resize(size_);
|
||||
state_ = State::kCommitted;
|
||||
}
|
||||
|
||||
void mm::ThreadLocalStorage::Clear() noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Cleared");
|
||||
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
||||
// Just free the storage.
|
||||
storage_.clear();
|
||||
@@ -33,6 +37,7 @@ void mm::ThreadLocalStorage::Clear() noexcept {
|
||||
}
|
||||
|
||||
ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Lookup key = %p, index = %d", key, index);
|
||||
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
||||
if (lastKeyAndEntry_.first == key) {
|
||||
return Lookup(lastKeyAndEntry_.second, index);
|
||||
@@ -44,6 +49,7 @@ ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) noexcept {
|
||||
}
|
||||
|
||||
ObjHeader** mm::ThreadLocalStorage::Lookup(Entry entry, int index) noexcept {
|
||||
RuntimeLogDebug({kTagTLS}, "Lookup entry = {%d, %d}, index = %d", entry.offset, entry.size, index);
|
||||
RuntimeAssert(index < entry.size, "Out of bounds TLS access");
|
||||
return &storage_[entry.offset + index];
|
||||
}
|
||||
|
||||
@@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
||||
RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitThreadLocalSingletonRelaxed, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitSingletonRelaxed, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||
ReleaseHeapRefRelaxed(object);
|
||||
}
|
||||
|
||||
@@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
||||
RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitThreadLocalSingletonStrict, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitSingletonStrict, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||
ReleaseHeapRefStrict(object);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user