From be1acc81030448d2df6341c2b91b9ac54fbf309a Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Thu, 26 Aug 2021 17:40:34 +0300 Subject: [PATCH] Native: eliminate autorelease in ObjCExport set/getAssociatedTypeInfo --- .../runtime/src/main/cpp/ObjCExport.mm | 19 ++++++++++++++++++- .../runtime/src/mm/cpp/CallsChecker.cpp | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExport.mm b/kotlin-native/runtime/src/main/cpp/ObjCExport.mm index 64aaecdcbe2..759251f9bb5 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExport.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExport.mm @@ -104,7 +104,24 @@ extern "C" const TypeInfo* Kotlin_ObjCExport_getAssociatedTypeInfo(Class clazz) } static void setAssociatedTypeInfo(Class clazz, const TypeInfo* typeInfo) { - objc_setAssociatedObject(clazz, &associatedTypeInfoKey, [NSValue valueWithPointer:typeInfo], OBJC_ASSOCIATION_RETAIN); + // Note: [NSValue valueWithPointer:] uses autorelease (without possibility to eliminate this at the call site), + // so using alloc-init sequence to avoid this. + NSValue* value = [[NSValue alloc] initWithBytes:&typeInfo objCType:@encode(void*)]; + + // Note: OBJC_ASSOCIATION_ASSIGN below means that this NSValue will leak. On the other hand, + // TypeInfo will "leak" anyway, and Class will likely "leak" too. So not a big deal. + // Also, alternative modes have problems: + // OBJC_ASSOCIATION_COPY and OBJC_ASSOCIATION_RETAIN make corresponding objc_getAssociatedObject + // use autorelease (without possibility to eliminate this at the call site). + // OBJC_ASSOCIATION_RETAIN_NONATOMIC and OBJC_ASSOCIATION_COPY_NONATOMIC are not thread safe and + // might require explicit synchronization on objc_getAssociatedObject. + // + // So OBJC_ASSOCIATION_ASSIGN seems the best option here. + + // Note: implementation for OBJC_ASSOCIATION_ASSIGN allows value not to be an Obj-C reference at all, + // because it simply stores a pointer without any memory management operations. + // But this is undocumented, and thus unsafe. + objc_setAssociatedObject(clazz, &associatedTypeInfoKey, value, OBJC_ASSOCIATION_ASSIGN); } extern "C" id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) { diff --git a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp index 4e6c3efc5e1..597779c73be 100644 --- a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp +++ b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp @@ -156,7 +156,7 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "+[NSObject new]", "+[NSString stringWithFormat:]", "+[NSString stringWithUTF8String:]", - "+[NSValue valueWithPointer:]", + "-[NSPlaceholderValue initWithBytes:objCType:]", "-[NSException name]", "-[NSException reason]", "-[NSMethodSignature getArgumentTypeAtIndex:]",