Native: improve ObjCExport thread state switching

This commit is contained in:
Svyatoslav Scherbina
2021-05-25 14:16:08 +03:00
committed by Space
parent 388538be60
commit fa36ccedeb
14 changed files with 173 additions and 51 deletions
@@ -133,6 +133,7 @@ internal inline fun<R> generateFunction(codegen: CodeGenerator,
codegen, codegen,
startLocation, startLocation,
endLocation, endLocation,
switchToRunnable = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE,
function) function)
try { try {
generateFunctionBody(functionGenerationContext, code) generateFunctionBody(functionGenerationContext, code)
@@ -148,8 +149,15 @@ internal inline fun<R> generateFunction(codegen: CodeGenerator,
internal inline fun<R> generateFunction(codegen: CodeGenerator, function: LLVMValueRef, internal inline fun<R> generateFunction(codegen: CodeGenerator, function: LLVMValueRef,
startLocation: LocationInfo? = null, endLocation: LocationInfo? = null, startLocation: LocationInfo? = null, endLocation: LocationInfo? = null,
switchToRunnable: Boolean = false,
code:FunctionGenerationContext.(FunctionGenerationContext) -> R) { code:FunctionGenerationContext.(FunctionGenerationContext) -> R) {
val functionGenerationContext = FunctionGenerationContext(function, codegen, startLocation, endLocation) val functionGenerationContext = FunctionGenerationContext(
function,
codegen,
startLocation,
endLocation,
switchToRunnable = switchToRunnable
)
try { try {
generateFunctionBody(functionGenerationContext, code) generateFunctionBody(functionGenerationContext, code)
} finally { } finally {
@@ -161,6 +169,7 @@ internal inline fun generateFunction(
codegen: CodeGenerator, codegen: CodeGenerator,
functionType: LLVMTypeRef, functionType: LLVMTypeRef,
name: String, name: String,
switchToRunnable: Boolean = false,
block: FunctionGenerationContext.(FunctionGenerationContext) -> Unit block: FunctionGenerationContext.(FunctionGenerationContext) -> Unit
): LLVMValueRef { ): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes( val function = addLlvmFunctionWithDefaultAttributes(
@@ -169,7 +178,7 @@ internal inline fun generateFunction(
name, name,
functionType functionType
) )
generateFunction(codegen, function, startLocation = null, endLocation = null, code = block) generateFunction(codegen, function, startLocation = null, endLocation = null, switchToRunnable = switchToRunnable, code = block)
return function return function
} }
@@ -179,7 +188,7 @@ internal inline fun <R> generateFunctionNoRuntime(
function: LLVMValueRef, function: LLVMValueRef,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R, code: FunctionGenerationContext.(FunctionGenerationContext) -> R,
) { ) {
val functionGenerationContext = FunctionGenerationContext(function, codegen, null, null) val functionGenerationContext = FunctionGenerationContext(function, codegen, null, null, switchToRunnable = false)
try { try {
functionGenerationContext.forbidRuntime = true functionGenerationContext.forbidRuntime = true
require(!functionGenerationContext.isObjectType(functionGenerationContext.returnType!!)) { require(!functionGenerationContext.isObjectType(functionGenerationContext.returnType!!)) {
@@ -367,6 +376,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
val codegen: CodeGenerator, val codegen: CodeGenerator,
startLocation: LocationInfo?, startLocation: LocationInfo?,
private val endLocation: LocationInfo?, private val endLocation: LocationInfo?,
private val switchToRunnable: Boolean,
internal val irFunction: IrFunction? = null): ContextUtils { internal val irFunction: IrFunction? = null): ContextUtils {
override val context = codegen.context override val context = codegen.context
@@ -1301,8 +1311,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
returnSlot = LLVMGetParam(function, numParameters(function.type) - 1) returnSlot = LLVMGetParam(function, numParameters(function.type) - 1)
} }
if (context.memoryModel == MemoryModel.EXPERIMENTAL && if (context.memoryModel == MemoryModel.EXPERIMENTAL && switchToRunnable) {
irFunction?.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE) {
check(!forbidRuntime) { "Attempt to switch the thread state when runtime is forbidden" } check(!forbidRuntime) { "Attempt to switch the thread state when runtime is forbidden" }
positionAtEnd(prologueBb) positionAtEnd(prologueBb)
// TODO: Do we need to do it for every c->kotlin bridge? // TODO: Do we need to do it for every c->kotlin bridge?
@@ -1320,10 +1329,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
internal fun epilogue() { internal fun epilogue() {
appendingTo(prologueBb) { appendingTo(prologueBb) {
if (needsRuntimeInit) { if (needsRuntimeInit && context.config.memoryModel != MemoryModel.EXPERIMENTAL) {
check(!forbidRuntime) { "Attempt to init runtime where runtime usage is forbidden" } check(!forbidRuntime) { "Attempt to init runtime where runtime usage is forbidden" }
call(context.llvm.initRuntimeIfNeeded, emptyList()) call(context.llvm.initRuntimeIfNeeded, emptyList())
// TODO: Do we also need to switch to runnable mode?
} }
val slots = if (needSlotsPhi) val slots = if (needSlotsPhi)
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!! LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
@@ -1391,7 +1399,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
val shouldHaveCleanupLandingpad = forwardingForeignExceptionsTerminatedWith != null || val shouldHaveCleanupLandingpad = forwardingForeignExceptionsTerminatedWith != null ||
needSlots || needSlots ||
!stackLocalsManager.isEmpty() || !stackLocalsManager.isEmpty() ||
(context.memoryModel == MemoryModel.EXPERIMENTAL && irFunction?.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE) (context.memoryModel == MemoryModel.EXPERIMENTAL && switchToRunnable)
if (shouldHaveCleanupLandingpad) { if (shouldHaveCleanupLandingpad) {
appendingTo(cleanupLandingpad) { appendingTo(cleanupLandingpad) {
@@ -1455,7 +1463,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
if (!forbidRuntime) { if (!forbidRuntime) {
call(safePointFunction, emptyList()) call(safePointFunction, emptyList())
} }
if (irFunction?.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE) { if (switchToRunnable) {
check(!forbidRuntime) { "Generating a bridge when runtime is forbidden" } check(!forbidRuntime) { "Generating a bridge when runtime is forbidden" }
switchThreadState(Native) switchThreadState(Native)
} }
@@ -52,7 +52,7 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
) )
val invoke = loadBlockInvoke(blockPtr, bridge) val invoke = loadBlockInvoke(blockPtr, bridge)
val result = callFromBridge(invoke, listOf(blockPtr) + args) val result = callFromBridge(invoke, listOf(blockPtr) + args, toNative = true)
val kotlinResult = if (bridge.returnsVoid) { val kotlinResult = if (bridge.returnsVoid) {
theUnitInstanceRef.llvm theUnitInstanceRef.llvm
@@ -154,7 +154,8 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
val disposeHelper = generateFunction( val disposeHelper = generateFunction(
codegen, codegen,
functionType(voidType, false, int8TypePtr), functionType(voidType, false, int8TypePtr),
"blockDisposeHelper" "blockDisposeHelper",
switchToRunnable = true
) { ) {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0)) val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
val refHolder = structGep(blockPtr, 1) val refHolder = structGep(blockPtr, 1)
@@ -234,7 +235,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
invokeName: String, invokeName: String,
genBody: FunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit genBody: FunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit
): ConstPointer { ): ConstPointer {
val result = generateFunction(codegen, blockType.blockInvokeLlvmType, invokeName) { val result = generateFunction(codegen, blockType.blockInvokeLlvmType, invokeName, switchToRunnable = true) {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0)) val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
val kotlinObject = call( val kotlinObject = call(
context.llvm.kRefSharedHolderRef, context.llvm.kRefSharedHolderRef,
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.ForeignExceptionMode
import org.jetbrains.kotlin.konan.target.AppleConfigurables import org.jetbrains.kotlin.konan.target.AppleConfigurables
import org.jetbrains.kotlin.konan.target.LinkerOutputKind import org.jetbrains.kotlin.konan.target.LinkerOutputKind
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -67,13 +68,31 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
fun FunctionGenerationContext.callFromBridge( fun FunctionGenerationContext.callFromBridge(
function: LLVMValueRef, function: LLVMValueRef,
args: List<LLVMValueRef>, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT resultLifetime: Lifetime = Lifetime.IRRELEVANT,
toNative: Boolean = false
): LLVMValueRef { ): LLVMValueRef {
// TODO: it is required only for Kotlin-to-Objective-C bridges. // TODO: it is required only for Kotlin-to-Objective-C bridges.
this.forwardingForeignExceptionsTerminatedWith = objcTerminate this.forwardingForeignExceptionsTerminatedWith = objcTerminate
return call(function, args, resultLifetime, ExceptionHandler.Caller) val switchStateToNative = toNative && context.config.memoryModel == MemoryModel.EXPERIMENTAL
val exceptionHandler: ExceptionHandler
if (switchStateToNative) {
switchThreadState(ThreadState.Native)
// Note: this is suboptimal. We should forbid Kotlin exceptions thrown from native code, and use simple fatal handler here.
exceptionHandler = filteringExceptionHandler(ExceptionHandler.Caller, ForeignExceptionMode.default, switchThreadState = true)
} else {
exceptionHandler = ExceptionHandler.Caller
}
val result = call(function, args, resultLifetime, exceptionHandler)
if (switchStateToNative) {
switchThreadState(ThreadState.Runnable)
}
return result
} }
fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) = fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) =
@@ -134,7 +153,8 @@ internal class ObjCExportCodeGenerator(
returnType: LLVMTypeRef, returnType: LLVMTypeRef,
receiver: LLVMValueRef, receiver: LLVMValueRef,
selector: String, selector: String,
vararg args: LLVMValueRef switchToNative: Boolean,
vararg args: LLVMValueRef,
): LLVMValueRef { ): LLVMValueRef {
val objcMsgSendType = functionType( val objcMsgSendType = functionType(
@@ -143,7 +163,7 @@ internal class ObjCExportCodeGenerator(
listOf(int8TypePtr, int8TypePtr) + args.map { it.type } listOf(int8TypePtr, int8TypePtr) + args.map { it.type }
) )
return callFromBridge(msgSender(objcMsgSendType), listOf(receiver, genSelector(selector)) + args) return callFromBridge(msgSender(objcMsgSendType), listOf(receiver, genSelector(selector)) + args, toNative = switchToNative)
} }
fun FunctionGenerationContext.kotlinToObjC( fun FunctionGenerationContext.kotlinToObjC(
@@ -621,7 +641,8 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
val value = kotlinToObjC(kotlinValue, objCValueType) val value = kotlinToObjC(kotlinValue, objCValueType)
val nsNumberSubclass = genGetLinkedClass(namer.numberBoxName(boxClass.classId!!).binaryName) val nsNumberSubclass = genGetLinkedClass(namer.numberBoxName(boxClass.classId!!).binaryName)
ret(genSendMessage(int8TypePtr, nsNumberSubclass, nsNumberFactorySelector, value)) val switchToNative = false // We consider these methods fast enough.
ret(genSendMessage(int8TypePtr, nsNumberSubclass, nsNumberFactorySelector, switchToNative, value))
} }
LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage) LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage)
@@ -748,7 +769,7 @@ private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
null null
} }
generateFunction(codegen, result, startLocation = location, endLocation = location) { generateFunction(codegen, result, startLocation = location, endLocation = location, switchToRunnable = true) {
genBody() genBody()
} }
@@ -861,7 +882,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
is MethodBridge.ReturnValue.WithError.ZeroForError -> { is MethodBridge.ReturnValue.WithError.ZeroForError -> {
if (returnType.successBridge == MethodBridge.ReturnValue.Instance.InitResult) { if (returnType.successBridge == MethodBridge.ReturnValue.Instance.InitResult) {
// Release init receiver, as required by convention. // Release init receiver, as required by convention.
callFromBridge(objcRelease, listOf(param(0))) callFromBridge(objcRelease, listOf(param(0)), toNative = true)
} }
Zero(returnType.objCType(context)).llvm Zero(returnType.objCType(context)).llvm
} }
@@ -1040,7 +1061,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
} }
} }
val targetResult = callFromBridge(objcMsgSend, objCArgs) val targetResult = callFromBridge(objcMsgSend, objCArgs, toNative = true)
assert(baseMethod.symbol !is IrConstructorSymbol) assert(baseMethod.symbol !is IrConstructorSymbol)
@@ -1496,7 +1517,7 @@ private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter(
MethodBridgeReceiver.Static, valueParameters = emptyList() MethodBridgeReceiver.Static, valueParameters = emptyList()
) )
val imp = generateFunction(codegen, objCFunctionType(context, methodBridge), "objc2kotlin") { val imp = generateFunction(codegen, objCFunctionType(context, methodBridge), "objc2kotlin", switchToRunnable = true) {
block() block()
} }
@@ -1517,6 +1538,7 @@ private fun ObjCExportCodeGenerator.objCToKotlinMethodAdapter(
private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(selector: String) = private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(selector: String) =
generateObjCToKotlinSyntheticGetter(selector) { generateObjCToKotlinSyntheticGetter(selector) {
// Note: generateObjCToKotlinSyntheticGetter switches to Runnable, which is probably not required here and thus suboptimal.
initRuntimeIfNeeded() // For instance methods it gets called when allocating. initRuntimeIfNeeded() // For instance methods it gets called when allocating.
ret(callFromBridge(context.llvm.Kotlin_ObjCExport_convertUnit, listOf(codegen.theUnitInstanceRef.llvm))) ret(callFromBridge(context.llvm.Kotlin_ObjCExport_convertUnit, listOf(codegen.theUnitInstanceRef.llvm)))
@@ -3746,4 +3746,8 @@ kotlin::ThreadState kotlin::GetThreadState(MemoryState* thread) noexcept {
return ThreadState::kRunnable; return ThreadState::kRunnable;
} }
ALWAYS_INLINE kotlin::CalledFromNativeGuard::CalledFromNativeGuard() noexcept {
// no-op, used by the new MM only.
}
const bool kotlin::kSupportsMultipleMutators = true; const bool kotlin::kSupportsMultipleMutators = true;
@@ -442,6 +442,20 @@ private:
bool reentrant_; bool reentrant_;
}; };
// Scopely sets the kRunnable thread state for the current thread,
// and initializes runtime if needed for new MM.
// No-op for old GC.
class CalledFromNativeGuard final : private Pinned {
public:
ALWAYS_INLINE CalledFromNativeGuard() noexcept;
~CalledFromNativeGuard() noexcept {
SwitchThreadState(thread_, ThreadState::kNative);
}
private:
MemoryState* thread_;
};
template <ThreadState state, typename R, typename... Args> template <ThreadState state, typename R, typename... Args>
ALWAYS_INLINE inline R CallWithThreadState(R(*function)(Args...), Args... args) { ALWAYS_INLINE inline R CallWithThreadState(R(*function)(Args...), Args... args) {
ThreadStateGuard guard(state); ThreadStateGuard guard(state);
@@ -19,7 +19,7 @@ inline bool isForeignRefAccessible(ObjHeader* object, ForeignRefContext context)
// If runtime has not been initialized on this thread, then the object is either unowned or shared. // If runtime has not been initialized on this thread, then the object is either unowned or shared.
// In the former case initialized runtime is required to throw exceptions // In the former case initialized runtime is required to throw exceptions
// in the latter case -- to provide proper execution context for caller. // in the latter case -- to provide proper execution context for caller.
// TODO: Can this be called in uninitialized state in the new MM? // TODO: this probably can't be called in uninitialized state in the new MM.
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
return IsForeignRefAccessible(object, context); return IsForeignRefAccessible(object, context);
@@ -85,6 +85,7 @@ void KRefSharedHolder::init(ObjHeader* obj) {
template <ErrorPolicy errorPolicy> template <ErrorPolicy errorPolicy>
ObjHeader* KRefSharedHolder::ref() const { ObjHeader* KRefSharedHolder::ref() const {
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
if (!ensureRefAccessible<errorPolicy>(obj_, context_)) { if (!ensureRefAccessible<errorPolicy>(obj_, context_)) {
return nullptr; return nullptr;
} }
@@ -129,11 +130,15 @@ void BackRefFromAssociatedObject::addRef() {
// There are no references to the associated object itself, so Kotlin object is being passed from Kotlin, // There are no references to the associated object itself, so Kotlin object is being passed from Kotlin,
// and it is owned therefore. // and it is owned therefore.
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
ensureRefAccessible<errorPolicy>(obj_, context_); // TODO: consider removing explicit verification. ensureRefAccessible<errorPolicy>(obj_, context_); // TODO: consider removing explicit verification.
// Foreign reference has already been deinitialized (see [releaseRef]). // Foreign reference has already been deinitialized (see [releaseRef]).
// Create a new one: // Create a new one:
context_ = InitForeignRef(obj_); context_ = InitForeignRef(obj_);
} else {
// Can be called both from Native state (if ObjC or Swift code adds RC)
// and from Runnable state (Kotlin_ObjCExport_refToObjC).
} }
} }
@@ -143,6 +148,7 @@ template void BackRefFromAssociatedObject::addRef<ErrorPolicy::kTerminate>();
template <ErrorPolicy errorPolicy> template <ErrorPolicy errorPolicy>
bool BackRefFromAssociatedObject::tryAddRef() { bool BackRefFromAssociatedObject::tryAddRef() {
static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here"); static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here");
kotlin::CalledFromNativeGuard guard;
if (obj_ == nullptr) return false; // E.g. after [detach]. if (obj_ == nullptr) return false; // E.g. after [detach].
@@ -180,6 +186,8 @@ void BackRefFromAssociatedObject::releaseRef() {
if (atomicAdd(&refCount, -1) == 0) { if (atomicAdd(&refCount, -1) == 0) {
if (obj_ == nullptr) return; // E.g. after [detach]. if (obj_ == nullptr) return; // E.g. after [detach].
kotlin::CalledFromNativeGuard guard;
// Note: by this moment "subsequent" addRef may have already happened and patched context_. // Note: by this moment "subsequent" addRef may have already happened and patched context_.
// So use the value loaded before refCount update: // So use the value loaded before refCount update:
DeinitForeignRef(obj_, context); DeinitForeignRef(obj_, context);
@@ -200,6 +208,7 @@ ALWAYS_INLINE void BackRefFromAssociatedObject::assertDetached() {
template <ErrorPolicy errorPolicy> template <ErrorPolicy errorPolicy>
ObjHeader* BackRefFromAssociatedObject::ref() const { ObjHeader* BackRefFromAssociatedObject::ref() const {
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
RuntimeAssert(obj_ != nullptr, "no valid Kotlin object found"); RuntimeAssert(obj_ != nullptr, "no valid Kotlin object found");
if (!ensureRefAccessible<errorPolicy>(obj_, context_)) { if (!ensureRefAccessible<errorPolicy>(obj_, context_)) {
@@ -30,6 +30,11 @@ class KRefSharedHolder {
void dispose() const; void dispose() const;
void disposeFromNative() const {
kotlin::CalledFromNativeGuard guard;
dispose();
}
OBJ_GETTER0(describe) const; OBJ_GETTER0(describe) const;
private: private:
@@ -126,24 +126,32 @@ extern "C" id objc_retainAutoreleaseReturnValue(id self);
namespace { namespace {
ALWAYS_INLINE void send_releaseAsAssociatedObject(void* associatedObject, ReleaseMode mode) { ALWAYS_INLINE void send_releaseAsAssociatedObject(void* associatedObject, ReleaseMode mode) {
if (associatedObject != nullptr) { auto msgSend = reinterpret_cast<void (*)(void* self, SEL cmd, ReleaseMode mode)>(&objc_msgSend);
auto msgSend = reinterpret_cast<void (*)(void* self, SEL cmd, ReleaseMode mode)>(&objc_msgSend); msgSend(associatedObject, Kotlin_ObjCExport_releaseAsAssociatedObjectSelector, mode);
msgSend(associatedObject, Kotlin_ObjCExport_releaseAsAssociatedObjectSelector, mode);
}
} }
} // namespace } // namespace
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) { extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) {
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kRelease); if (associatedObject != nullptr) {
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kRelease);
}
} }
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAndReleaseAssociatedObject(void* associatedObject) { extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAndReleaseAssociatedObject(void* associatedObject) {
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kDetachAndRelease); if (associatedObject != nullptr) {
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kDetachAndRelease);
}
} }
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAssociatedObject(void* associatedObject) { extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAssociatedObject(void* associatedObject) {
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kDetach); if (associatedObject != nullptr) {
// Switching to Native state is not required, because detach is fast and can't call user code.
// Also switching is not possible, because this is called from GC.
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kDetach);
}
} }
extern "C" id Kotlin_ObjCExport_convertUnit(ObjHeader* unitInstance) { extern "C" id Kotlin_ObjCExport_convertUnit(ObjHeader* unitInstance) {
@@ -498,6 +506,8 @@ static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
template <bool retainAutorelease> template <bool retainAutorelease>
static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* obj) { static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* obj) {
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
if (obj == nullptr) return nullptr; if (obj == nullptr) return nullptr;
id associatedObject = GetAssociatedObject(obj); id associatedObject = GetAssociatedObject(obj);
@@ -536,6 +546,8 @@ extern "C" OBJ_GETTER(Kotlin_Interop_CreateObjCObjectHolder, id obj) {
} }
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) { extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) {
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
if (obj == nullptr) RETURN_OBJ(nullptr); if (obj == nullptr) RETURN_OBJ(nullptr);
auto msgSend = reinterpret_cast<ObjHeader* (*)(id self, SEL cmd, ObjHeader** slot)>(&objc_msgSend); auto msgSend = reinterpret_cast<ObjHeader* (*)(id self, SEL cmd, ObjHeader** slot)>(&objc_msgSend);
RETURN_RESULT_OF(msgSend, obj, Kotlin_ObjCExport_toKotlinSelector); RETURN_RESULT_OF(msgSend, obj, Kotlin_ObjCExport_toKotlinSelector);
@@ -46,8 +46,8 @@ static inline OBJ_GETTER(refFromObjCOrNSNull, id obj) {
} }
static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj) { static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj) {
// TODO: this probably can't be called in uninitialized state in the new MM.
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
// TODO: Does this need a switch to runnable state?
KRef kotlinObj = func(OBJ_RESULT); KRef kotlinObj = func(OBJ_RESULT);
@@ -358,6 +358,7 @@ konan::AutoreleasePool::AutoreleasePool()
: handle(objc_autoreleasePoolPush()) {} : handle(objc_autoreleasePoolPush()) {}
konan::AutoreleasePool::~AutoreleasePool() { konan::AutoreleasePool::~AutoreleasePool() {
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
objc_autoreleasePoolPop(handle); objc_autoreleasePoolPop(handle);
} }
@@ -366,6 +367,7 @@ void* Kotlin_objc_autoreleasePoolPush() {
} }
void Kotlin_objc_autoreleasePoolPop(void* ptr) { void Kotlin_objc_autoreleasePoolPop(void* ptr) {
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
objc_autoreleasePoolPop(ptr); objc_autoreleasePoolPop(ptr);
} }
@@ -149,6 +149,7 @@ RuntimeState* initRuntime() {
} }
void deinitRuntime(RuntimeState* state, bool destroyRuntime) { void deinitRuntime(RuntimeState* state, bool destroyRuntime) {
AssertThreadState(state->memoryState, kotlin::ThreadState::kRunnable);
RuntimeAssert(state->status == RuntimeStatus::kRunning, "Runtime must be in the running state"); RuntimeAssert(state->status == RuntimeStatus::kRunning, "Runtime must be in the running state");
state->status = RuntimeStatus::kDestroying; state->status = RuntimeStatus::kDestroying;
// This may be called after TLS is zeroed out, so ::runtimeState and ::memoryState in Memory cannot be trusted. // This may be called after TLS is zeroed out, so ::runtimeState and ::memoryState in Memory cannot be trusted.
@@ -465,18 +465,21 @@ extern "C" void EnsureNeverFrozen(ObjHeader* obj) {
} }
extern "C" ForeignRefContext InitLocalForeignRef(ObjHeader* object) { extern "C" ForeignRefContext InitLocalForeignRef(ObjHeader* object) {
AssertThreadState(ThreadState::kRunnable);
// TODO: Remove when legacy MM is gone. // TODO: Remove when legacy MM is gone.
// Nothing to do. // Nothing to do.
return nullptr; return nullptr;
} }
extern "C" ForeignRefContext InitForeignRef(ObjHeader* object) { extern "C" ForeignRefContext InitForeignRef(ObjHeader* object) {
AssertThreadState(ThreadState::kRunnable);
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
auto* node = mm::StableRefRegistry::Instance().RegisterStableRef(threadData, object); auto* node = mm::StableRefRegistry::Instance().RegisterStableRef(threadData, object);
return ToForeignRefManager(node); return ToForeignRefManager(node);
} }
extern "C" void DeinitForeignRef(ObjHeader* object, ForeignRefContext context) { extern "C" void DeinitForeignRef(ObjHeader* object, ForeignRefContext context) {
AssertThreadState(ThreadState::kRunnable);
RuntimeAssert(context != nullptr, "DeinitForeignRef must not be called for InitLocalForeignRef"); RuntimeAssert(context != nullptr, "DeinitForeignRef must not be called for InitLocalForeignRef");
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
auto* node = FromForeignRefManager(context); auto* node = FromForeignRefManager(context);
@@ -529,4 +532,10 @@ MemoryState* kotlin::mm::GetMemoryState() {
return ToMemoryState(ThreadRegistry::Instance().CurrentThreadDataNode()); return ToMemoryState(ThreadRegistry::Instance().CurrentThreadDataNode());
} }
ALWAYS_INLINE kotlin::CalledFromNativeGuard::CalledFromNativeGuard() noexcept {
Kotlin_initRuntimeIfNeeded();
thread_ = mm::GetMemoryState();
SwitchThreadState(thread_, ThreadState::kRunnable);
}
const bool kotlin::kSupportsMultipleMutators = kotlin::gc::kSupportsMultipleMutators; const bool kotlin::kSupportsMultipleMutators = kotlin::gc::kSupportsMultipleMutators;
@@ -61,7 +61,7 @@ static void injectToRuntime();
+(instancetype)allocWithZone:(NSZone*)zone { +(instancetype)allocWithZone:(NSZone*)zone {
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
// TODO: Does this need a switch to runnable state? kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
KotlinBase* result = [super allocWithZone:zone]; KotlinBase* result = [super allocWithZone:zone];
@@ -86,6 +86,8 @@ static void injectToRuntime();
} }
+(instancetype)createWrapper:(ObjHeader*)obj { +(instancetype)createWrapper:(ObjHeader*)obj {
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
KotlinBase* candidate = [super allocWithZone:nil]; KotlinBase* candidate = [super allocWithZone:nil];
// TODO: should we call NSObject.init ? // TODO: should we call NSObject.init ?
candidate->refHolder.initAndAddRef(obj); candidate->refHolder.initAndAddRef(obj);
@@ -97,8 +99,11 @@ static void injectToRuntime();
} else { } else {
id old = AtomicCompareAndSwapAssociatedObject(obj, nullptr, candidate); id old = AtomicCompareAndSwapAssociatedObject(obj, nullptr, candidate);
if (old != nullptr) { if (old != nullptr) {
candidate->refHolder.releaseRef(); {
[candidate releaseAsAssociatedObject:ReleaseMode::kDetachAndRelease]; kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
candidate->refHolder.releaseRef();
[candidate releaseAsAssociatedObject:ReleaseMode::kDetachAndRelease];
}
return objc_retainAutoreleaseReturnValue(old); return objc_retainAutoreleaseReturnValue(old);
} }
} }
@@ -156,7 +156,7 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
} }
-(void)dealloc { -(void)dealloc {
iteratorHolder.dispose(); iteratorHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
@@ -167,6 +167,7 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
} }
- (id)nextObject { - (id)nextObject {
kotlin::CalledFromNativeGuard guard;
KRef iterator = iteratorHolder.ref<ErrorPolicy::kTerminate>(); KRef iterator = iteratorHolder.ref<ErrorPolicy::kTerminate>();
if (Kotlin_Iterator_hasNext(iterator)) { if (Kotlin_Iterator_hasNext(iterator)) {
ObjHolder holder; ObjHolder holder;
@@ -185,7 +186,7 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
} }
-(void)dealloc { -(void)dealloc {
listHolder.dispose(); listHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
@@ -200,12 +201,14 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
} }
-(id)objectAtIndex:(NSUInteger)index { -(id)objectAtIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
ObjHolder kotlinValueHolder; ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(listHolder.ref<ErrorPolicy::kTerminate>(), index, kotlinValueHolder.slot()); KRef kotlinValue = Kotlin_List_get(listHolder.ref<ErrorPolicy::kTerminate>(), index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue); return refToObjCOrNSNull(kotlinValue);
} }
-(NSUInteger)count { -(NSUInteger)count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(listHolder.ref<ErrorPolicy::kTerminate>()); return Kotlin_Collection_getSize(listHolder.ref<ErrorPolicy::kTerminate>());
} }
@@ -219,7 +222,7 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
} }
-(void)dealloc { -(void)dealloc {
listHolder.dispose(); listHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
@@ -234,35 +237,42 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
} }
-(id)objectAtIndex:(NSUInteger)index { -(id)objectAtIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
ObjHolder kotlinValueHolder; ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(listHolder.ref<ErrorPolicy::kTerminate>(), index, kotlinValueHolder.slot()); KRef kotlinValue = Kotlin_List_get(listHolder.ref<ErrorPolicy::kTerminate>(), index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue); return refToObjCOrNSNull(kotlinValue);
} }
-(NSUInteger)count { -(NSUInteger)count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(listHolder.ref<ErrorPolicy::kTerminate>()); return Kotlin_Collection_getSize(listHolder.ref<ErrorPolicy::kTerminate>());
} }
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index { - (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot()); KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_addObjectAtIndex(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index), kotlinObject); Kotlin_MutableList_addObjectAtIndex(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index), kotlinObject);
} }
- (void)removeObjectAtIndex:(NSUInteger)index { - (void)removeObjectAtIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
Kotlin_MutableList_removeObjectAtIndex(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index)); Kotlin_MutableList_removeObjectAtIndex(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index));
} }
- (void)addObject:(id)anObject { - (void)addObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
Kotlin_MutableCollection_addObject(listHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot())); Kotlin_MutableCollection_addObject(listHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot()));
} }
- (void)removeLastObject { - (void)removeLastObject {
kotlin::CalledFromNativeGuard guard;
Kotlin_MutableList_removeLastObject(listHolder.ref<ErrorPolicy::kTerminate>()); Kotlin_MutableList_removeLastObject(listHolder.ref<ErrorPolicy::kTerminate>());
} }
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot()); KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_setObject(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index), kotlinObject); Kotlin_MutableList_setObject(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index), kotlinObject);
@@ -292,7 +302,7 @@ static inline id KSet_getElement(KRef set, id object) {
} }
-(void)dealloc { -(void)dealloc {
setHolder.dispose(); setHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
@@ -307,20 +317,24 @@ static inline id KSet_getElement(KRef set, id object) {
} }
-(NSUInteger) count { -(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(setHolder.ref<ErrorPolicy::kTerminate>()); return Kotlin_Collection_getSize(setHolder.ref<ErrorPolicy::kTerminate>());
} }
- (id)member:(id)object { - (id)member:(id)object {
kotlin::CalledFromNativeGuard guard;
return KSet_getElement(setHolder.ref<ErrorPolicy::kTerminate>(), object); return KSet_getElement(setHolder.ref<ErrorPolicy::kTerminate>(), object);
} }
// Not mandatory, just an optimization: // Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject { - (BOOL)containsObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
return Kotlin_Set_contains(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot())); return Kotlin_Set_contains(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot()));
} }
- (NSEnumerator*)objectEnumerator { - (NSEnumerator*)objectEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())]; return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
} }
@@ -336,7 +350,7 @@ static inline id KSet_getElement(KRef set, id object) {
-(instancetype)init { -(instancetype)init {
if (self = [super init]) { if (self = [super init]) {
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
// TODO: Does this need a switch to runnable state? kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder; ObjHolder holder;
KRef set = Kotlin_MutableSet_createWithCapacity(8, holder.slot()); KRef set = Kotlin_MutableSet_createWithCapacity(8, holder.slot());
self->setHolder.init(set); self->setHolder.init(set);
@@ -348,7 +362,7 @@ static inline id KSet_getElement(KRef set, id object) {
- (instancetype)initWithCapacity:(NSUInteger)numItems { - (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) { if (self = [super init]) {
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
// TODO: Does this need a switch to runnable state? kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder; ObjHolder holder;
KRef set = Kotlin_MutableSet_createWithCapacity(objCCapacityToKotlin(numItems), holder.slot()); KRef set = Kotlin_MutableSet_createWithCapacity(objCCapacityToKotlin(numItems), holder.slot());
self->setHolder.init(set); self->setHolder.init(set);
@@ -376,7 +390,7 @@ static inline id KSet_getElement(KRef set, id object) {
// Note: since setHolder initialization is not performed directly with alloc, // Note: since setHolder initialization is not performed directly with alloc,
// it is possible that it wasn't initialized properly. // it is possible that it wasn't initialized properly.
// Fortunately setHolder.dispose() handles the zero-initialized case too. // Fortunately setHolder.dispose() handles the zero-initialized case too.
setHolder.dispose(); setHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
@@ -393,30 +407,36 @@ static inline id KSet_getElement(KRef set, id object) {
} }
-(NSUInteger) count { -(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(setHolder.ref<ErrorPolicy::kTerminate>()); return Kotlin_Collection_getSize(setHolder.ref<ErrorPolicy::kTerminate>());
} }
- (id)member:(id)object { - (id)member:(id)object {
kotlin::CalledFromNativeGuard guard;
return KSet_getElement(setHolder.ref<ErrorPolicy::kTerminate>(), object); return KSet_getElement(setHolder.ref<ErrorPolicy::kTerminate>(), object);
} }
// Not mandatory, just an optimization: // Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject { - (BOOL)containsObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
return Kotlin_Set_contains(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot())); return Kotlin_Set_contains(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot()));
} }
- (NSEnumerator*)objectEnumerator { - (NSEnumerator*)objectEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())]; return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
} }
- (void)addObject:(id)object { - (void)addObject:(id)object {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
Kotlin_MutableCollection_addObject(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(object, holder.slot())); Kotlin_MutableCollection_addObject(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(object, holder.slot()));
} }
- (void)removeObject:(id)object { - (void)removeObject:(id)object {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
Kotlin_MutableCollection_removeObject(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(object, holder.slot())); Kotlin_MutableCollection_removeObject(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(object, holder.slot()));
} }
@@ -444,7 +464,7 @@ static inline id KMap_get(KRef map, id aKey) {
} }
-(void)dealloc { -(void)dealloc {
mapHolder.dispose(); mapHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
@@ -462,14 +482,17 @@ static inline id KMap_get(KRef map, id aKey) {
// But that doesn't make any sense, since this class can't be arbitrary initialized. // But that doesn't make any sense, since this class can't be arbitrary initialized.
-(NSUInteger) count { -(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Map_getSize(mapHolder.ref<ErrorPolicy::kTerminate>()); return Kotlin_Map_getSize(mapHolder.ref<ErrorPolicy::kTerminate>());
} }
- (id)objectForKey:(id)aKey { - (id)objectForKey:(id)aKey {
kotlin::CalledFromNativeGuard guard;
return KMap_get(mapHolder.ref<ErrorPolicy::kTerminate>(), aKey); return KMap_get(mapHolder.ref<ErrorPolicy::kTerminate>(), aKey);
} }
- (NSEnumerator *)keyEnumerator { - (NSEnumerator *)keyEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())]; return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
} }
@@ -487,14 +510,14 @@ static inline id KMap_get(KRef map, id aKey) {
// Note: since mapHolder initialization is not performed directly with alloc, // Note: since mapHolder initialization is not performed directly with alloc,
// it is possible that it wasn't initialized properly. // it is possible that it wasn't initialized properly.
// Fortunately mapHolder.dispose() handles the zero-initialized case too. // Fortunately mapHolder.dispose() handles the zero-initialized case too.
mapHolder.dispose(); mapHolder.disposeFromNative();
[super dealloc]; [super dealloc];
} }
-(instancetype)init { -(instancetype)init {
if (self = [super init]) { if (self = [super init]) {
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
// TODO: Does this need a switch to runnable state? kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder; ObjHolder holder;
KRef map = Kotlin_MutableMap_createWithCapacity(8, holder.slot()); KRef map = Kotlin_MutableMap_createWithCapacity(8, holder.slot());
self->mapHolder.init(map); self->mapHolder.init(map);
@@ -511,7 +534,7 @@ static inline id KMap_get(KRef map, id aKey) {
- (instancetype)initWithCapacity:(NSUInteger)numItems { - (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) { if (self = [super init]) {
Kotlin_initRuntimeIfNeeded(); Kotlin_initRuntimeIfNeeded();
// TODO: Does this need a switch to runnable state? kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder; ObjHolder holder;
KRef map = Kotlin_MutableMap_createWithCapacity(objCCapacityToKotlin(numItems), holder.slot()); KRef map = Kotlin_MutableMap_createWithCapacity(objCCapacityToKotlin(numItems), holder.slot());
self->mapHolder.init(map); self->mapHolder.init(map);
@@ -532,31 +555,38 @@ static inline id KMap_get(KRef map, id aKey) {
} }
-(NSUInteger) count { -(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Map_getSize(mapHolder.ref<ErrorPolicy::kTerminate>()); return Kotlin_Map_getSize(mapHolder.ref<ErrorPolicy::kTerminate>());
} }
- (id)objectForKey:(id)aKey { - (id)objectForKey:(id)aKey {
kotlin::CalledFromNativeGuard guard;
return KMap_get(mapHolder.ref<ErrorPolicy::kTerminate>(), aKey); return KMap_get(mapHolder.ref<ErrorPolicy::kTerminate>(), aKey);
} }
- (NSEnumerator *)keyEnumerator { - (NSEnumerator *)keyEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())]; return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
} }
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey { - (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
ObjHolder keyHolder, valueHolder;
id keyCopy = [aKey copyWithZone:nullptr]; // Correspond to the expected NSMutableDictionary behaviour. id keyCopy = [aKey copyWithZone:nullptr]; // Correspond to the expected NSMutableDictionary behaviour.
KRef kotlinKey = refFromObjCOrNSNull(keyCopy, keyHolder.slot()); {
kotlin::CalledFromNativeGuard guard;
ObjHolder keyHolder, valueHolder;
KRef kotlinKey = refFromObjCOrNSNull(keyCopy, keyHolder.slot());
KRef kotlinValue = refFromObjCOrNSNull(anObject, valueHolder.slot());
Kotlin_MutableMap_set(mapHolder.ref<ErrorPolicy::kTerminate>(), kotlinKey, kotlinValue);
}
objc_release(keyCopy); objc_release(keyCopy);
KRef kotlinValue = refFromObjCOrNSNull(anObject, valueHolder.slot());
Kotlin_MutableMap_set(mapHolder.ref<ErrorPolicy::kTerminate>(), kotlinKey, kotlinValue);
} }
- (void)removeObjectForKey:(id)aKey { - (void)removeObjectForKey:(id)aKey {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder; ObjHolder holder;
KRef kotlinKey = refFromObjCOrNSNull(aKey, holder.slot()); KRef kotlinKey = refFromObjCOrNSNull(aKey, holder.slot());