Implement multi-file Obj-C interop tests (interop_objc_tests task)
Extract most of interop_objc_smoke tests
This commit is contained in:
committed by
SvyatoslavScherbina
parent
f8e1409376
commit
cd6840a817
@@ -3445,6 +3445,11 @@ if (PlatformInfo.isAppleTarget(project)) {
|
|||||||
it.headers "$projectDir/interop/objc/smoke.h"
|
it.headers "$projectDir/interop/objc/smoke.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInterop("objcTests") {
|
||||||
|
it.defFile 'interop/objc/objcTests.def'
|
||||||
|
it.headers fileTree("$projectDir/interop/objc/tests") { include '**/*.h' }
|
||||||
|
}
|
||||||
|
|
||||||
createInterop("objcMisc") {
|
createInterop("objcMisc") {
|
||||||
it.defFile 'interop/objc_with_initializer/objc_misc.def'
|
it.defFile 'interop/objc_with_initializer/objc_misc.def'
|
||||||
it.headers "$projectDir/interop/objc_with_initializer/objc_misc.h"
|
it.headers "$projectDir/interop/objc_with_initializer/objc_misc.h"
|
||||||
@@ -3475,7 +3480,7 @@ createInterop("withSpaces") {
|
|||||||
/**
|
/**
|
||||||
* Creates a task for the interop test. Configures runner and adds library and test build tasks.
|
* Creates a task for the interop test. Configures runner and adds library and test build tasks.
|
||||||
*/
|
*/
|
||||||
Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
Task interopTestBase(String name, boolean multiFile, Closure<KonanInteropTest> configureClosure) {
|
||||||
return KotlinNativeTestKt.createTest(project, name, KonanInteropTest) { task ->
|
return KotlinNativeTestKt.createTest(project, name, KonanInteropTest) { task ->
|
||||||
task.configure(configureClosure)
|
task.configure(configureClosure)
|
||||||
if (task.enabled) {
|
if (task.enabled) {
|
||||||
@@ -3487,7 +3492,7 @@ Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
|||||||
libraries {
|
libraries {
|
||||||
artifact lib
|
artifact lib
|
||||||
}
|
}
|
||||||
srcFiles task.getSources()
|
srcFiles multiFile ? fileTree(task.source) { include '**/*.kt' } : task.getSources()
|
||||||
baseDir "$testOutputLocal/$name"
|
baseDir "$testOutputLocal/$name"
|
||||||
linkerOpts "-L$buildDir"
|
linkerOpts "-L$buildDir"
|
||||||
extraOpts task.flags
|
extraOpts task.flags
|
||||||
@@ -3498,6 +3503,13 @@ Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
||||||
|
return interopTestBase(name, false, configureClosure)
|
||||||
|
}
|
||||||
|
|
||||||
|
Task interopTestMultifile(String name, Closure<KonanInteropTest> configureClosure) {
|
||||||
|
return interopTestBase(name, true, configureClosure)
|
||||||
|
}
|
||||||
|
|
||||||
standaloneTest("interop_objc_allocException") {
|
standaloneTest("interop_objc_allocException") {
|
||||||
dependsOnPlatformLibs(it)
|
dependsOnPlatformLibs(it)
|
||||||
@@ -3721,6 +3733,26 @@ if (PlatformInfo.isAppleTarget(project)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interopTestMultifile("interop_objc_tests") {
|
||||||
|
source = "interop/objc/tests/"
|
||||||
|
interop = 'objcTests'
|
||||||
|
flags = ['-tr', '-e', 'main']
|
||||||
|
|
||||||
|
doBeforeBuild {
|
||||||
|
mkdir(buildDir)
|
||||||
|
execKonanClang(project.target) {
|
||||||
|
args fileTree("$projectDir/interop/objc/tests/") { include '**/*.m' }
|
||||||
|
args "-lobjc", '-fobjc-arc'
|
||||||
|
args '-fPIC', '-shared', '-o', "$buildDir/libobjctests.dylib"
|
||||||
|
// Enable ARC optimizations to prevent some objects from leaking in Obj-C code due to exceptions:
|
||||||
|
args '-O2'
|
||||||
|
}
|
||||||
|
if (project.target instanceof KonanTarget.IOS_X64) {
|
||||||
|
UtilsKt.codesign(project, "$buildDir/libobjctests.dylib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interopTest("interop_objc_global_initializer") {
|
interopTest("interop_objc_global_initializer") {
|
||||||
goldValue = "OK\n"
|
goldValue = "OK\n"
|
||||||
source = "interop/objc_with_initializer/objc_test.kt"
|
source = "interop/objc_with_initializer/objc_test.kt"
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
language = Objective-C
|
||||||
|
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h Foundation/NSBundle.h
|
||||||
|
headerFilter = **/interop/objc/tests/**.h \
|
||||||
|
Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
|
||||||
|
objc/objc.h Foundation/NSBundle.h
|
||||||
|
linkerOpts = -lobjctests
|
||||||
@@ -76,24 +76,6 @@ id createObjectWithFactory(id<ObjectFactory> factory) {
|
|||||||
return [factory create];
|
return [factory create];
|
||||||
}
|
}
|
||||||
|
|
||||||
@protocol BlockProvider
|
|
||||||
@required
|
|
||||||
-(int (^)(int)) block;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
int callProvidedBlock(id<BlockProvider> blockProvider, int argument) {
|
|
||||||
return [blockProvider block](argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
@protocol BlockConsumer
|
|
||||||
@required
|
|
||||||
-(int)callBlock:(int (^)(int))block argument:(int)argument;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
int callPlusOneBlock(id<BlockConsumer> blockConsumer, int argument) {
|
|
||||||
return [blockConsumer callBlock:^int(int p) { return p + 1; } argument:argument];
|
|
||||||
}
|
|
||||||
|
|
||||||
@protocol CustomRetainMethods
|
@protocol CustomRetainMethods
|
||||||
@required
|
@required
|
||||||
-(id)returnRetained:(id)obj __attribute__((ns_returns_retained));
|
-(id)returnRetained:(id)obj __attribute__((ns_returns_retained));
|
||||||
@@ -126,86 +108,6 @@ void useCustomRetainMethods(id<CustomRetainMethods> p) {
|
|||||||
[p returnRetainedBlock:retainedBlock]();
|
[p returnRetainedBlock:retainedBlock]();
|
||||||
}
|
}
|
||||||
|
|
||||||
NSObject* createNSObject() {
|
|
||||||
return [NSObject new];
|
|
||||||
}
|
|
||||||
|
|
||||||
@protocol ExceptionThrower
|
|
||||||
-(void)throwException;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface ExceptionThrowerManager : NSObject
|
|
||||||
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface Blocks : NSObject
|
|
||||||
+(BOOL)blockIsNull:(void (^)(void))block;
|
|
||||||
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block;
|
|
||||||
|
|
||||||
@property (class) void (^nullBlock)(void);
|
|
||||||
@property (class) void (^notNullBlock)(void);
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestVarargs : NSObject
|
|
||||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
|
||||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...;
|
|
||||||
@property NSString* formatted;
|
|
||||||
|
|
||||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ...;
|
|
||||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestVarargs (TestVarargsExtension)
|
|
||||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestVarargsSubclass : TestVarargs
|
|
||||||
// Test clashes:
|
|
||||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args;
|
|
||||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestOverrideInit : NSObject
|
|
||||||
-(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER;
|
|
||||||
+(instancetype)createWithValue:(int)value;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface MultipleInheritanceClashBase : NSObject
|
|
||||||
@property (nonnull) MultipleInheritanceClashBase* delegate;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@protocol MultipleInheritanceClash
|
|
||||||
@optional
|
|
||||||
@property (nullable) id<MultipleInheritanceClash> delegate;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface MultipleInheritanceClash1 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface MultipleInheritanceClash2 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
|
||||||
@property MultipleInheritanceClashBase* delegate;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestClashingWithAny1 : NSObject
|
|
||||||
-(NSString*)toString;
|
|
||||||
-(NSString*)toString_;
|
|
||||||
-(int)hashCode;
|
|
||||||
-(BOOL)equals:(id _Nullable)other;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestClashingWithAny2 : NSObject
|
|
||||||
-(void)toString;
|
|
||||||
-(void)hashCode;
|
|
||||||
-(void)equals:(int)p; // May clash.
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestClashingWithAny3 : NSObject
|
|
||||||
// Not clashing actually.
|
|
||||||
-(NSString*)toString:(int)p;
|
|
||||||
-(int)hashCode:(int)p;
|
|
||||||
-(BOOL)equals;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
id getPrinterProtocolRaw() {
|
id getPrinterProtocolRaw() {
|
||||||
return @protocol(Printer);
|
return @protocol(Printer);
|
||||||
}
|
}
|
||||||
@@ -213,76 +115,3 @@ id getPrinterProtocolRaw() {
|
|||||||
Protocol* getPrinterProtocol() {
|
Protocol* getPrinterProtocol() {
|
||||||
return @protocol(Printer);
|
return @protocol(Printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@interface TestInitWithCustomSelector : NSObject
|
|
||||||
-(instancetype)initCustom;
|
|
||||||
@property BOOL custom;
|
|
||||||
|
|
||||||
+(instancetype _Nonnull)createCustom;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestAllocNoRetain : NSObject
|
|
||||||
@property BOOL ok;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface CustomString : NSString
|
|
||||||
- (instancetype)initWithValue:(int)value;
|
|
||||||
@property int value;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
CustomString* _Nonnull createCustomString(int value) {
|
|
||||||
return [[CustomString alloc] initWithValue:value];
|
|
||||||
}
|
|
||||||
|
|
||||||
int getCustomStringValue(CustomString* str) {
|
|
||||||
return str.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern BOOL customStringDeallocated;
|
|
||||||
|
|
||||||
@interface TestConstructorReturnsNull : NSObject
|
|
||||||
- (instancetype)init;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface TestCallableReferences : NSObject
|
|
||||||
@property int value;
|
|
||||||
- (int)instanceMethod;
|
|
||||||
+ (int)classMethod:(int)first :(int)second;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
// [KT-36067] cinterop tool fails when there is a structure member named Companion
|
|
||||||
|
|
||||||
struct EnumFieldMangleStruct {
|
|
||||||
enum {Companion, Any} smth;
|
|
||||||
};
|
|
||||||
struct EnumFieldMangleStruct enumMangledStruct = { Any };
|
|
||||||
|
|
||||||
struct MyStruct {
|
|
||||||
int Companion; // simple clash
|
|
||||||
int _Companion;
|
|
||||||
int $_Companion;
|
|
||||||
int super;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MyStruct myStruct = {11, 12, 13, 14};
|
|
||||||
|
|
||||||
@protocol Proto
|
|
||||||
@property int Companion; // clash on implementing
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface FooMangled : NSObject<Proto>
|
|
||||||
//- (void) CompanionS; // mangleSimple does not support this: it may clash after mangling
|
|
||||||
@end;
|
|
||||||
|
|
||||||
|
|
||||||
@class DeallocListener;
|
|
||||||
|
|
||||||
@interface DeallocExecutor : NSObject
|
|
||||||
@property DeallocListener* deallocListener;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface DeallocListener : NSObject
|
|
||||||
@property (weak) DeallocExecutor* deallocExecutor;
|
|
||||||
@property BOOL deallocated;
|
|
||||||
-(BOOL)deallocExecutorIsNil;
|
|
||||||
@end;
|
|
||||||
|
|||||||
@@ -16,27 +16,11 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
testConversions()
|
// TODO: migrate remaining tests to interop/objc/tests/
|
||||||
testTypeOps()
|
testTypeOps()
|
||||||
testWeakRefs()
|
|
||||||
testExceptions()
|
|
||||||
testBlocks()
|
|
||||||
testCustomRetain()
|
testCustomRetain()
|
||||||
testVarargs()
|
|
||||||
testOverrideInit()
|
|
||||||
testMultipleInheritanceClash()
|
|
||||||
testClashingWithAny()
|
|
||||||
testInitWithCustomSelector()
|
|
||||||
testAllocNoRetain()
|
|
||||||
testNSOutputStreamToMemoryConstructor()
|
|
||||||
testExportObjCClass()
|
testExportObjCClass()
|
||||||
testCustomString()
|
|
||||||
testLocalizedStrings()
|
testLocalizedStrings()
|
||||||
testConstructorReturnsNull()
|
|
||||||
testCallableReferences()
|
|
||||||
testMangling()
|
|
||||||
testSharing()
|
|
||||||
testObjCWeakRef()
|
|
||||||
|
|
||||||
assertEquals(2, ForwardDeclaredEnum.TWO.value)
|
assertEquals(2, ForwardDeclaredEnum.TWO.value)
|
||||||
|
|
||||||
@@ -197,80 +181,6 @@ fun testTypeOps() {
|
|||||||
assertFails { MutablePairImpl(1, 2).asAny() as Foo }
|
assertFails { MutablePairImpl(1, 2).asAny() as Foo }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testConversions() {
|
|
||||||
testMethodsOfAny(emptyList<Nothing>(), NSArray())
|
|
||||||
testMethodsOfAny(listOf(1, "foo"), nsArrayOf(1, "foo"))
|
|
||||||
testMethodsOfAny(42, NSNumber.numberWithInt(42), 17)
|
|
||||||
testMethodsOfAny(true, NSNumber.numberWithBool(true), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testMethodsOfAny(kotlinObject: Any, equalNsObject: NSObject, otherObject: Any = Any()) {
|
|
||||||
assertEquals(kotlinObject.hashCode(), equalNsObject.hashCode())
|
|
||||||
assertEquals(kotlinObject.toString(), equalNsObject.toString())
|
|
||||||
assertEquals(kotlinObject, equalNsObject)
|
|
||||||
assertEquals(equalNsObject, kotlinObject)
|
|
||||||
assertNotEquals(equalNsObject, otherObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testWeakRefs() {
|
|
||||||
testWeakReference({ createNSObject()!! })
|
|
||||||
|
|
||||||
createAndAbandonWeakRef(NSObject())
|
|
||||||
|
|
||||||
testWeakReference({ NSArray.arrayWithArray(listOf(42)) as NSArray })
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testWeakReference(block: () -> NSObject) {
|
|
||||||
val ref = autoreleasepool {
|
|
||||||
createAndTestWeakReference(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin.native.internal.GC.collect()
|
|
||||||
|
|
||||||
assertNull(ref.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createAndTestWeakReference(block: () -> NSObject): WeakReference<NSObject> {
|
|
||||||
val ref = createWeakReference(block)
|
|
||||||
assertNotNull(ref.get())
|
|
||||||
assertEquals(ref.get()!!.hash(), ref.get()!!.hash())
|
|
||||||
return ref
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createWeakReference(block: () -> NSObject) = WeakReference(block())
|
|
||||||
|
|
||||||
fun createAndAbandonWeakRef(obj: NSObject) {
|
|
||||||
WeakReference(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testExceptions() {
|
|
||||||
assertFailsWith<MyException> {
|
|
||||||
ExceptionThrowerManager.throwExceptionWith(object : NSObject(), ExceptionThrowerProtocol {
|
|
||||||
override fun throwException() {
|
|
||||||
throw MyException()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testBlocks() {
|
|
||||||
assertTrue(Blocks.blockIsNull(null))
|
|
||||||
assertFalse(Blocks.blockIsNull({}))
|
|
||||||
|
|
||||||
assertEquals(null, Blocks.nullBlock)
|
|
||||||
assertNotEquals(null, Blocks.notNullBlock)
|
|
||||||
|
|
||||||
assertEquals(10, Blocks.same({ a, b, c, d -> a + b + c + d })!!(1, 2, 3, 4))
|
|
||||||
|
|
||||||
assertEquals(222, callProvidedBlock(object : NSObject(), BlockProviderProtocol {
|
|
||||||
override fun block(): (Int) -> Int = { it * 2 }
|
|
||||||
}, 111))
|
|
||||||
|
|
||||||
assertEquals(322, callPlusOneBlock(object : NSObject(), BlockConsumerProtocol {
|
|
||||||
override fun callBlock(block: ((Int) -> Int)?, argument: Int) = block!!(argument)
|
|
||||||
}, 321))
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var retainedMustNotBeDeallocated: MustNotBeDeallocated
|
private lateinit var retainedMustNotBeDeallocated: MustNotBeDeallocated
|
||||||
|
|
||||||
fun testCustomRetain() {
|
fun testCustomRetain() {
|
||||||
@@ -299,156 +209,6 @@ fun testCustomRetain() {
|
|||||||
assertFalse(unexpectedDeallocation)
|
assertFalse(unexpectedDeallocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testVarargs() {
|
|
||||||
assertEquals(
|
|
||||||
"a b -1",
|
|
||||||
TestVarargs.testVarargsWithFormat(
|
|
||||||
"%@ %s %d",
|
|
||||||
"a" as NSString, "b".cstr, (-1).toByte()
|
|
||||||
).formatted
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"2 3 9223372036854775807",
|
|
||||||
TestVarargs(
|
|
||||||
"%d %d %lld",
|
|
||||||
2.toShort(), 3, Long.MAX_VALUE
|
|
||||||
).formatted
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"0.1 0.2 1 0",
|
|
||||||
TestVarargs.create(
|
|
||||||
"%.1f %.1lf %d %d",
|
|
||||||
0.1.toFloat(), 0.2, true, false
|
|
||||||
).formatted
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"1 2 3",
|
|
||||||
TestVarargs(
|
|
||||||
format = "%d %d %d",
|
|
||||||
args = *arrayOf(1, 2, 3)
|
|
||||||
).formatted
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"4 5 6",
|
|
||||||
TestVarargs(
|
|
||||||
args = *arrayOf(4, *arrayOf(5, 6)),
|
|
||||||
format = "%d %d %d"
|
|
||||||
).formatted
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"7",
|
|
||||||
TestVarargsSubclass.stringWithFormat(
|
|
||||||
"%d",
|
|
||||||
7
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testOverrideInit() {
|
|
||||||
assertEquals(42, (TestOverrideInitImpl.createWithValue(42) as TestOverrideInitImpl).value)
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestOverrideInitImpl @OverrideInit constructor(val value: Int) : TestOverrideInit(value) {
|
|
||||||
companion object : TestOverrideInitMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MyException : Throwable()
|
|
||||||
|
|
||||||
fun testMultipleInheritanceClash() {
|
|
||||||
val clash1 = MultipleInheritanceClash1()
|
|
||||||
val clash2 = MultipleInheritanceClash2()
|
|
||||||
|
|
||||||
clash1.delegate = clash1
|
|
||||||
assertEquals(clash1, clash1.delegate)
|
|
||||||
clash1.setDelegate(clash2)
|
|
||||||
assertEquals(clash2, clash1.delegate())
|
|
||||||
|
|
||||||
clash2.delegate = clash1
|
|
||||||
assertEquals(clash1, clash2.delegate)
|
|
||||||
clash2.setDelegate(clash2)
|
|
||||||
assertEquals(clash2, clash2.delegate())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testClashingWithAny() {
|
|
||||||
assertEquals("description", TestClashingWithAny1().toString())
|
|
||||||
assertEquals("toString", TestClashingWithAny1().toString_())
|
|
||||||
assertEquals("toString_", TestClashingWithAny1().toString__())
|
|
||||||
assertEquals(1, TestClashingWithAny1().hashCode())
|
|
||||||
assertEquals(31, TestClashingWithAny1().hashCode_())
|
|
||||||
assertFalse(TestClashingWithAny1().equals(TestClashingWithAny1()))
|
|
||||||
assertTrue(TestClashingWithAny1().equals_(TestClashingWithAny1()))
|
|
||||||
|
|
||||||
assertEquals("description", TestClashingWithAny2().toString())
|
|
||||||
assertEquals(Unit, TestClashingWithAny2().toString_())
|
|
||||||
assertEquals(2, TestClashingWithAny2().hashCode())
|
|
||||||
assertEquals(Unit, TestClashingWithAny2().hashCode_())
|
|
||||||
assertFalse(TestClashingWithAny2().equals(TestClashingWithAny2()))
|
|
||||||
assertEquals(Unit, TestClashingWithAny2().equals_(42))
|
|
||||||
|
|
||||||
assertEquals("description", TestClashingWithAny3().toString())
|
|
||||||
assertEquals("toString:11", TestClashingWithAny3().toString(11))
|
|
||||||
assertEquals(3, TestClashingWithAny3().hashCode())
|
|
||||||
assertEquals(4, TestClashingWithAny3().hashCode(3))
|
|
||||||
assertFalse(TestClashingWithAny3().equals(TestClashingWithAny3()))
|
|
||||||
assertTrue(TestClashingWithAny3().equals())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testInitWithCustomSelector() {
|
|
||||||
assertFalse(TestInitWithCustomSelector().custom)
|
|
||||||
assertTrue(TestInitWithCustomSelector(custom = Unit).custom)
|
|
||||||
|
|
||||||
val customSubclass: TestInitWithCustomSelector = TestInitWithCustomSelectorSubclass.createCustom()
|
|
||||||
assertTrue(customSubclass is TestInitWithCustomSelectorSubclass)
|
|
||||||
assertTrue(customSubclass.custom)
|
|
||||||
|
|
||||||
// Test side effect:
|
|
||||||
var ok = false
|
|
||||||
assertTrue(TestInitWithCustomSelector(run { ok = true }).custom)
|
|
||||||
assertTrue(ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestInitWithCustomSelectorSubclass : TestInitWithCustomSelector {
|
|
||||||
@OverrideInit constructor(custom: Unit) : super(custom) {
|
|
||||||
assertSame(Unit, custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : TestInitWithCustomSelectorMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testAllocNoRetain() {
|
|
||||||
// Ensure that calling Kotlin constructor generated for Objective-C initializer doesn't result in
|
|
||||||
// redundant retain-release sequence for `alloc` result, since it may provoke specific bugs to reproduce, e.g.
|
|
||||||
// the one found in [[NSOutputStream alloc] initToMemory] sequence where initToMemory deallocates its receiver
|
|
||||||
// forcibly when replacing it with other object: (to be compiled with ARC enabled)
|
|
||||||
/*
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
void* mem;
|
|
||||||
NSOutputStream* allocated = nil;
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
allocated = [NSOutputStream alloc];
|
|
||||||
NSOutputStream* initialized = [allocated initToMemory];
|
|
||||||
mem = calloc(1, 0x10); // To corrupt the 'allocated' object header.
|
|
||||||
allocated = nil; // Crashes here in objc_release.
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
assertTrue(TestAllocNoRetain().ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testNSOutputStreamToMemoryConstructor() {
|
|
||||||
val stream: Any = NSOutputStream(toMemory = Unit)
|
|
||||||
assertTrue(stream is NSOutputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val TestExportObjCClass1Name = "TestExportObjCClass"
|
private const val TestExportObjCClass1Name = "TestExportObjCClass"
|
||||||
@ExportObjCClass(TestExportObjCClass1Name) class TestExportObjCClass1 : NSObject()
|
@ExportObjCClass(TestExportObjCClass1Name) class TestExportObjCClass1 : NSObject()
|
||||||
|
|
||||||
@@ -466,21 +226,6 @@ fun testExportObjCClass() {
|
|||||||
xor (TestExportObjCClass4().objCClassName == TestExportObjCClass34Name))
|
xor (TestExportObjCClass4().objCClassName == TestExportObjCClass34Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testCustomString() {
|
|
||||||
assertFalse(customStringDeallocated)
|
|
||||||
|
|
||||||
fun test() = autoreleasepool {
|
|
||||||
val str: String = createCustomString(321)
|
|
||||||
assertEquals("321", str)
|
|
||||||
assertEquals("CustomString", str.objCClassName)
|
|
||||||
assertEquals(321, getCustomStringValue(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
test()
|
|
||||||
kotlin.native.internal.GC.collect()
|
|
||||||
assertTrue(customStringDeallocated)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testLocalizedStrings() {
|
fun testLocalizedStrings() {
|
||||||
val key = "screen_main_plural_string"
|
val key = "screen_main_plural_string"
|
||||||
val localizedString = NSBundle.mainBundle.localizedStringForKey(key, value = "", table = "Localizable")
|
val localizedString = NSBundle.mainBundle.localizedStringForKey(key, value = "", table = "Localizable")
|
||||||
@@ -488,140 +233,7 @@ fun testLocalizedStrings() {
|
|||||||
assertEquals("Plural: 5 apples", string)
|
assertEquals("Plural: 5 apples", string)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testConstructorReturnsNull() {
|
|
||||||
assertFailsWith<NullPointerException>() {
|
|
||||||
TestConstructorReturnsNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testCallableReferences() {
|
|
||||||
val createTestCallableReferences = ::TestCallableReferences
|
|
||||||
assertEquals("<init>", createTestCallableReferences.name)
|
|
||||||
val testCallableReferences: Any = createTestCallableReferences()
|
|
||||||
assertTrue(testCallableReferences is TestCallableReferences)
|
|
||||||
|
|
||||||
val valueRef: kotlin.reflect.KMutableProperty0<Int> = testCallableReferences::value
|
|
||||||
assertEquals("value", valueRef.name)
|
|
||||||
assertEquals(0, valueRef())
|
|
||||||
valueRef.set(42)
|
|
||||||
assertEquals(42, valueRef())
|
|
||||||
|
|
||||||
val classMethodRef = (TestCallableReferences)::classMethod
|
|
||||||
assertEquals("classMethod", classMethodRef.name)
|
|
||||||
assertEquals(3, classMethodRef(1, 2))
|
|
||||||
|
|
||||||
val instanceMethodRef = TestCallableReferences::instanceMethod
|
|
||||||
assertEquals("instanceMethod", instanceMethodRef.name)
|
|
||||||
assertEquals(42, instanceMethodRef(testCallableReferences))
|
|
||||||
|
|
||||||
val boundInstanceMethodRef = testCallableReferences::instanceMethod
|
|
||||||
assertEquals("instanceMethod", boundInstanceMethodRef.name)
|
|
||||||
assertEquals(42, boundInstanceMethodRef())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testMangling() {
|
|
||||||
assertEquals(11, myStruct.`Companion$`)
|
|
||||||
assertEquals(12, myStruct._Companion)
|
|
||||||
assertEquals(13, myStruct.`$_Companion`)
|
|
||||||
assertEquals(14, myStruct.`super`)
|
|
||||||
|
|
||||||
val objc = FooMangled()
|
|
||||||
objc.`Companion$` = 99
|
|
||||||
assertEquals(99, objc.Companion())
|
|
||||||
assertEquals(99, objc.`Companion$`)
|
|
||||||
|
|
||||||
enumMangledStruct.smth = Companion
|
|
||||||
assertEquals(Companion, enumMangledStruct.smth)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NSObjectImpl : NSObject() {
|
|
||||||
var x = 111
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testSharing() = withWorker {
|
|
||||||
val obj = NSObjectImpl()
|
|
||||||
val array = nsArrayOf(obj)
|
|
||||||
|
|
||||||
assertFalse(obj.isFrozen)
|
|
||||||
|
|
||||||
runInWorker {
|
|
||||||
assertFailsWith<IncorrectDereferenceException> {
|
|
||||||
array.objectAtIndex(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.x = 222
|
|
||||||
obj.freeze()
|
|
||||||
assertTrue(obj.isFrozen)
|
|
||||||
|
|
||||||
runInWorker {
|
|
||||||
val obj1 = array.objectAtIndex(0) as NSObjectImpl
|
|
||||||
assertFailsWith<InvalidMutabilityException> {
|
|
||||||
obj1.x = 333
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(222, obj.x)
|
|
||||||
|
|
||||||
// TODO: test [obj release] etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testObjCWeakRef() {
|
|
||||||
val deallocListener = DeallocListener()
|
|
||||||
assertFalse(deallocListener.deallocated)
|
|
||||||
|
|
||||||
testObjCWeakRef0(deallocListener)
|
|
||||||
|
|
||||||
kotlin.native.internal.GC.collect()
|
|
||||||
assertTrue(deallocListener.deallocated)
|
|
||||||
assertTrue(deallocListener.deallocExecutorIsNil())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testObjCWeakRef0(deallocListener: DeallocListener) = withWorker {
|
|
||||||
assertTrue(deallocListener.deallocExecutorIsNil())
|
|
||||||
|
|
||||||
val obj = object : DeallocExecutor() {}
|
|
||||||
deallocListener.deallocExecutor = obj
|
|
||||||
obj.deallocListener = deallocListener
|
|
||||||
|
|
||||||
assertFalse(deallocListener.deallocExecutorIsNil())
|
|
||||||
|
|
||||||
// TODO: can't actually test, Obj-C runtime doesn't expect _tryRetain throwing an exception.
|
|
||||||
// runInWorker {
|
|
||||||
// assertFailsWith<IncorrectDereferenceException> {
|
|
||||||
// deallocListener.deallocExecutorIsNil()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
obj.freeze()
|
|
||||||
|
|
||||||
runInWorker {
|
|
||||||
// [deallocListener.deallocExecutorIsNil()] calls deallocExecutor getter, which retains [obj] and either
|
|
||||||
// puts it to autoreleasepool or releases it immediately (Obj-C ARC optimization).
|
|
||||||
// Wrap the call to autoreleasepool to ensure [obj] will be released:
|
|
||||||
autoreleasepool {
|
|
||||||
assertFalse(deallocListener.deallocExecutorIsNil())
|
|
||||||
}
|
|
||||||
// Process release of Kotlin reference to [obj] in any case:
|
|
||||||
kotlin.native.internal.GC.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Worker.runInWorker(block: () -> Unit) {
|
|
||||||
block.freeze()
|
|
||||||
val future = this.execute(TransferMode.SAFE, { block }) {
|
|
||||||
it()
|
|
||||||
}
|
|
||||||
future.result // Throws on failure.
|
|
||||||
}
|
|
||||||
|
|
||||||
private val Any.objCClassName: String
|
private val Any.objCClassName: String
|
||||||
get() = object_getClassName(this)!!.toKString()
|
get() = object_getClassName(this)!!.toKString()
|
||||||
|
|
||||||
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
|
|
||||||
elements.forEach {
|
|
||||||
this.addObject(it as ObjCObject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Any?.asAny(): Any? = this
|
fun Any?.asAny(): Any? = this
|
||||||
|
|||||||
@@ -95,263 +95,3 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
@implementation ExceptionThrowerManager
|
|
||||||
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower {
|
|
||||||
[thrower throwException];
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation Blocks
|
|
||||||
+(BOOL)blockIsNull:(void (^)(void))block {
|
|
||||||
return block == nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(void (^)(void)) nullBlock {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(void (^)(void)) notNullBlock {
|
|
||||||
return ^{};
|
|
||||||
}
|
|
||||||
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestVarargs
|
|
||||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ... {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
self.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... {
|
|
||||||
TestVarargs* result = [[self alloc] init];
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
result.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ... {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestVarargsSubclass
|
|
||||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestOverrideInit
|
|
||||||
-(instancetype)initWithValue:(int)value {
|
|
||||||
return self = [super init];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(instancetype)createWithValue:(int)value {
|
|
||||||
return [[self alloc] initWithValue:value];
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation MultipleInheritanceClashBase
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation MultipleInheritanceClash1
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation MultipleInheritanceClash2
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestClashingWithAny1
|
|
||||||
-(NSString*)description {
|
|
||||||
return @"description";
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)toString {
|
|
||||||
return @"toString";
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)toString_ {
|
|
||||||
return @"toString_";
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSUInteger)hash {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)hashCode {
|
|
||||||
return 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL)equals:(id _Nullable)other {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestClashingWithAny2
|
|
||||||
-(NSString*)description {
|
|
||||||
return @"description";
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)toString {
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSUInteger)hash {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)hashCode {
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)equals:(int)p {
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestClashingWithAny3
|
|
||||||
-(NSString*)description {
|
|
||||||
return @"description";
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)toString:(int)p {
|
|
||||||
return [NSString stringWithFormat:@"%s:%d", "toString", p];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSUInteger)hash {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)hashCode:(int)p {
|
|
||||||
return p + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL)equals {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestInitWithCustomSelector
|
|
||||||
|
|
||||||
-(instancetype)initCustom {
|
|
||||||
if (self = [super init]) {
|
|
||||||
self.custom = YES;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(instancetype)init {
|
|
||||||
if (self = [super init]) {
|
|
||||||
self.custom = NO;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(instancetype)createCustom {
|
|
||||||
return [[self alloc] initCustom];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestAllocNoRetain
|
|
||||||
-(instancetype)init {
|
|
||||||
__weak id weakSelf = self;
|
|
||||||
self = [TestAllocNoRetain alloc];
|
|
||||||
if (self = [super init]) {
|
|
||||||
// Ensure that original self value was deallocated:
|
|
||||||
self.ok = (weakSelf == nil);
|
|
||||||
// So it's RC was 1, which means there wasn't redundant retain applied to it.
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
BOOL customStringDeallocated = NO;
|
|
||||||
|
|
||||||
@implementation CustomString {
|
|
||||||
NSString* delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithValue:(int)value {
|
|
||||||
if (self = [super init]) {
|
|
||||||
self->delegate = @(value).description;
|
|
||||||
self.value = value;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unichar)characterAtIndex:(NSUInteger)index {
|
|
||||||
return [self->delegate characterAtIndex:index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUInteger)length {
|
|
||||||
return self->delegate.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)copyWithZone:(NSZone *)zone {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
customStringDeallocated = YES;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestConstructorReturnsNull
|
|
||||||
- (instancetype)init {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation TestCallableReferences
|
|
||||||
- (int)instanceMethod {
|
|
||||||
return self.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (int)classMethod:(int)first :(int)second {
|
|
||||||
return first + second;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
|
|
||||||
// [KT-36067] mangling
|
|
||||||
@implementation FooMangled : NSObject
|
|
||||||
@synthesize Companion;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
|
|
||||||
@implementation DeallocExecutor
|
|
||||||
-(void)dealloc {
|
|
||||||
self.deallocListener.deallocated = YES;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@implementation DeallocListener
|
|
||||||
-(BOOL)deallocExecutorIsNil {
|
|
||||||
return self.deallocExecutor == nil;
|
|
||||||
}
|
|
||||||
@end;
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface TestAllocNoRetain : NSObject
|
||||||
|
@property BOOL ok;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testAllocNoRetain() {
|
||||||
|
// Ensure that calling Kotlin constructor generated for Objective-C initializer doesn't result in
|
||||||
|
// redundant retain-release sequence for `alloc` result, since it may provoke specific bugs to reproduce, e.g.
|
||||||
|
// the one found in [[NSOutputStream alloc] initToMemory] sequence where initToMemory deallocates its receiver
|
||||||
|
// forcibly when replacing it with other object: (to be compiled with ARC enabled)
|
||||||
|
/*
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
void* mem;
|
||||||
|
NSOutputStream* allocated = nil;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
allocated = [NSOutputStream alloc];
|
||||||
|
NSOutputStream* initialized = [allocated initToMemory];
|
||||||
|
mem = calloc(1, 0x10); // To corrupt the 'allocated' object header.
|
||||||
|
allocated = nil; // Crashes here in objc_release.
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
assertTrue(TestAllocNoRetain().ok)
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#import "allocNoRetain.h"
|
||||||
|
|
||||||
|
@implementation TestAllocNoRetain
|
||||||
|
-(instancetype)init {
|
||||||
|
__weak id weakSelf = self;
|
||||||
|
self = [TestAllocNoRetain alloc];
|
||||||
|
if (self = [super init]) {
|
||||||
|
// Ensure that original self value was deallocated:
|
||||||
|
self.ok = (weakSelf == nil);
|
||||||
|
// So it's RC was 1, which means there wasn't redundant retain applied to it.
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@protocol BlockProvider
|
||||||
|
@required
|
||||||
|
-(int (^)(int)) block;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
int callProvidedBlock(id<BlockProvider> blockProvider, int argument) {
|
||||||
|
return [blockProvider block](argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
@protocol BlockConsumer
|
||||||
|
@required
|
||||||
|
-(int)callBlock:(int (^)(int))block argument:(int)argument;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
int callPlusOneBlock(id<BlockConsumer> blockConsumer, int argument) {
|
||||||
|
return [blockConsumer callBlock:^int(int p) { return p + 1; } argument:argument];
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface Blocks : NSObject
|
||||||
|
+(BOOL)blockIsNull:(void (^)(void))block;
|
||||||
|
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block;
|
||||||
|
|
||||||
|
@property (class) void (^nullBlock)(void);
|
||||||
|
@property (class) void (^notNullBlock)(void);
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testBlocks() {
|
||||||
|
assertTrue(Blocks.blockIsNull(null))
|
||||||
|
assertFalse(Blocks.blockIsNull({}))
|
||||||
|
|
||||||
|
assertEquals(null, Blocks.nullBlock)
|
||||||
|
assertNotEquals(null, Blocks.notNullBlock)
|
||||||
|
|
||||||
|
assertEquals(10, Blocks.same({ a, b, c, d -> a + b + c + d })!!(1, 2, 3, 4))
|
||||||
|
|
||||||
|
assertEquals(222, callProvidedBlock(object : NSObject(), BlockProviderProtocol {
|
||||||
|
override fun block(): (Int) -> Int = { it * 2 }
|
||||||
|
}, 111))
|
||||||
|
|
||||||
|
assertEquals(322, callPlusOneBlock(object : NSObject(), BlockConsumerProtocol {
|
||||||
|
override fun callBlock(block: ((Int) -> Int)?, argument: Int) = block!!(argument)
|
||||||
|
}, 321))
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#import "blocks.h"
|
||||||
|
|
||||||
|
@implementation Blocks
|
||||||
|
+(BOOL)blockIsNull:(void (^)(void))block {
|
||||||
|
return block == nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(void (^)(void)) nullBlock {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(void (^)(void)) notNullBlock {
|
||||||
|
return ^{};
|
||||||
|
}
|
||||||
|
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface TestCallableReferences : NSObject
|
||||||
|
@property int value;
|
||||||
|
- (int)instanceMethod;
|
||||||
|
+ (int)classMethod:(int)first :(int)second;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testCallableReferences() {
|
||||||
|
val createTestCallableReferences = ::TestCallableReferences
|
||||||
|
assertEquals("<init>", createTestCallableReferences.name)
|
||||||
|
val testCallableReferences: Any = createTestCallableReferences()
|
||||||
|
assertTrue(testCallableReferences is TestCallableReferences)
|
||||||
|
|
||||||
|
val valueRef: kotlin.reflect.KMutableProperty0<Int> = testCallableReferences::value
|
||||||
|
assertEquals("value", valueRef.name)
|
||||||
|
assertEquals(0, valueRef())
|
||||||
|
valueRef.set(42)
|
||||||
|
assertEquals(42, valueRef())
|
||||||
|
|
||||||
|
val classMethodRef = (TestCallableReferences)::classMethod
|
||||||
|
assertEquals("classMethod", classMethodRef.name)
|
||||||
|
assertEquals(3, classMethodRef(1, 2))
|
||||||
|
|
||||||
|
val instanceMethodRef = TestCallableReferences::instanceMethod
|
||||||
|
assertEquals("instanceMethod", instanceMethodRef.name)
|
||||||
|
assertEquals(42, instanceMethodRef(testCallableReferences))
|
||||||
|
|
||||||
|
val boundInstanceMethodRef = testCallableReferences::instanceMethod
|
||||||
|
assertEquals("instanceMethod", boundInstanceMethodRef.name)
|
||||||
|
assertEquals(42, boundInstanceMethodRef())
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#import "callableReferences.h"
|
||||||
|
|
||||||
|
@implementation TestCallableReferences
|
||||||
|
- (int)instanceMethod {
|
||||||
|
return self.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (int)classMethod:(int)first :(int)second {
|
||||||
|
return first + second;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
|
||||||
|
@interface TestClashingWithAny1 : NSObject
|
||||||
|
-(NSString*)toString;
|
||||||
|
-(NSString*)toString_;
|
||||||
|
-(int)hashCode;
|
||||||
|
-(BOOL)equals:(id _Nullable)other;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface TestClashingWithAny2 : NSObject
|
||||||
|
-(void)toString;
|
||||||
|
-(void)hashCode;
|
||||||
|
-(void)equals:(int)p; // May clash.
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface TestClashingWithAny3 : NSObject
|
||||||
|
// Not clashing actually.
|
||||||
|
-(NSString*)toString:(int)p;
|
||||||
|
-(int)hashCode:(int)p;
|
||||||
|
-(BOOL)equals;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testClashingWithAny() {
|
||||||
|
assertEquals("description", TestClashingWithAny1().toString())
|
||||||
|
assertEquals("toString", TestClashingWithAny1().toString_())
|
||||||
|
assertEquals("toString_", TestClashingWithAny1().toString__())
|
||||||
|
assertEquals(1, TestClashingWithAny1().hashCode())
|
||||||
|
assertEquals(31, TestClashingWithAny1().hashCode_())
|
||||||
|
assertFalse(TestClashingWithAny1().equals(TestClashingWithAny1()))
|
||||||
|
assertTrue(TestClashingWithAny1().equals_(TestClashingWithAny1()))
|
||||||
|
|
||||||
|
assertEquals("description", TestClashingWithAny2().toString())
|
||||||
|
assertEquals(Unit, TestClashingWithAny2().toString_())
|
||||||
|
assertEquals(2, TestClashingWithAny2().hashCode())
|
||||||
|
assertEquals(Unit, TestClashingWithAny2().hashCode_())
|
||||||
|
assertFalse(TestClashingWithAny2().equals(TestClashingWithAny2()))
|
||||||
|
assertEquals(Unit, TestClashingWithAny2().equals_(42))
|
||||||
|
|
||||||
|
assertEquals("description", TestClashingWithAny3().toString())
|
||||||
|
assertEquals("toString:11", TestClashingWithAny3().toString(11))
|
||||||
|
assertEquals(3, TestClashingWithAny3().hashCode())
|
||||||
|
assertEquals(4, TestClashingWithAny3().hashCode(3))
|
||||||
|
assertFalse(TestClashingWithAny3().equals(TestClashingWithAny3()))
|
||||||
|
assertTrue(TestClashingWithAny3().equals())
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#import "clashingWithAny.h"
|
||||||
|
|
||||||
|
@implementation TestClashingWithAny1
|
||||||
|
-(NSString*)description {
|
||||||
|
return @"description";
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString*)toString {
|
||||||
|
return @"toString";
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString*)toString_ {
|
||||||
|
return @"toString_";
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSUInteger)hash {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int)hashCode {
|
||||||
|
return 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)equals:(id _Nullable)other {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation TestClashingWithAny2
|
||||||
|
-(NSString*)description {
|
||||||
|
return @"description";
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)toString {
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSUInteger)hash {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)hashCode {
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)equals:(int)p {
|
||||||
|
}
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation TestClashingWithAny3
|
||||||
|
-(NSString*)description {
|
||||||
|
return @"description";
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString*)toString:(int)p {
|
||||||
|
return [NSString stringWithFormat:@"%s:%d", "toString", p];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSUInteger)hash {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int)hashCode:(int)p {
|
||||||
|
return p + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)equals {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface TestConstructorReturnsNull : NSObject
|
||||||
|
- (instancetype)init;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testConstructorReturnsNull() {
|
||||||
|
assertFailsWith<NullPointerException>() {
|
||||||
|
TestConstructorReturnsNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#import "constructorReturnsNull.h"
|
||||||
|
|
||||||
|
@implementation TestConstructorReturnsNull
|
||||||
|
- (instancetype)init {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testConversions() {
|
||||||
|
testMethodsOfAny(emptyList<Nothing>(), NSArray())
|
||||||
|
testMethodsOfAny(listOf(1, "foo"), nsArrayOf(1, "foo"))
|
||||||
|
testMethodsOfAny(42, NSNumber.numberWithInt(42), 17)
|
||||||
|
testMethodsOfAny(true, NSNumber.numberWithBool(true), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testMethodsOfAny(kotlinObject: Any, equalNsObject: NSObject, otherObject: Any = Any()) {
|
||||||
|
assertEquals(kotlinObject.hashCode(), equalNsObject.hashCode())
|
||||||
|
assertEquals(kotlinObject.toString(), equalNsObject.toString())
|
||||||
|
assertEquals(kotlinObject, equalNsObject)
|
||||||
|
assertEquals(equalNsObject, kotlinObject)
|
||||||
|
assertNotEquals(equalNsObject, otherObject)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#import <Foundation/NSString.h>
|
||||||
|
|
||||||
|
@interface CustomString : NSString
|
||||||
|
- (instancetype)initWithValue:(int)value;
|
||||||
|
@property int value;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
CustomString* _Nonnull createCustomString(int value) {
|
||||||
|
return [[CustomString alloc] initWithValue:value];
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCustomStringValue(CustomString* str) {
|
||||||
|
return str.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern BOOL customStringDeallocated;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testCustomString() {
|
||||||
|
assertFalse(customStringDeallocated)
|
||||||
|
|
||||||
|
fun test() = autoreleasepool {
|
||||||
|
val str: String = createCustomString(321)
|
||||||
|
assertEquals("321", str)
|
||||||
|
assertEquals("CustomString", str.objCClassName)
|
||||||
|
assertEquals(321, getCustomStringValue(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
test()
|
||||||
|
kotlin.native.internal.GC.collect()
|
||||||
|
assertTrue(customStringDeallocated)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Any.objCClassName: String
|
||||||
|
get() = object_getClassName(this)!!.toKString()
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#import "customString.h"
|
||||||
|
|
||||||
|
BOOL customStringDeallocated = NO;
|
||||||
|
|
||||||
|
@implementation CustomString {
|
||||||
|
NSString* delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithValue:(int)value {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self->delegate = @(value).description;
|
||||||
|
self.value = value;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (unichar)characterAtIndex:(NSUInteger)index {
|
||||||
|
return [self->delegate characterAtIndex:index];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)length {
|
||||||
|
return self->delegate.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)copyWithZone:(NSZone *)zone {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
customStringDeallocated = YES;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@protocol ExceptionThrower
|
||||||
|
-(void)throwException;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface ExceptionThrowerManager : NSObject
|
||||||
|
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testExceptions() {
|
||||||
|
assertFailsWith<MyException> {
|
||||||
|
ExceptionThrowerManager.throwExceptionWith(object : NSObject(), ExceptionThrowerProtocol {
|
||||||
|
override fun throwException() {
|
||||||
|
throw MyException()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyException : Throwable()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#import "exceptions.h"
|
||||||
|
|
||||||
|
@implementation ExceptionThrowerManager
|
||||||
|
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower {
|
||||||
|
[thrower throwException];
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface TestInitWithCustomSelector : NSObject
|
||||||
|
-(instancetype)initCustom;
|
||||||
|
@property BOOL custom;
|
||||||
|
|
||||||
|
+(instancetype _Nonnull)createCustom;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testInitWithCustomSelector() {
|
||||||
|
assertFalse(TestInitWithCustomSelector().custom)
|
||||||
|
assertTrue(TestInitWithCustomSelector(custom = Unit).custom)
|
||||||
|
|
||||||
|
val customSubclass: TestInitWithCustomSelector = TestInitWithCustomSelectorSubclass.createCustom()
|
||||||
|
assertTrue(customSubclass is TestInitWithCustomSelectorSubclass)
|
||||||
|
assertTrue(customSubclass.custom)
|
||||||
|
|
||||||
|
// Test side effect:
|
||||||
|
var ok = false
|
||||||
|
assertTrue(TestInitWithCustomSelector(run { ok = true }).custom)
|
||||||
|
assertTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestInitWithCustomSelectorSubclass : TestInitWithCustomSelector {
|
||||||
|
@OverrideInit constructor(custom: Unit) : super(custom) {
|
||||||
|
assertSame(Unit, custom)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : TestInitWithCustomSelectorMeta()
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#import "initWithCustomSelector.h"
|
||||||
|
|
||||||
|
@implementation TestInitWithCustomSelector
|
||||||
|
|
||||||
|
-(instancetype)initCustom {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.custom = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(instancetype)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.custom = NO;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(instancetype)createCustom {
|
||||||
|
return [[self alloc] initCustom];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import kotlin.system.exitProcess
|
||||||
|
import kotlinx.cinterop.autoreleasepool
|
||||||
|
import kotlin.native.internal.test.testLauncherEntryPoint
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
autoreleasepool {
|
||||||
|
val exitCode = testLauncherEntryPoint(args)
|
||||||
|
// Note: this test runner checks for memory leaks after successful execution, unlike standard one.
|
||||||
|
if (exitCode != 0) exitProcess(exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
// [KT-36067] cinterop tool fails when there is a structure member named Companion
|
||||||
|
|
||||||
|
struct EnumFieldMangleStruct {
|
||||||
|
enum {Companion, Any} smth;
|
||||||
|
};
|
||||||
|
struct EnumFieldMangleStruct enumMangledStruct = { Any };
|
||||||
|
|
||||||
|
struct MyStruct {
|
||||||
|
int Companion; // simple clash
|
||||||
|
int _Companion;
|
||||||
|
int $_Companion;
|
||||||
|
int super;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MyStruct myStruct = {11, 12, 13, 14};
|
||||||
|
|
||||||
|
@protocol Proto
|
||||||
|
@property int Companion; // clash on implementing
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface FooMangled : NSObject<Proto>
|
||||||
|
//- (void) CompanionS; // mangleSimple does not support this: it may clash after mangling
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testMangling() {
|
||||||
|
assertEquals(11, myStruct.`Companion$`)
|
||||||
|
assertEquals(12, myStruct._Companion)
|
||||||
|
assertEquals(13, myStruct.`$_Companion`)
|
||||||
|
assertEquals(14, myStruct.`super`)
|
||||||
|
|
||||||
|
val objc = FooMangled()
|
||||||
|
objc.`Companion$` = 99
|
||||||
|
assertEquals(99, objc.Companion())
|
||||||
|
assertEquals(99, objc.`Companion$`)
|
||||||
|
|
||||||
|
enumMangledStruct.smth = Companion
|
||||||
|
assertEquals(Companion, enumMangledStruct.smth)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#import "mangling.h"
|
||||||
|
|
||||||
|
// [KT-36067] mangling
|
||||||
|
@implementation FooMangled : NSObject
|
||||||
|
@synthesize Companion;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface MultipleInheritanceClashBase : NSObject
|
||||||
|
@property (nonnull) MultipleInheritanceClashBase* delegate;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@protocol MultipleInheritanceClash
|
||||||
|
@optional
|
||||||
|
@property (nullable) id<MultipleInheritanceClash> delegate;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface MultipleInheritanceClash1 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface MultipleInheritanceClash2 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
||||||
|
@property MultipleInheritanceClashBase* delegate;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testMultipleInheritanceClash() {
|
||||||
|
val clash1 = MultipleInheritanceClash1()
|
||||||
|
val clash2 = MultipleInheritanceClash2()
|
||||||
|
|
||||||
|
clash1.delegate = clash1
|
||||||
|
assertEquals(clash1, clash1.delegate)
|
||||||
|
clash1.setDelegate(clash2)
|
||||||
|
assertEquals(clash2, clash1.delegate())
|
||||||
|
|
||||||
|
clash2.delegate = clash1
|
||||||
|
assertEquals(clash1, clash2.delegate)
|
||||||
|
clash2.setDelegate(clash2)
|
||||||
|
assertEquals(clash2, clash2.delegate())
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#import "multipleInheritanceClash.h"
|
||||||
|
|
||||||
|
@implementation MultipleInheritanceClashBase
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation MultipleInheritanceClash1
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation MultipleInheritanceClash2
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testNSOutputStreamToMemoryConstructor() {
|
||||||
|
val stream: Any = NSOutputStream(toMemory = Unit)
|
||||||
|
assertTrue(stream is NSOutputStream)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@class DeallocListener;
|
||||||
|
|
||||||
|
@interface DeallocExecutor : NSObject
|
||||||
|
@property DeallocListener* deallocListener;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface DeallocListener : NSObject
|
||||||
|
@property (weak) DeallocExecutor* deallocExecutor;
|
||||||
|
@property BOOL deallocated;
|
||||||
|
-(BOOL)deallocExecutorIsNil;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.native.concurrent.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testObjCWeakRef() {
|
||||||
|
val deallocListener = DeallocListener()
|
||||||
|
assertFalse(deallocListener.deallocated)
|
||||||
|
|
||||||
|
testObjCWeakRef0(deallocListener)
|
||||||
|
|
||||||
|
kotlin.native.internal.GC.collect()
|
||||||
|
assertTrue(deallocListener.deallocated)
|
||||||
|
assertTrue(deallocListener.deallocExecutorIsNil())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testObjCWeakRef0(deallocListener: DeallocListener) = withWorker {
|
||||||
|
assertTrue(deallocListener.deallocExecutorIsNil())
|
||||||
|
|
||||||
|
val obj = object : DeallocExecutor() {}
|
||||||
|
deallocListener.deallocExecutor = obj
|
||||||
|
obj.deallocListener = deallocListener
|
||||||
|
|
||||||
|
assertFalse(deallocListener.deallocExecutorIsNil())
|
||||||
|
|
||||||
|
// TODO: can't actually test, Obj-C runtime doesn't expect _tryRetain throwing an exception.
|
||||||
|
// runInWorker {
|
||||||
|
// assertFailsWith<IncorrectDereferenceException> {
|
||||||
|
// deallocListener.deallocExecutorIsNil()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
obj.freeze()
|
||||||
|
|
||||||
|
runInWorker {
|
||||||
|
// [deallocListener.deallocExecutorIsNil()] calls deallocExecutor getter, which retains [obj] and either
|
||||||
|
// puts it to autoreleasepool or releases it immediately (Obj-C ARC optimization).
|
||||||
|
// Wrap the call to autoreleasepool to ensure [obj] will be released:
|
||||||
|
autoreleasepool {
|
||||||
|
assertFalse(deallocListener.deallocExecutorIsNil())
|
||||||
|
}
|
||||||
|
// Process release of Kotlin reference to [obj] in any case:
|
||||||
|
kotlin.native.internal.GC.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#import "objcWeakRefs.h"
|
||||||
|
|
||||||
|
@implementation DeallocExecutor
|
||||||
|
-(void)dealloc {
|
||||||
|
self.deallocListener.deallocated = YES;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation DeallocListener
|
||||||
|
-(BOOL)deallocExecutorIsNil {
|
||||||
|
return self.deallocExecutor == nil;
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface TestOverrideInit : NSObject
|
||||||
|
-(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER;
|
||||||
|
+(instancetype)createWithValue:(int)value;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testOverrideInit() {
|
||||||
|
assertEquals(42, (TestOverrideInitImpl.createWithValue(42) as TestOverrideInitImpl).value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestOverrideInitImpl @OverrideInit constructor(val value: Int) : TestOverrideInit(value) {
|
||||||
|
companion object : TestOverrideInitMeta()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#import "OverrideInit.h"
|
||||||
|
|
||||||
|
@implementation TestOverrideInit
|
||||||
|
-(instancetype)initWithValue:(int)value {
|
||||||
|
return self = [super init];
|
||||||
|
}
|
||||||
|
|
||||||
|
+(instancetype)createWithValue:(int)value {
|
||||||
|
return [[self alloc] initWithValue:value];
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.native.concurrent.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
private class NSObjectImpl : NSObject() {
|
||||||
|
var x = 111
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun testSharing() = withWorker {
|
||||||
|
val obj = NSObjectImpl()
|
||||||
|
val array = nsArrayOf(obj)
|
||||||
|
|
||||||
|
assertFalse(obj.isFrozen)
|
||||||
|
|
||||||
|
runInWorker {
|
||||||
|
assertFailsWith<IncorrectDereferenceException> {
|
||||||
|
array.objectAtIndex(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.x = 222
|
||||||
|
obj.freeze()
|
||||||
|
assertTrue(obj.isFrozen)
|
||||||
|
|
||||||
|
runInWorker {
|
||||||
|
val obj1 = array.objectAtIndex(0) as NSObjectImpl
|
||||||
|
assertFailsWith<InvalidMutabilityException> {
|
||||||
|
obj1.x = 333
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(222, obj.x)
|
||||||
|
|
||||||
|
// TODO: test [obj release] etc.
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.native.concurrent.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
fun Worker.runInWorker(block: () -> Unit) {
|
||||||
|
block.freeze()
|
||||||
|
val future = this.execute(TransferMode.SAFE, { block }) {
|
||||||
|
it()
|
||||||
|
}
|
||||||
|
future.result // Throws on failure.
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
|
||||||
|
elements.forEach {
|
||||||
|
this.addObject(it as ObjCObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface TestVarargs : NSObject
|
||||||
|
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||||
|
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...;
|
||||||
|
@property NSString* formatted;
|
||||||
|
|
||||||
|
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ...;
|
||||||
|
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface TestVarargs (TestVarargsExtension)
|
||||||
|
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface TestVarargsSubclass : TestVarargs
|
||||||
|
// Test clashes:
|
||||||
|
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args;
|
||||||
|
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testVarargs() {
|
||||||
|
assertEquals(
|
||||||
|
"a b -1",
|
||||||
|
TestVarargs.testVarargsWithFormat(
|
||||||
|
"%@ %s %d",
|
||||||
|
"a" as NSString, "b".cstr, (-1).toByte()
|
||||||
|
).formatted
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"2 3 9223372036854775807",
|
||||||
|
TestVarargs(
|
||||||
|
"%d %d %lld",
|
||||||
|
2.toShort(), 3, Long.MAX_VALUE
|
||||||
|
).formatted
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"0.1 0.2 1 0",
|
||||||
|
TestVarargs.create(
|
||||||
|
"%.1f %.1lf %d %d",
|
||||||
|
0.1.toFloat(), 0.2, true, false
|
||||||
|
).formatted
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"1 2 3",
|
||||||
|
TestVarargs(
|
||||||
|
format = "%d %d %d",
|
||||||
|
args = *arrayOf(1, 2, 3)
|
||||||
|
).formatted
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"4 5 6",
|
||||||
|
TestVarargs(
|
||||||
|
args = *arrayOf(4, *arrayOf(5, 6)),
|
||||||
|
format = "%d %d %d"
|
||||||
|
).formatted
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"7",
|
||||||
|
TestVarargsSubclass.stringWithFormat(
|
||||||
|
"%d",
|
||||||
|
7
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#import <stdlib.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
#import "varargs.h"
|
||||||
|
|
||||||
|
@implementation TestVarargs
|
||||||
|
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ... {
|
||||||
|
self = [super init];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
self.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... {
|
||||||
|
TestVarargs* result = [[self alloc] init];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
result.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ... {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation TestVarargsSubclass
|
||||||
|
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
NSObject* createNSObject() {
|
||||||
|
return [NSObject new];
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import kotlin.native.ref.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test fun testWeakRefs() {
|
||||||
|
testWeakReference({ createNSObject()!! })
|
||||||
|
|
||||||
|
createAndAbandonWeakRef(NSObject())
|
||||||
|
|
||||||
|
testWeakReference({ NSArray.arrayWithArray(listOf(42)) as NSArray })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testWeakReference(block: () -> NSObject) {
|
||||||
|
val ref = autoreleasepool {
|
||||||
|
createAndTestWeakReference(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin.native.internal.GC.collect()
|
||||||
|
|
||||||
|
assertNull(ref.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAndTestWeakReference(block: () -> NSObject): WeakReference<NSObject> {
|
||||||
|
val ref = createWeakReference(block)
|
||||||
|
assertNotNull(ref.get())
|
||||||
|
assertEquals(ref.get()!!.hash(), ref.get()!!.hash())
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createWeakReference(block: () -> NSObject) = WeakReference(block())
|
||||||
|
|
||||||
|
private fun createAndAbandonWeakRef(obj: NSObject) {
|
||||||
|
WeakReference(obj)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user