Alexander Shabalin
2021-05-27 17:18:55 +00:00
committed by Space
parent 0b1884c994
commit af1f57007a
23 changed files with 190 additions and 75 deletions
@@ -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);
+4 -2
View File
@@ -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);
}