Fix isa swizzling for Kotlin subclasses of Obj-C classes

Don't rely on indexed ivars of instance's isa since isa swizzling
replaces it. Use method instead, it works on the replacement too
(if it is a subclass of the original isa).

 #KT-42482 Fixed.
This commit is contained in:
SvyatoslavScherbina
2020-12-04 15:37:48 +03:00
committed by Stanislav Erokhin
parent e1d9a36de3
commit 62272f54c1
6 changed files with 162 additions and 24 deletions
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMStoreSizeOfType
import llvm.LLVMLinkage
import llvm.LLVMSetLinkage
import llvm.LLVMValueRef
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.ir.*
@@ -65,7 +65,9 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
staticData.placeGlobal(
"kobjcclassptr:${irClass.fqNameForIrSerialization}#internal",
NullPointer(int8Type)
).pointer
).pointer,
generateClassDataImp(irClass)
)
objCLLvmDeclarations.classInfoGlobal.setInitializer(info)
@@ -131,6 +133,26 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
)
}
private fun generateClassDataImp(irClass: IrClass): ConstPointer {
val classDataPointer = staticData.placeGlobal(
"kobjcclassdata:${irClass.fqNameForIrSerialization}#internal",
Zero(runtime.kotlinObjCClassData)
).pointer
val functionType = functionType(classDataPointer.llvmType, false, int8TypePtr, int8TypePtr)
val functionName = "kobjcclassdataimp:${irClass.fqNameForIrSerialization}#internal"
val function = generateFunction(codegen, functionType, functionName) {
ret(classDataPointer.llvm)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage)
}
return constPointer(function)
}
private val codegen = CodeGenerator(context)
companion object {
const val createdClassFieldIndex = 11
}
@@ -44,6 +44,7 @@ class Runtime(bitcodeFile: String) {
val targetData = LLVMCreateTargetData(dataLayout)!!
val kotlinObjCClassData by lazy { getStructType("KotlinObjCClassData") }
val kotlinObjCClassInfo by lazy { getStructType("KotlinObjCClassInfo") }
val objCMethodDescription by lazy { getStructType("ObjCMethodDescription") }
val objCTypeAdapter by lazy { getStructType("ObjCTypeAdapter") }
@@ -0,0 +1,10 @@
#import <Foundation/NSObject.h>
extern BOOL kt42482Deallocated;
extern id kt42482Global;
@interface KT42482 : NSObject
-(int)fortyTwo;
@end;
void kt42482Swizzle(id obj);
@@ -0,0 +1,35 @@
import kotlin.native.ref.WeakReference
import kotlinx.cinterop.*
import kotlin.test.*
import objcTests.*
@Test
fun testKT42482() {
// Attempt to make the state predictable:
kotlin.native.internal.GC.collect()
kt42482Deallocated = false
assertFalse(kt42482Deallocated);
{
assertEquals(41, KT42482().fortyTwo())
val obj: KT42482 = KT42482Impl()
assertEquals(42, obj.fortyTwo())
kt42482Swizzle(obj)
assertEquals(43, obj.fortyTwo())
// Test retain and release on swizzled object:
kt42482Global = obj
kt42482Global = null
}()
kotlin.native.internal.GC.collect()
assertTrue(kt42482Deallocated)
}
class KT42482Impl : KT42482() {
override fun fortyTwo() = 42
}
@@ -0,0 +1,36 @@
#import "kt42482.h"
#import "assert.h"
#import <objc/runtime.h>
BOOL kt42482Deallocated = NO;
id kt42482Global = nil;
@implementation KT42482
-(int)fortyTwo {
return 41;
}
-(void)dealloc {
kt42482Deallocated = YES;
}
@end;
int fortyTwoSwizzledImp(id self, SEL _cmd) {
return 43;
}
void kt42482Swizzle(id obj) {
Class oldClass = object_getClass(obj);
SEL selector = @selector(fortyTwo);
Class newClass = objc_allocateClassPair(oldClass, "KT42482Swizzled", 0);
assert(newClass != nil);
objc_registerClassPair(newClass);
Method method = class_getInstanceMethod([KT42482 class], selector);
assert(method != nil);
class_addMethod(newClass, selector, (IMP)&fortyTwoSwizzledImp, method_getTypeEncoding(method));
object_setClass(obj, newClass);
}