This commit is contained in:
committed by
Space
parent
0b1884c994
commit
af1f57007a
+1
-1
@@ -101,7 +101,7 @@ private fun PatchBuilder.addObjCPatches() {
|
||||
addProtocolImport("NSCopying")
|
||||
|
||||
addPrivateSelector("toKotlin:")
|
||||
addPrivateSelector("releaseAsAssociatedObject")
|
||||
addPrivateSelector("releaseAsAssociatedObject:")
|
||||
|
||||
addPrivateClass("KIteratorAsNSEnumerator", "iteratorHolder")
|
||||
addPrivateClass("KListAsNSArray", "listHolder")
|
||||
|
||||
@@ -4316,7 +4316,7 @@ if (PlatformInfo.isAppleTarget(project)) {
|
||||
}
|
||||
|
||||
interopTestMultifile("interop_objc_tests") {
|
||||
enabled = !isExperimentalMM // Experimental MM does not support Obj-C weaks and thread state switching for ObjC interop yet.
|
||||
enabled = !isExperimentalMM // Experimental MM does not support thread state switching for ObjC interop yet.
|
||||
source = "interop/objc/tests/"
|
||||
interop = 'objcTests'
|
||||
flags = ['-tr', '-e', 'main']
|
||||
@@ -4787,7 +4787,7 @@ Task frameworkTest(String name, Closure<FrameworkTest> configurator) {
|
||||
|
||||
if (isAppleTarget(project)) {
|
||||
frameworkTest('testObjCExport') {
|
||||
enabled = !isExperimentalMM // Experimental MM does not support Obj-C weaks and thread state switching for ObjC interop yet.
|
||||
enabled = !isExperimentalMM // Experimental MM does not support thread state switching for ObjC interop yet.
|
||||
final String frameworkName = 'Kt'
|
||||
final String dir = "$testOutputFramework/testObjCExport"
|
||||
final File lazyHeader = file("$dir/$target-lazy.h")
|
||||
@@ -4836,7 +4836,7 @@ if (isAppleTarget(project)) {
|
||||
}
|
||||
|
||||
frameworkTest('testObjCExportNoGenerics') {
|
||||
enabled = !isExperimentalMM // Experimental MM does not support Obj-C weaks and thread state switching for ObjC interop yet.
|
||||
enabled = !isExperimentalMM // Experimental MM does not support thread state switching for ObjC interop yet.
|
||||
final String frameworkName = 'KtNoGenerics'
|
||||
final String frameworkArtifactName = 'Kt'
|
||||
final String dir = "$testOutputFramework/testObjCExportNoGenerics"
|
||||
@@ -4889,7 +4889,7 @@ if (isAppleTarget(project)) {
|
||||
}
|
||||
|
||||
frameworkTest('testObjCExportStatic') {
|
||||
enabled = !isExperimentalMM // Experimental MM does not support Obj-C weaks and thread state switching for ObjC interop yet.
|
||||
enabled = !isExperimentalMM // Experimental MM does not support thread state switching for ObjC interop yet.
|
||||
final String frameworkName = 'KtStatic'
|
||||
final String frameworkArtifactName = 'Kt'
|
||||
final String libraryName = frameworkName + "Library"
|
||||
|
||||
@@ -2312,6 +2312,7 @@ __attribute__((swift_name("ValuesKt")))
|
||||
@property (class) id _Nullable warningVar __attribute__((swift_name("warningVar"))) __attribute__((deprecated("warning")));
|
||||
@property (class) int32_t gh3525BaseInitCount __attribute__((swift_name("gh3525BaseInitCount")));
|
||||
@property (class) int32_t gh3525InitCount __attribute__((swift_name("gh3525InitCount")));
|
||||
@property (class, readonly) BOOL isExperimentalMM __attribute__((swift_name("isExperimentalMM")));
|
||||
@end;
|
||||
|
||||
__attribute__((swift_name("InvariantSuper")))
|
||||
|
||||
@@ -2285,6 +2285,7 @@ __attribute__((swift_name("ValuesKt")))
|
||||
@property (class) id _Nullable warningVar __attribute__((swift_name("warningVar"))) __attribute__((deprecated("warning")));
|
||||
@property (class) int32_t gh3525BaseInitCount __attribute__((swift_name("gh3525BaseInitCount")));
|
||||
@property (class) int32_t gh3525InitCount __attribute__((swift_name("gh3525InitCount")));
|
||||
@property (class, readonly) BOOL isExperimentalMM __attribute__((swift_name("isExperimentalMM")));
|
||||
@end;
|
||||
|
||||
__attribute__((swift_name("InvariantSuper")))
|
||||
|
||||
@@ -1020,4 +1020,7 @@ interface Bar_FakeOverrideInInterface : Foo_FakeOverrideInInterface<String>
|
||||
|
||||
fun callFoo_FakeOverrideInInterface(obj: Bar_FakeOverrideInInterface) {
|
||||
obj.foo(null)
|
||||
}
|
||||
}
|
||||
|
||||
val isExperimentalMM: Boolean
|
||||
get() = kotlin.native.Platform.memoryModel == kotlin.native.MemoryModel.EXPERIMENTAL
|
||||
|
||||
@@ -596,10 +596,17 @@ func testShared() throws {
|
||||
try assertFalse(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))")
|
||||
}
|
||||
|
||||
try assertFrozen(NSObject())
|
||||
try assertFrozen(TestSharedIImpl())
|
||||
try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject)
|
||||
try assertNotFrozen(FinalClassExtOpen())
|
||||
if ValuesKt.isExperimentalMM {
|
||||
try assertNotFrozen(NSObject())
|
||||
try assertNotFrozen(TestSharedIImpl())
|
||||
try assertNotFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject)
|
||||
try assertNotFrozen(FinalClassExtOpen())
|
||||
} else {
|
||||
try assertFrozen(NSObject())
|
||||
try assertFrozen(TestSharedIImpl())
|
||||
try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject)
|
||||
try assertNotFrozen(FinalClassExtOpen())
|
||||
}
|
||||
}
|
||||
|
||||
class PureSwiftClass {
|
||||
@@ -1411,4 +1418,4 @@ class ValuesTests : SimpleTestProvider {
|
||||
// Stress test, must remain the last one:
|
||||
test("TestGH2931", testGH2931)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ typename Traits::ObjectFactory::FinalizerQueue Sweep(typename Traits::ObjectFact
|
||||
auto* objHeader = it->IsArray() ? it->GetArrayHeader()->obj() : it->GetObjHeader();
|
||||
if (auto* extraObject = mm::ExtraObjectData::Get(objHeader)) {
|
||||
extraObject->ClearWeakReferenceCounter();
|
||||
extraObject->DetachAssociatedObject();
|
||||
}
|
||||
if (HasFinalizers(objHeader)) {
|
||||
iter.MoveAndAdvance(finalizerQueue, it);
|
||||
|
||||
@@ -3137,7 +3137,7 @@ void ObjHeader::destroyMetaObject(ObjHeader* object) {
|
||||
}
|
||||
|
||||
#ifdef KONAN_OBJC_INTEROP
|
||||
Kotlin_ObjCExport_releaseAssociatedObject(meta->associatedObject_);
|
||||
Kotlin_ObjCExport_detachAndReleaseAssociatedObject(meta->associatedObject_);
|
||||
#endif
|
||||
|
||||
konanFreeMemory(meta);
|
||||
@@ -3286,6 +3286,10 @@ RUNTIME_NOTHROW void ReleaseHeapRefNoCollectRelaxed(const ObjHeader* object) {
|
||||
releaseHeapRef<false, /* CanCollect = */ false>(const_cast<ObjHeader*>(object));
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW OBJ_GETTER(TryRef, ObjHeader* object) {
|
||||
RuntimeFail("Only for experimental MM");
|
||||
}
|
||||
|
||||
ForeignRefContext InitLocalForeignRef(ObjHeader* object) {
|
||||
return initLocalForeignRef(object);
|
||||
}
|
||||
|
||||
@@ -327,6 +327,7 @@ MODEL_VARIANTS(void, UpdateHeapRefsInsideOneArray, const ArrayHeader* array, int
|
||||
MODEL_VARIANTS(void, EnterFrame, ObjHeader** start, int parameters, int count);
|
||||
MODEL_VARIANTS(void, LeaveFrame, ObjHeader** start, int parameters, int count);
|
||||
|
||||
void ReleaseHeapRef(const ObjHeader* object) RUNTIME_NOTHROW;
|
||||
MODEL_VARIANTS(void, ReleaseHeapRef, const ObjHeader* object);
|
||||
MODEL_VARIANTS(void, ReleaseHeapRefNoCollect, const ObjHeader* object);
|
||||
|
||||
|
||||
@@ -298,11 +298,13 @@ bool Kotlin_Any_isShareable(ObjHeader* thiz);
|
||||
void Kotlin_Any_share(ObjHeader* thiz);
|
||||
void PerformFullGC(MemoryState* memory) RUNTIME_NOTHROW;
|
||||
|
||||
// Only for legacy
|
||||
bool TryAddHeapRef(const ObjHeader* object);
|
||||
|
||||
void ReleaseHeapRef(const ObjHeader* object) RUNTIME_NOTHROW;
|
||||
void ReleaseHeapRefNoCollect(const ObjHeader* object) RUNTIME_NOTHROW;
|
||||
|
||||
// Only for experimental
|
||||
OBJ_GETTER(TryRef, ObjHeader* object) RUNTIME_NOTHROW;
|
||||
|
||||
ForeignRefContext InitLocalForeignRef(ObjHeader* object);
|
||||
|
||||
ForeignRefContext InitForeignRef(ObjHeader* object);
|
||||
|
||||
@@ -146,19 +146,30 @@ bool BackRefFromAssociatedObject::tryAddRef() {
|
||||
|
||||
if (obj_ == nullptr) return false; // E.g. after [detach].
|
||||
|
||||
// Suboptimal but simple:
|
||||
ensureRefAccessible<errorPolicy>(obj_, context_);
|
||||
if (CurrentMemoryModel == MemoryModel::kExperimental) {
|
||||
ObjHolder holder;
|
||||
ObjHeader* obj = TryRef(obj_, holder.slot());
|
||||
// Failed to lock weak reference.
|
||||
if (obj == nullptr) return false;
|
||||
RuntimeAssert(obj == obj_, "Mismatched locked weak. obj=%p obj_=%p", obj, obj_);
|
||||
// TODO: This is a very weird way to ask for "unsafe" addRef.
|
||||
addRef<ErrorPolicy::kIgnore>();
|
||||
return true;
|
||||
} else {
|
||||
// Suboptimal but simple:
|
||||
ensureRefAccessible<errorPolicy>(obj_, context_);
|
||||
|
||||
ObjHeader* obj = obj_;
|
||||
ObjHeader* obj = obj_;
|
||||
|
||||
if (!TryAddHeapRef(obj)) return false;
|
||||
RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above");
|
||||
// TODO: This is a very weird way to ask for "unsafe" addRef.
|
||||
addRef<ErrorPolicy::kIgnore>();
|
||||
ReleaseHeapRefNoCollect(obj); // Balance TryAddHeapRef.
|
||||
// TODO: consider optimizing for non-shared objects.
|
||||
if (!TryAddHeapRef(obj)) return false;
|
||||
RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above");
|
||||
// TODO: This is a very weird way to ask for "unsafe" addRef.
|
||||
addRef<ErrorPolicy::kIgnore>();
|
||||
ReleaseHeapRefNoCollect(obj); // Balance TryAddHeapRef.
|
||||
// TODO: consider optimizing for non-shared objects.
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template bool BackRefFromAssociatedObject::tryAddRef<ErrorPolicy::kThrow>();
|
||||
@@ -183,6 +194,10 @@ void BackRefFromAssociatedObject::detach() {
|
||||
obj_ = nullptr; // Handled in addRef/tryAddRef/releaseRef/ref.
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void BackRefFromAssociatedObject::assertDetached() {
|
||||
RuntimeAssert(obj_ == nullptr, "Expecting this=%p to be detached, but found obj_=%p", this, obj_);
|
||||
}
|
||||
|
||||
template <ErrorPolicy errorPolicy>
|
||||
ObjHeader* BackRefFromAssociatedObject::ref() const {
|
||||
RuntimeAssert(obj_ != nullptr, "no valid Kotlin object found");
|
||||
|
||||
@@ -54,6 +54,7 @@ class BackRefFromAssociatedObject {
|
||||
void releaseRef();
|
||||
|
||||
void detach();
|
||||
void assertDetached();
|
||||
|
||||
// Error if called from the wrong worker with non-frozen obj_.
|
||||
template <ErrorPolicy errorPolicy>
|
||||
|
||||
@@ -123,13 +123,29 @@ static Class getOrCreateClass(const TypeInfo* typeInfo);
|
||||
|
||||
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
||||
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) {
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE void send_releaseAsAssociatedObject(void* associatedObject, ReleaseMode mode) {
|
||||
if (associatedObject != nullptr) {
|
||||
auto msgSend = reinterpret_cast<void (*)(void* self, SEL cmd)>(&objc_msgSend);
|
||||
msgSend(associatedObject, Kotlin_ObjCExport_releaseAsAssociatedObjectSelector);
|
||||
auto msgSend = reinterpret_cast<void (*)(void* self, SEL cmd, ReleaseMode mode)>(&objc_msgSend);
|
||||
msgSend(associatedObject, Kotlin_ObjCExport_releaseAsAssociatedObjectSelector, mode);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) {
|
||||
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kRelease);
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAndReleaseAssociatedObject(void* associatedObject) {
|
||||
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kDetachAndRelease);
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAssociatedObject(void* associatedObject) {
|
||||
send_releaseAsAssociatedObject(associatedObject, ReleaseMode::kDetach);
|
||||
}
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_convertUnit(ObjHeader* unitInstance) {
|
||||
static dispatch_once_t onceToken;
|
||||
static id instance = nullptr;
|
||||
@@ -300,7 +316,7 @@ static OBJ_GETTER(blockToKotlinImp, id self, SEL cmd);
|
||||
static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd);
|
||||
|
||||
static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd);
|
||||
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd);
|
||||
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd, ReleaseMode mode);
|
||||
|
||||
static void initTypeAdaptersFrom(const ObjCTypeAdapter** adapters, int count) {
|
||||
for (int index = 0; index < count; ++index) {
|
||||
@@ -359,7 +375,7 @@ static void Kotlin_ObjCExport_initializeImpl() {
|
||||
swiftRootClass, releaseAsAssociatedObjectSelector,
|
||||
(IMP)SwiftObject_releaseAsAssociatedObjectImp, releaseAsAssociatedObjectTypeEncoding
|
||||
);
|
||||
RuntimeAssert(added, "Unable to add 'releaseAsAssociatedObject' method to SwiftObject class");
|
||||
RuntimeAssert(added, "Unable to add 'releaseAsAssociatedObject:' method to SwiftObject class");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,7 +395,9 @@ static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd) {
|
||||
RETURN_RESULT_OF(Kotlin_ObjCExport_convertUnmappedObjCObject, self);
|
||||
}
|
||||
|
||||
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd) {
|
||||
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd, ReleaseMode mode) {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,32 @@
|
||||
+(instancetype)createWrapper:(ObjHeader*)obj;
|
||||
@end;
|
||||
|
||||
enum class ReleaseMode {
|
||||
kRelease,
|
||||
kDetachAndRelease,
|
||||
kDetach,
|
||||
};
|
||||
|
||||
inline bool ReleaseModeHasDetach(ReleaseMode mode) {
|
||||
switch (mode) {
|
||||
case ReleaseMode::kRelease:
|
||||
return false;
|
||||
case ReleaseMode::kDetachAndRelease:
|
||||
case ReleaseMode::kDetach:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ReleaseModeHasRelease(ReleaseMode mode) {
|
||||
switch (mode) {
|
||||
case ReleaseMode::kRelease:
|
||||
case ReleaseMode::kDetachAndRelease:
|
||||
return true;
|
||||
case ReleaseMode::kDetach:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void Kotlin_ObjCExport_initializeClass(Class clazz);
|
||||
extern "C" const TypeInfo* Kotlin_ObjCExport_getAssociatedTypeInfo(Class clazz);
|
||||
extern "C" OBJ_GETTER(Kotlin_ObjCExport_convertUnmappedObjCObject, id obj);
|
||||
|
||||
@@ -126,13 +126,15 @@ void releaseImp(id self, SEL _cmd) {
|
||||
getBackRef(self)->releaseRef();
|
||||
}
|
||||
|
||||
void releaseAsAssociatedObjectImp(id self, SEL _cmd) {
|
||||
void releaseAsAssociatedObjectImp(id self, SEL _cmd, ReleaseMode mode) {
|
||||
auto* classData = GetKotlinClassData(self);
|
||||
|
||||
// This function is called by the GC. It made a decision to reclaim Kotlin object, and runs
|
||||
// deallocation hooks at the moment, including deallocation of the "associated object" ([self])
|
||||
// using the [super release] call below.
|
||||
|
||||
auto* backRef = getBackRef(self, classData);
|
||||
|
||||
// The deallocation involves running [self dealloc] which can contain arbitrary code.
|
||||
// In particular, this code can retain and release [self]. Obj-C and Swift runtimes handle this
|
||||
// gracefully (unless the object gets accessed after the deallocation of course), but Kotlin doesn't.
|
||||
@@ -141,17 +143,24 @@ void releaseAsAssociatedObjectImp(id self, SEL _cmd) {
|
||||
// Generally retaining and releasing Kotlin object that is being deallocated would lead to
|
||||
// use-after-dispose and double-dispose problems (with unpredictable consequences) or to an assertion failure.
|
||||
// To workaround this, detach the back ref from the Kotlin object:
|
||||
getBackRef(self, classData)->detach();
|
||||
if (ReleaseModeHasDetach(mode)) {
|
||||
backRef->detach();
|
||||
} else {
|
||||
// With Mark&Sweep this object should already have been detached earlier.
|
||||
backRef->assertDetached();
|
||||
}
|
||||
|
||||
// So retain/release/etc. on [self] won't affect the Kotlin object, and an attempt to get
|
||||
// the reference to it (e.g. when calling Kotlin method on [self]) would crash.
|
||||
// The latter is generally ok, because by the time superclass dealloc gets launched, subclass state
|
||||
// should already be deinitialized, and Kotlin methods operate on the subclass.
|
||||
|
||||
// [super release]
|
||||
Class clazz = classData->objcClass;
|
||||
struct objc_super s = {self, clazz};
|
||||
auto messenger = reinterpret_cast<void (*) (struct objc_super*, SEL _cmd)>(objc_msgSendSuper2);
|
||||
messenger(&s, @selector(release));
|
||||
if (ReleaseModeHasRelease(mode)) {
|
||||
// [super release]
|
||||
Class clazz = classData->objcClass;
|
||||
struct objc_super s = {self, clazz};
|
||||
auto messenger = reinterpret_cast<void (*) (struct objc_super*, SEL _cmd)>(objc_msgSendSuper2);
|
||||
messenger(&s, @selector(release));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#if KONAN_OBJC_INTEROP
|
||||
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAndReleaseAssociatedObject(void* associatedObject);
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAssociatedObject(void* associatedObject);
|
||||
|
||||
namespace konan {
|
||||
class AutoreleasePool : private kotlin::Pinned {
|
||||
|
||||
@@ -65,6 +65,12 @@ void mm::ExtraObjectData::Uninstall(ObjHeader* object) noexcept {
|
||||
delete &data;
|
||||
}
|
||||
|
||||
void mm::ExtraObjectData::DetachAssociatedObject() noexcept {
|
||||
#ifdef KONAN_OBJC_INTEROP
|
||||
Kotlin_ObjCExport_detachAssociatedObject(associatedObject_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mm::ExtraObjectData::HasWeakReferenceCounter() noexcept {
|
||||
return weakReferenceCounter_ != nullptr;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
#ifdef KONAN_OBJC_INTEROP
|
||||
void** GetAssociatedObjectLocation() noexcept { return &associatedObject_; }
|
||||
#endif
|
||||
void DetachAssociatedObject() noexcept;
|
||||
|
||||
ObjHeader** GetWeakCounterLocation() noexcept { return &weakReferenceCounter_; }
|
||||
|
||||
|
||||
@@ -370,6 +370,24 @@ extern "C" RUNTIME_NOTHROW void PerformFullGC(MemoryState* memory) {
|
||||
memory->GetThreadData()->gc().PerformFullGC();
|
||||
}
|
||||
|
||||
extern "C" bool TryAddHeapRef(const ObjHeader* object) {
|
||||
RuntimeFail("Only for legacy MM");
|
||||
}
|
||||
|
||||
extern "C" RUNTIME_NOTHROW void ReleaseHeapRefNoCollect(const ObjHeader* object) {
|
||||
RuntimeFail("Only for legacy MM");
|
||||
}
|
||||
|
||||
extern "C" RUNTIME_NOTHROW OBJ_GETTER(TryRef, ObjHeader* object) {
|
||||
// TODO: With CMS this needs:
|
||||
// * during marking phase if `object` is unmarked: barrier (might be automatic because of the stack write)
|
||||
// and return `object`;
|
||||
// * during marking phase if `object` is marked: return `object`;
|
||||
// * during sweeping phase if `object` is unmarked: return nullptr;
|
||||
// * during sweeping phase if `object` is marked: return `object`;
|
||||
RETURN_OBJ(object);
|
||||
}
|
||||
|
||||
extern "C" RUNTIME_NOTHROW bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
|
||||
// TODO: Remove when legacy MM is gone.
|
||||
return true;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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 "Memory.h"
|
||||
|
||||
#include "KAssert.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool TryAddHeapRef(const ObjHeader* object) {
|
||||
TODO();
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||
TODO();
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW void ReleaseHeapRefNoCollect(const ObjHeader* object) {
|
||||
TODO();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -98,7 +98,7 @@ static void injectToRuntime();
|
||||
id old = AtomicCompareAndSwapAssociatedObject(obj, nullptr, candidate);
|
||||
if (old != nullptr) {
|
||||
candidate->refHolder.releaseRef();
|
||||
[candidate releaseAsAssociatedObject];
|
||||
[candidate releaseAsAssociatedObject:ReleaseMode::kDetachAndRelease];
|
||||
return objc_retainAutoreleaseReturnValue(old);
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ static void injectToRuntime();
|
||||
}
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
// This function is called by the GC. It made a decision to reclaim Kotlin object, and runs
|
||||
// deallocation hooks at the moment, including deallocation of the "associated object" ([self])
|
||||
// using the [super release] call below.
|
||||
@@ -143,13 +143,20 @@ static void injectToRuntime();
|
||||
// Generally retaining and releasing Kotlin object that is being deallocated would lead to
|
||||
// use-after-dispose and double-dispose problems (with unpredictable consequences) or to an assertion failure.
|
||||
// To workaround this, detach the back ref from the Kotlin object:
|
||||
refHolder.detach();
|
||||
if (ReleaseModeHasDetach(mode)) {
|
||||
refHolder.detach();
|
||||
} else {
|
||||
// With Mark&Sweep this object should already have been detached earlier.
|
||||
refHolder.assertDetached();
|
||||
}
|
||||
|
||||
// So retain/release/etc. on [self] won't affect the Kotlin object, and an attempt to get
|
||||
// the reference to it (e.g. when calling Kotlin method on [self]) would crash.
|
||||
// The latter is generally ok because can be triggered only by user-defined Swift/Obj-C
|
||||
// subclasses of Kotlin classes.
|
||||
|
||||
[super release];
|
||||
if (ReleaseModeHasRelease(mode)) {
|
||||
[super release];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
@@ -167,7 +174,9 @@ static void injectToRuntime();
|
||||
RETURN_RESULT_OF(Kotlin_ObjCExport_convertUnmappedObjCObject, self);
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
@end;
|
||||
@@ -240,7 +249,7 @@ static void injectToRuntime() {
|
||||
Kotlin_ObjCExport_toKotlinSelector = @selector(toKotlin:);
|
||||
|
||||
RuntimeCheck(Kotlin_ObjCExport_releaseAsAssociatedObjectSelector == nullptr, errorMessage);
|
||||
Kotlin_ObjCExport_releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject);
|
||||
Kotlin_ObjCExport_releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject:);
|
||||
}
|
||||
|
||||
#endif // KONAN_OBJC_INTEROP
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import "Runtime.h"
|
||||
#import "ObjCExport.h"
|
||||
#import "ObjCExportCollections.h"
|
||||
#import "ObjCExportPrivate.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -92,7 +93,9 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
|
||||
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSArrayAsKList_create, objc_retain(self));
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
@end;
|
||||
@@ -105,7 +108,9 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
|
||||
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSMutableArrayAsKMutableList_create, objc_retain(self));
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
@end;
|
||||
@@ -119,7 +124,9 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
|
||||
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSSetAsKSet_create, objc_retain(self));
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
|
||||
@@ -133,7 +140,9 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
|
||||
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSDictionaryAsKMap_create, objc_retain(self));
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
|
||||
@@ -560,7 +569,9 @@ static inline id KMap_get(KRef map, id aKey) {
|
||||
@end;
|
||||
|
||||
@implementation NSEnumerator (NSEnumeratorAsAssociatedObject)
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_release(self);
|
||||
}
|
||||
@end;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
#import "Memory.h"
|
||||
#import "MemorySharedRefs.hpp"
|
||||
#import "ObjCExportPrivate.h"
|
||||
#import "ObjCInteropUtilsPrivate.h"
|
||||
|
||||
// TODO: rework the interface to reduce the number of virtual calls
|
||||
@@ -73,7 +74,9 @@ void objc_release(id obj);
|
||||
}
|
||||
|
||||
// Called when removing Kotlin object.
|
||||
-(void)releaseAsAssociatedObject {
|
||||
-(void)releaseAsAssociatedObject:(ReleaseMode)mode {
|
||||
if (!ReleaseModeHasRelease(mode))
|
||||
return;
|
||||
objc_destroyWeak(&referred);
|
||||
objc_release(self);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user