Move everything under kotlin-native folder
I was forced to manually do update the following files, because otherwise they would be ignored according .gitignore settings. Probably they should be deleted from repo. Interop/.idea/compiler.xml Interop/.idea/gradle.xml Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_3.xml Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_3.xml Interop/.idea/modules.xml Interop/.idea/modules/Indexer/Indexer.iml Interop/.idea/modules/Runtime/Runtime.iml Interop/.idea/modules/StubGenerator/StubGenerator.iml backend.native/backend.native.iml backend.native/bc.frontend/bc.frontend.iml backend.native/cli.bc/cli.bc.iml backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt backend.native/tests/link/lib/foo.kt backend.native/tests/link/lib/foo2.kt backend.native/tests/teamcity-test.property
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- AUTO-GENERATED FILE. DO NOT MODIFY. -->
|
||||
<!-- This file was automatically generated by the LocoLaser tool. -->
|
||||
<!-- It should not be modified by hand. -->
|
||||
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>screen_main_plural_string</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@value@</string>
|
||||
<key>value</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>other</key>
|
||||
<string>Plural: %d apples</string>
|
||||
<key>one</key>
|
||||
<string>Plural: one apple</string>
|
||||
<key>zero</key>
|
||||
<string>Plural: no apples</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.
|
||||
*/
|
||||
|
||||
import platform.Foundation.*
|
||||
import platform.objc.*
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import platform.posix.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun exc_handler(x: Any?) : Unit {
|
||||
println("Uncaught exception handler")
|
||||
println(x.toString())
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
// This does not work anymore, as NSException propagated as ForeignException
|
||||
// so we got `Uncaught Kotlin exception` instead. Use normal try/catch.
|
||||
objc_setUncaughtExceptionHandler(staticCFunction(::exc_handler))
|
||||
|
||||
try {
|
||||
println(NSJSONSerialization())
|
||||
} catch (e: Exception) { // ForeignException expected
|
||||
println(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
language = Objective-C
|
||||
headerFilter = **/objc_wrap.h
|
||||
linkerOpts = -lobjcexception
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Test different behavior depending on foreignExceptionMode option
|
||||
*/
|
||||
|
||||
import kotlin.test.*
|
||||
import objcExceptionMode.*
|
||||
import kotlinx.cinterop.*
|
||||
import platform.objc.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
|
||||
@Test fun testKT35056() {
|
||||
val name = "Some native exception"
|
||||
val reason = "Illegal value"
|
||||
var finallyBlockTest = "FAILED"
|
||||
var catchBlockTest = "FAILED"
|
||||
try {
|
||||
raiseExc(name, reason)
|
||||
assertNotEquals("FAILED", catchBlockTest) // shall not get here anyway
|
||||
} catch (e: ForeignException) {
|
||||
val ret = logExc(e.nativeException) // return NSException name
|
||||
assertEquals(name, ret)
|
||||
assertEquals("$name:: $reason", e.message)
|
||||
println("OK: ForeignException")
|
||||
catchBlockTest = "PASSED"
|
||||
} finally {
|
||||
finallyBlockTest = "PASSED"
|
||||
}
|
||||
assertEquals("PASSED", catchBlockTest)
|
||||
assertEquals("PASSED", finallyBlockTest)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun abnormal_handler(x: Any?) : Unit {
|
||||
println("OK: Ends with uncaught exception handler")
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
// Depending on the `foreignxceptionMode` option (def file or cinterop cli) this test should ends
|
||||
// normally with `ForeignException` handled or abnormally with `abnormal_handler`.
|
||||
// Test shall validate output (golden value) from `abnormal_handler`.
|
||||
|
||||
objc_setUncaughtExceptionHandler(staticCFunction(::abnormal_handler))
|
||||
|
||||
testKT35056()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
language = Objective-C
|
||||
headerFilter = **/objc_wrap.h
|
||||
linkerOpts = -lobjcexception
|
||||
foreignExceptionMode = objc-wrap
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void raiseExc(id name, id reason);
|
||||
id logExc(id exception);
|
||||
|
||||
@interface Foo : NSObject
|
||||
- (void)instanceMethodThrow:(id)name reason:(id)reason;
|
||||
+ (void)classMethodThrow:(id)name reason:(id)reason;
|
||||
@end
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Test different types of callable with foreignExceptionMode=objc-wrap
|
||||
*/
|
||||
|
||||
import kotlin.test.*
|
||||
//import objcTests.*
|
||||
import objc_wrap.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
fun testInner(name: String, reason: String) {
|
||||
var finallyBlockTest = "FAILED"
|
||||
var catchBlockTest = "NOT EXPECTED"
|
||||
try {
|
||||
raiseExc(name, reason)
|
||||
} catch (e: RuntimeException) {
|
||||
catchBlockTest = "This shouldn't happen"
|
||||
} finally {
|
||||
finallyBlockTest = "PASSED"
|
||||
}
|
||||
assertEquals("NOT EXPECTED", catchBlockTest)
|
||||
assertEquals("PASSED", finallyBlockTest)
|
||||
}
|
||||
|
||||
typealias CallMe = (String, String) -> Unit
|
||||
|
||||
@Test fun testExceptionWrap(raise: CallMe) {
|
||||
val name = "Some native exception"
|
||||
val reason = "Illegal value"
|
||||
var finallyBlockTest = "FAILED"
|
||||
var catchBlockTest = "FAILED"
|
||||
try {
|
||||
raise(name, reason)
|
||||
} catch (e: ForeignException) {
|
||||
val ret = logExc(e.nativeException) // return NSException name
|
||||
assertEquals(name, ret)
|
||||
assertEquals("$name:: $reason", e.message)
|
||||
catchBlockTest = "PASSED"
|
||||
} finally {
|
||||
finallyBlockTest = "PASSED"
|
||||
}
|
||||
assertEquals("PASSED", catchBlockTest)
|
||||
assertEquals("PASSED", finallyBlockTest)
|
||||
}
|
||||
|
||||
class Bar() : Foo()
|
||||
|
||||
fun main() {
|
||||
testExceptionWrap(::raiseExc) // simple
|
||||
testExceptionWrap(::testInner) // nested try block
|
||||
testExceptionWrap(Foo::classMethodThrow) // class method
|
||||
testExceptionWrap(Foo()::instanceMethodThrow) // instance method
|
||||
testExceptionWrap(Bar()::instanceMethodThrow) // fake override
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "objc_wrap.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void raiseExc(id name, id reason) {
|
||||
[NSException raise:name format:@"%@", reason];
|
||||
}
|
||||
|
||||
id logExc(id exc) {
|
||||
assert([exc isKindOfClass:[NSException class]]);
|
||||
return ((NSException*)exc).name;
|
||||
}
|
||||
|
||||
@implementation Foo : NSObject
|
||||
- (void) instanceMethodThrow:(id)name reason:(id)reason {
|
||||
raiseExc(name, reason);
|
||||
}
|
||||
+ (void) classMethodThrow:(id)name reason:(id)reason {
|
||||
raiseExc(name, reason);
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,30 @@
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.test.*
|
||||
import platform.Foundation.*
|
||||
import platform.darwin.NSObject
|
||||
|
||||
fun Worker.runInWorker(block: () -> Unit) {
|
||||
this.execute(TransferMode.SAFE, { block.freeze() }) {
|
||||
it()
|
||||
}.result
|
||||
}
|
||||
|
||||
private class NSObjectImpl : NSObject() {
|
||||
var x = 111
|
||||
}
|
||||
|
||||
// Also see counterpart in interop/objc/tests/sharing.kt
|
||||
fun main() = withWorker {
|
||||
val obj = NSObjectImpl()
|
||||
val array: NSArray = NSMutableArray().apply {
|
||||
addObject(obj)
|
||||
}
|
||||
|
||||
assertFalse(obj.isFrozen)
|
||||
|
||||
println("Before")
|
||||
runInWorker {
|
||||
array.objectAtIndex(0)
|
||||
}
|
||||
println("After")
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlinx.cinterop.autoreleasepool
|
||||
import objclib.*
|
||||
|
||||
fun main() {
|
||||
autoreleasepool {
|
||||
run()
|
||||
}
|
||||
}
|
||||
|
||||
private class NSObjectImpl : NSObject() {
|
||||
var x = 111
|
||||
}
|
||||
|
||||
fun run() = withWorker {
|
||||
val obj = NSObjectImpl()
|
||||
setObject(obj)
|
||||
|
||||
println("Before")
|
||||
val isAlive = try {
|
||||
execute(TransferMode.SAFE, {}) {
|
||||
isObjectAliveShouldCrash()
|
||||
}.result
|
||||
} catch (e: Throwable) {
|
||||
false
|
||||
}
|
||||
println("After $isAlive")
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
language = Objective-C
|
||||
headerFilter = **/objclib.h
|
||||
@@ -0,0 +1,13 @@
|
||||
#import <objc/NSObject.h>
|
||||
|
||||
static NSObject* __weak globalObject = nil;
|
||||
|
||||
void setObject(NSObject* obj) {
|
||||
globalObject = obj;
|
||||
}
|
||||
|
||||
// Make sure this function persists, because the test expects to find this function in the stack trace.
|
||||
__attribute__((noinline))
|
||||
bool isObjectAliveShouldCrash() {
|
||||
return globalObject != nil;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#define ANSWER 42
|
||||
@@ -0,0 +1,6 @@
|
||||
import module_library.*
|
||||
|
||||
fun main() {
|
||||
println("OK")
|
||||
println(ANSWER)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
language = Objective-C
|
||||
modules = module_library
|
||||
@@ -0,0 +1,6 @@
|
||||
module module_library {
|
||||
umbrella header "module_library_umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#import <foo.h>
|
||||
@@ -0,0 +1,58 @@
|
||||
import messaging.*
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
autoreleasepool {
|
||||
primitives()
|
||||
aggregates()
|
||||
}
|
||||
}
|
||||
|
||||
private fun primitives() {
|
||||
assertEquals(3.14f, PrimitiveTestSubject.floatFn())
|
||||
assertEquals(3.14, PrimitiveTestSubject.doubleFn())
|
||||
assertEquals(42, PrimitiveTestSubject.intFn())
|
||||
assertEquals(vectorOf(2f, 4f, 5f, 8f), PrimitiveTestSubject.simdFn())
|
||||
}
|
||||
|
||||
private fun aggregates() {
|
||||
AggregateTestSubject.singleFloatFn().useContents {
|
||||
assertEquals(3.14f, f)
|
||||
}
|
||||
AggregateTestSubject.simplePackedFn().useContents {
|
||||
assertEquals('0'.toByte(), f1)
|
||||
assertEquals(111, f2)
|
||||
}
|
||||
AggregateTestSubject.evenSmallerPackedFn().useContents {
|
||||
assertEquals('x'.toByte(), x)
|
||||
assertEquals(169, y)
|
||||
assertEquals('z'.toByte(), z)
|
||||
}
|
||||
AggregateTestSubject.homogeneousBigFn().useContents {
|
||||
assertEquals(1.0f, f1)
|
||||
assertEquals(2.0f, f2)
|
||||
assertEquals(3.0f, f3)
|
||||
assertEquals(4.0f, f4)
|
||||
assertEquals(5.0f, f5)
|
||||
assertEquals(6.0f, f6)
|
||||
assertEquals(7.0f, f7)
|
||||
assertEquals(8.0f, f8)
|
||||
}
|
||||
AggregateTestSubject.homogeneousSmallFn().useContents {
|
||||
assertEquals(1.0f, f1)
|
||||
assertEquals(2.0f, f2)
|
||||
assertEquals(3.0f, f3)
|
||||
assertEquals(4.0f, f4)
|
||||
}
|
||||
AggregateTestSubject.simd_quatfFn().useContents {
|
||||
assertEquals(vectorOf(1f, 4f, 9f, 25f), vector)
|
||||
}
|
||||
AggregateTestSubject.geterogeneousSmallFn().useContents {
|
||||
assertEquals(1, s1)
|
||||
assertEquals(vectorOf(1f, 4f, 9f, 25f), v2)
|
||||
assertEquals(3f, f3)
|
||||
assertEquals(4, i4)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
language = Objective-C
|
||||
headerFilter = **/messaging.h
|
||||
linkerOpts = -lobjcmessaging
|
||||
@@ -0,0 +1,68 @@
|
||||
#import <objc/NSObject.h>
|
||||
#include <simd/simd.h>
|
||||
|
||||
@interface PrimitiveTestSubject : NSObject
|
||||
|
||||
+ (int)intFn;
|
||||
+ (float)floatFn;
|
||||
+ (double)doubleFn;
|
||||
+ (simd_float4)simdFn;
|
||||
|
||||
@end;
|
||||
|
||||
typedef struct {
|
||||
float f;
|
||||
} SingleFloat;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char f1;
|
||||
short f2;
|
||||
char f3;
|
||||
char f4;
|
||||
} SimplePacked;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char x;
|
||||
short y;
|
||||
char z;
|
||||
} EvenSmallerPacked;
|
||||
|
||||
typedef struct {
|
||||
float f1;
|
||||
float f2;
|
||||
float f3;
|
||||
float f4;
|
||||
} HomogeneousSmall;
|
||||
|
||||
typedef struct {
|
||||
float f1;
|
||||
float f2;
|
||||
float f3;
|
||||
float f4;
|
||||
float f5;
|
||||
float f6;
|
||||
float f7;
|
||||
float f8;
|
||||
} HomogeneousBig;
|
||||
|
||||
// TODO: Add more cases later: SIMD, bitfields.
|
||||
|
||||
typedef struct {
|
||||
short s1;
|
||||
simd_float4 v2;
|
||||
float f3;
|
||||
int i4;
|
||||
} GeterogeneousSmall;
|
||||
|
||||
@interface AggregateTestSubject : NSObject
|
||||
|
||||
+ (SingleFloat)singleFloatFn;
|
||||
+ (SimplePacked)simplePackedFn;
|
||||
+ (EvenSmallerPacked)evenSmallerPackedFn;
|
||||
+ (HomogeneousSmall)homogeneousSmallFn;
|
||||
+ (HomogeneousBig)homogeneousBigFn;
|
||||
+ (simd_quatf)simd_quatfFn;
|
||||
+ (GeterogeneousSmall)geterogeneousSmallFn;
|
||||
|
||||
|
||||
@end;
|
||||
@@ -0,0 +1,81 @@
|
||||
#import "messaging.h"
|
||||
|
||||
@implementation PrimitiveTestSubject
|
||||
|
||||
+ (int)intFn {
|
||||
return 42;
|
||||
}
|
||||
|
||||
+ (float)floatFn {
|
||||
return 3.14f;
|
||||
}
|
||||
|
||||
+ (double)doubleFn {
|
||||
return 3.14;
|
||||
}
|
||||
|
||||
+ (simd_float4)simdFn {
|
||||
simd_float4 v;
|
||||
v.x = 2;
|
||||
v.y = 4;
|
||||
v.z = 5;
|
||||
v.w = 8;
|
||||
return v;
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation AggregateTestSubject
|
||||
|
||||
+ (SingleFloat)singleFloatFn {
|
||||
SingleFloat s;
|
||||
s.f = 3.14f;
|
||||
return s;
|
||||
}
|
||||
|
||||
+ (SimplePacked)simplePackedFn {
|
||||
SimplePacked s;
|
||||
s.f1 = '0';
|
||||
s.f2 = 111;
|
||||
return s;
|
||||
}
|
||||
|
||||
+ (EvenSmallerPacked)evenSmallerPackedFn {
|
||||
EvenSmallerPacked s;
|
||||
s.x = 'x';
|
||||
s.y = 169;
|
||||
s.z = 'z';
|
||||
return s;
|
||||
}
|
||||
|
||||
+ (HomogeneousSmall)homogeneousSmallFn {
|
||||
HomogeneousSmall s;
|
||||
s.f1 = 1.0f;
|
||||
s.f2 = 2.0f;
|
||||
s.f3 = 3.0f;
|
||||
s.f4 = 4.0f;
|
||||
return s;
|
||||
}
|
||||
|
||||
+ (HomogeneousBig)homogeneousBigFn {
|
||||
HomogeneousBig s;
|
||||
s.f1 = 1.0f;
|
||||
s.f2 = 2.0f;
|
||||
s.f3 = 3.0f;
|
||||
s.f4 = 4.0f;
|
||||
s.f5 = 5.0f;
|
||||
s.f6 = 6.0f;
|
||||
s.f7 = 7.0f;
|
||||
s.f8 = 8.0f;
|
||||
return s;
|
||||
}
|
||||
|
||||
+ (GeterogeneousSmall)geterogeneousSmallFn {
|
||||
return (GeterogeneousSmall){1, {1, 4, 9, 25}, 3, 4};
|
||||
}
|
||||
|
||||
+ (simd_quatf)simd_quatfFn {
|
||||
return (simd_quatf){ {1, 4, 9, 25} };
|
||||
}
|
||||
|
||||
@end;
|
||||
@@ -0,0 +1,5 @@
|
||||
language = Objective-C
|
||||
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h Foundation/NSBundle.h
|
||||
headerFilter = **/smoke.h Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
|
||||
objc/objc.h Foundation/NSBundle.h
|
||||
linkerOpts = -lobjcsmoke
|
||||
@@ -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
|
||||
@@ -0,0 +1,117 @@
|
||||
#import <objc/NSObject.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
@class Foo;
|
||||
|
||||
@protocol Printer;
|
||||
@protocol Printer;
|
||||
|
||||
@protocol Empty
|
||||
@end;
|
||||
|
||||
@protocol Forward;
|
||||
@class Forward;
|
||||
|
||||
void useForward1(Forward * p) {}
|
||||
void useForward2(id<Forward> p) {}
|
||||
|
||||
typedef NSString NSStringTypedef;
|
||||
|
||||
@interface Foo : NSObject <Empty>
|
||||
@property NSStringTypedef* name;
|
||||
-(void)helloWithPrinter:(id <Printer>)printer;
|
||||
@end;
|
||||
|
||||
@interface Foo (FooExtensions)
|
||||
-(void)hello;
|
||||
@end;
|
||||
|
||||
@protocol Printer
|
||||
@required
|
||||
-(void)print:(const char*)string;
|
||||
@end;
|
||||
|
||||
@protocol MutablePair
|
||||
@required
|
||||
@property (readonly) int first;
|
||||
@property (readonly) int second;
|
||||
|
||||
-(void)update:(int)index add:(int)delta;
|
||||
-(void)update:(int)index sub:(int)delta;
|
||||
|
||||
@end;
|
||||
|
||||
void replacePairElements(id <MutablePair> pair, int first, int second);
|
||||
|
||||
int invoke1(int arg, int (^block)(int)) {
|
||||
return block(arg);
|
||||
}
|
||||
|
||||
void invoke2(void (^block)(void)) {
|
||||
block();
|
||||
}
|
||||
|
||||
int (^getSupplier(int x))(void);
|
||||
Class (^ _Nonnull getClassGetter(NSObject* obj))(void);
|
||||
|
||||
extern NSString* globalString;
|
||||
extern NSObject* globalObject;
|
||||
|
||||
int formatStringLength(NSString* format, ...);
|
||||
|
||||
#define STRING_MACRO @"String macro"
|
||||
#define CFSTRING_MACRO CFSTR("CFString macro")
|
||||
|
||||
typedef NS_ENUM(int32_t, ForwardDeclaredEnum);
|
||||
typedef NS_ENUM(int32_t, ForwardDeclaredEnum) {
|
||||
ZERO, ONE, TWO,
|
||||
};
|
||||
|
||||
@protocol ObjectFactory
|
||||
@required
|
||||
-(id)create;
|
||||
@end;
|
||||
|
||||
id createObjectWithFactory(id<ObjectFactory> factory) {
|
||||
return [factory create];
|
||||
}
|
||||
|
||||
@protocol CustomRetainMethods
|
||||
@required
|
||||
-(id)returnRetained:(id)obj __attribute__((ns_returns_retained));
|
||||
-(void)consume:(id) __attribute__((ns_consumed)) obj;
|
||||
-(void)consumeSelf __attribute__((ns_consumes_self));
|
||||
-(void (^)(void))returnRetainedBlock:(void (^)(void))block __attribute__((ns_returns_retained));
|
||||
@end;
|
||||
|
||||
extern BOOL unexpectedDeallocation;
|
||||
|
||||
@interface MustNotBeDeallocated : NSObject
|
||||
@end;
|
||||
|
||||
@interface CustomRetainMethodsImpl : MustNotBeDeallocated <CustomRetainMethods>
|
||||
@end;
|
||||
|
||||
static MustNotBeDeallocated* retainedObj;
|
||||
static void (^retainedBlock)(void);
|
||||
|
||||
void useCustomRetainMethods(id<CustomRetainMethods> p) {
|
||||
MustNotBeDeallocated* obj = [MustNotBeDeallocated new];
|
||||
retainedObj = obj; // Retain to detect possible over-release.
|
||||
[p returnRetained:obj];
|
||||
|
||||
[p consume:p];
|
||||
[p consumeSelf];
|
||||
|
||||
MustNotBeDeallocated* capturedObj = [MustNotBeDeallocated new];
|
||||
retainedBlock = ^{ [capturedObj description]; }; // Retain to detect possible over-release.
|
||||
[p returnRetainedBlock:retainedBlock]();
|
||||
}
|
||||
|
||||
id getPrinterProtocolRaw() {
|
||||
return @protocol(Printer);
|
||||
}
|
||||
|
||||
Protocol* getPrinterProtocol() {
|
||||
return @protocol(Printer);
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import objcSmoke.*
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.ref.*
|
||||
import kotlin.test.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
autoreleasepool {
|
||||
run()
|
||||
}
|
||||
}
|
||||
|
||||
fun run() {
|
||||
// TODO: migrate remaining tests to interop/objc/tests/
|
||||
testTypeOps()
|
||||
testCustomRetain()
|
||||
testExportObjCClass()
|
||||
testLocalizedStrings()
|
||||
|
||||
assertEquals(2, ForwardDeclaredEnum.TWO.value)
|
||||
|
||||
println(
|
||||
getSupplier(
|
||||
invoke1(42) { it * 2 }
|
||||
)!!()
|
||||
)
|
||||
|
||||
val foo = Foo()
|
||||
|
||||
val classGetter = getClassGetter(foo)
|
||||
invoke2 { println(classGetter()) }
|
||||
|
||||
foo.hello()
|
||||
foo.name = "everybody"
|
||||
foo.helloWithPrinter(object : NSObject(), PrinterProtocol {
|
||||
override fun print(string: CPointer<ByteVar>?) {
|
||||
println("Kotlin says: " + string?.toKString())
|
||||
}
|
||||
})
|
||||
|
||||
Bar().hello()
|
||||
|
||||
val pair = MutablePairImpl(42, 17)
|
||||
replacePairElements(pair, 1, 2)
|
||||
pair.swap()
|
||||
println("${pair.first}, ${pair.second}")
|
||||
|
||||
val defaultPair = MutablePairImpl()
|
||||
assertEquals(defaultPair.first(), 123)
|
||||
assertEquals(defaultPair.second(), 321)
|
||||
|
||||
// equals and hashCode (virtually):
|
||||
val map = mapOf(foo to pair, pair to foo)
|
||||
|
||||
// equals (directly):
|
||||
if (!foo.equals(pair)) {
|
||||
// toString (directly):
|
||||
println(map[pair].toString() + map[foo].toString() == foo.description() + pair.description())
|
||||
}
|
||||
|
||||
// hashCode (directly):
|
||||
// hash() returns value of NSUInteger type.
|
||||
val hash = when (Platform.osFamily) {
|
||||
// `typedef unsigned int NSInteger` on watchOS.
|
||||
OsFamily.WATCHOS -> foo.hash().toInt()
|
||||
// `typedef unsigned long NSUInteger` on iOS, macOS, tvOS.
|
||||
else -> foo.hash().let { it.toInt() xor (it shr 32).toInt() }
|
||||
}
|
||||
if (foo.hashCode() == hash) {
|
||||
// toString (virtually):
|
||||
if (Platform.memoryModel == MemoryModel.STRICT)
|
||||
println(map.keys.map { it.toString() }.min() == foo.description())
|
||||
else
|
||||
// TODO: hack until proper cycle collection in maps.
|
||||
println(true)
|
||||
}
|
||||
println(globalString)
|
||||
autoreleasepool {
|
||||
globalString = "Another global string"
|
||||
}
|
||||
println(globalString)
|
||||
|
||||
println(globalObject)
|
||||
globalObject = object : NSObject() {
|
||||
override fun description() = "global object"
|
||||
}
|
||||
println(globalObject)
|
||||
globalObject = null // Prevent Kotlin object above from leaking.
|
||||
|
||||
println(formatStringLength("%d %d", 42, 17))
|
||||
|
||||
println(STRING_MACRO)
|
||||
println(CFSTRING_MACRO)
|
||||
|
||||
// Ensure that overriding method bridge has retain-autorelease sequence:
|
||||
createObjectWithFactory(object : NSObject(), ObjectFactoryProtocol {
|
||||
override fun create() = autoreleasepool { NSObject() }
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fun MutablePairProtocol.swap() {
|
||||
update(0, add = second)
|
||||
update(1, sub = first)
|
||||
update(0, add = second)
|
||||
update(1, sub = second*2)
|
||||
}
|
||||
|
||||
class Bar : Foo() {
|
||||
override fun helloWithPrinter(printer: PrinterProtocol?) = memScoped {
|
||||
printer!!.print("Hello from Kotlin".cstr.getPointer(memScope))
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CONFLICTING_OVERLOADS")
|
||||
class MutablePairImpl(first: Int, second: Int) : NSObject(), MutablePairProtocol {
|
||||
private var elements = intArrayOf(first, second)
|
||||
|
||||
override fun first() = elements.first()
|
||||
override fun second() = elements.last()
|
||||
|
||||
override fun update(index: Int, add: Int) {
|
||||
elements[index] += add
|
||||
}
|
||||
|
||||
override fun update(index: Int, sub: Int) {
|
||||
elements[index] -= sub
|
||||
}
|
||||
|
||||
constructor() : this(123, 321)
|
||||
}
|
||||
|
||||
interface Zzz
|
||||
|
||||
fun testTypeOps() {
|
||||
assertTrue(99.asAny() is NSNumber)
|
||||
assertTrue(null.asAny() is NSNumber?)
|
||||
assertFalse(null.asAny() is NSNumber)
|
||||
assertFalse("".asAny() is NSNumber)
|
||||
assertTrue("bar".asAny() is NSString)
|
||||
|
||||
assertTrue(Foo.asAny() is FooMeta)
|
||||
assertFalse(Foo.asAny() is Zzz)
|
||||
assertTrue(Foo.asAny() is NSObjectMeta)
|
||||
assertTrue(Foo.asAny() is NSObject)
|
||||
assertFalse(Foo.asAny() is Foo)
|
||||
assertTrue(NSString.asAny() is NSCopyingProtocolMeta)
|
||||
assertFalse(NSString.asAny() is NSCopyingProtocol)
|
||||
assertTrue(NSValue.asAny() is NSObjectProtocolMeta)
|
||||
assertFalse(NSValue.asAny() is NSObjectProtocol) // Must be true, but not implemented properly yet.
|
||||
|
||||
assertFalse(Any() is ObjCClass)
|
||||
assertFalse(Any() is ObjCClassOf<*>)
|
||||
assertFalse(NSObject().asAny() is ObjCClass)
|
||||
assertFalse(NSObject().asAny() is ObjCClassOf<*>)
|
||||
assertTrue(NSObject.asAny() is ObjCClass)
|
||||
assertTrue(NSObject.asAny() is ObjCClassOf<*>)
|
||||
|
||||
assertFalse(Any() is ObjCProtocol)
|
||||
assertTrue(getPrinterProtocolRaw() is ObjCProtocol)
|
||||
val printerProtocol = getPrinterProtocol()!!
|
||||
assertTrue(printerProtocol.asAny() is ObjCProtocol)
|
||||
|
||||
assertEquals(3u, ("foo" as NSString).length())
|
||||
assertEquals(4u, ((1..4).joinToString("") as NSString).length())
|
||||
assertEquals(2u, (listOf(0, 1) as NSArray).count())
|
||||
assertEquals(42L, (42 as NSNumber).longLongValue())
|
||||
|
||||
assertFails { "bar" as NSNumber }
|
||||
assertFails { 42 as NSArray }
|
||||
assertFails { listOf(1) as NSString }
|
||||
assertFails { NSObject() as Bar }
|
||||
assertFails { NSObject() as NSValue }
|
||||
|
||||
MutablePairImpl(1, 2).asAny() as MutablePairProtocol
|
||||
assertFails { MutablePairImpl(1, 2).asAny() as Foo }
|
||||
}
|
||||
|
||||
private lateinit var retainedMustNotBeDeallocated: MustNotBeDeallocated
|
||||
|
||||
fun testCustomRetain() {
|
||||
fun test() {
|
||||
useCustomRetainMethods(object : Foo(), CustomRetainMethodsProtocol {
|
||||
override fun returnRetained(obj: Any?) = obj
|
||||
override fun consume(obj: Any?) {}
|
||||
override fun consumeSelf() {}
|
||||
override fun returnRetainedBlock(block: (() -> Unit)?) = block
|
||||
})
|
||||
|
||||
CustomRetainMethodsImpl().let {
|
||||
it.returnRetained(Any())
|
||||
retainedMustNotBeDeallocated = MustNotBeDeallocated() // Retain to detect possible over-release.
|
||||
it.consume(retainedMustNotBeDeallocated)
|
||||
it.consumeSelf()
|
||||
it.returnRetainedBlock({})!!()
|
||||
}
|
||||
}
|
||||
|
||||
autoreleasepool {
|
||||
test()
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
|
||||
assertFalse(unexpectedDeallocation)
|
||||
}
|
||||
|
||||
private const val TestExportObjCClass1Name = "TestExportObjCClass"
|
||||
@ExportObjCClass(TestExportObjCClass1Name) class TestExportObjCClass1 : NSObject()
|
||||
|
||||
@ExportObjCClass class TestExportObjCClass2 : NSObject()
|
||||
|
||||
const val TestExportObjCClass34Name = "TestExportObjCClass34"
|
||||
@ExportObjCClass(TestExportObjCClass34Name) class TestExportObjCClass3 : NSObject()
|
||||
@ExportObjCClass(TestExportObjCClass34Name) class TestExportObjCClass4 : NSObject()
|
||||
|
||||
fun testExportObjCClass() {
|
||||
assertEquals(TestExportObjCClass1Name, TestExportObjCClass1().objCClassName)
|
||||
assertEquals("TestExportObjCClass2", TestExportObjCClass2().objCClassName)
|
||||
|
||||
assertTrue((TestExportObjCClass3().objCClassName == TestExportObjCClass34Name)
|
||||
xor (TestExportObjCClass4().objCClassName == TestExportObjCClass34Name))
|
||||
}
|
||||
|
||||
fun testLocalizedStrings() {
|
||||
val key = "screen_main_plural_string"
|
||||
val localizedString = NSBundle.mainBundle.localizedStringForKey(key, value = "", table = "Localizable")
|
||||
val string = NSString.localizedStringWithFormat(localizedString, 5)
|
||||
assertEquals("Plural: 5 apples", string)
|
||||
}
|
||||
|
||||
private val Any.objCClassName: String
|
||||
get() = object_getClassName(this)!!.toKString()
|
||||
|
||||
fun Any?.asAny(): Any? = this
|
||||
@@ -0,0 +1,97 @@
|
||||
#import <stdio.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import "smoke.h"
|
||||
|
||||
@interface CPrinter : NSObject <Printer>
|
||||
-(void)print:(const char*)string;
|
||||
@end;
|
||||
|
||||
@implementation CPrinter
|
||||
-(void)print:(const char*)string {
|
||||
printf("%s\n", string);
|
||||
fflush(stdout);
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize name;
|
||||
|
||||
-(instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.name = @"World";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)helloWithPrinter:(id <Printer>)printer {
|
||||
NSString* message = [NSString stringWithFormat:@"Hello, %@!", self.name];
|
||||
[printer print:message.UTF8String];
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
printf("Deallocated\n");
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation Foo (FooExtensions)
|
||||
|
||||
-(void)hello {
|
||||
CPrinter* printer = [[CPrinter alloc] init];
|
||||
[self helloWithPrinter:printer];
|
||||
}
|
||||
@end;
|
||||
|
||||
void replacePairElements(id <MutablePair> pair, int first, int second) {
|
||||
[pair update:0 add:(first - pair.first)];
|
||||
[pair update:1 sub:(pair.second - second)];
|
||||
}
|
||||
|
||||
int (^getSupplier(int x))(void) {
|
||||
return ^{
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
Class (^ _Nonnull getClassGetter(NSObject* obj))() {
|
||||
return ^{ return obj.class; };
|
||||
}
|
||||
|
||||
NSString* globalString = @"Global string";
|
||||
NSObject* globalObject = nil;
|
||||
|
||||
int formatStringLength(NSString* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
return result.length;
|
||||
}
|
||||
|
||||
BOOL unexpectedDeallocation = NO;
|
||||
|
||||
@implementation MustNotBeDeallocated
|
||||
-(void)dealloc {
|
||||
unexpectedDeallocation = YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
|
||||
|
||||
@implementation CustomRetainMethodsImpl
|
||||
-(id)returnRetained:(id)obj __attribute__((ns_returns_retained)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
-(void)consume:(id) __attribute__((ns_consumed)) obj {
|
||||
}
|
||||
|
||||
-(void)consumeSelf __attribute__((ns_consumes_self)) {
|
||||
retainedCustomRetainMethodsImpl = self; // Retain to detect possible over-release.
|
||||
}
|
||||
|
||||
-(void (^)(void))returnRetainedBlock:(void (^)(void))block __attribute__((ns_returns_retained)) {
|
||||
return block;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@protocol KT38234_P1
|
||||
-(int)foo;
|
||||
@end;
|
||||
|
||||
@interface KT38234_Base : NSObject <KT38234_P1>
|
||||
-(int)callFoo;
|
||||
@end;
|
||||
@@ -0,0 +1,10 @@
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
class KT38234_Impl : KT38234_P1Protocol, KT38234_Base() {
|
||||
override fun foo(): Int = 566
|
||||
}
|
||||
|
||||
@Test fun testKT38234() {
|
||||
assertEquals(566, KT38234_Impl().callFoo())
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "KT38234_override.h"
|
||||
|
||||
@implementation KT38234_Base
|
||||
-(int)foo {
|
||||
return 1;
|
||||
}
|
||||
-(int)callFoo {
|
||||
return [self foo];
|
||||
}
|
||||
@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 @@
|
||||
void kt36766(void (^block)()) {}
|
||||
@@ -0,0 +1,6 @@
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testKt36766() {
|
||||
kt36766(null)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
extern BOOL deallocRetainReleaseDeallocated;
|
||||
|
||||
@interface DeallocRetainRelease : NSObject
|
||||
@end;
|
||||
|
||||
extern DeallocRetainRelease* globalDeallocRetainRelease;
|
||||
|
||||
@protocol WeakReference
|
||||
@required
|
||||
@property (weak) id referent;
|
||||
@end;
|
||||
|
||||
@interface ObjCWeakReference : NSObject <WeakReference>
|
||||
@property (weak) id referent;
|
||||
@end;
|
||||
|
||||
extern id <WeakReference> weakDeallocLoadWeak;
|
||||
extern BOOL deallocLoadWeakDeallocated;
|
||||
|
||||
@interface DeallocLoadWeak : NSObject
|
||||
-(void)checkWeak;
|
||||
@end;
|
||||
@@ -0,0 +1,118 @@
|
||||
import kotlin.native.ref.WeakReference
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
// Note: these tests rely on GC assertions: without the fix and the assertions it won't actually crash.
|
||||
// GC should fire an assertion if it obtains a reference to Kotlin object that is being (or has been) deallocated.
|
||||
|
||||
@Test
|
||||
fun testKT41811() {
|
||||
// Attempt to make the state predictable:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
deallocRetainReleaseDeallocated = false
|
||||
assertFalse(deallocRetainReleaseDeallocated)
|
||||
|
||||
createGarbageDeallocRetainRelease()
|
||||
|
||||
// Runs [DeallocRetainRelease dealloc]:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertTrue(deallocRetainReleaseDeallocated)
|
||||
|
||||
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
|
||||
private fun createGarbageDeallocRetainRelease() {
|
||||
autoreleasepool {
|
||||
object : DeallocRetainRelease() {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKT41811LoadWeak() {
|
||||
testKT41811LoadWeak(ObjCWeakReference())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKT41811LoadKotlinWeak() {
|
||||
val kotlinWeak = object : NSObject(), WeakReferenceProtocol {
|
||||
lateinit var weakReference: WeakReference<Any>
|
||||
|
||||
override fun referent(): Any? {
|
||||
return weakReference.value
|
||||
}
|
||||
|
||||
override fun setReferent(referent: Any?) {
|
||||
weakReference = WeakReference(referent!!)
|
||||
}
|
||||
}
|
||||
|
||||
testKT41811LoadWeak(kotlinWeak)
|
||||
}
|
||||
|
||||
private fun testKT41811LoadWeak(weakRef: WeakReferenceProtocol) {
|
||||
// Attempt to make the state predictable:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
deallocLoadWeakDeallocated = false
|
||||
assertFalse(deallocLoadWeakDeallocated)
|
||||
|
||||
createGarbageDeallocLoadWeak(weakRef)
|
||||
|
||||
// Runs [DeallocLoadWeak dealloc]:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertTrue(deallocLoadWeakDeallocated)
|
||||
|
||||
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
weakDeallocLoadWeak = null
|
||||
}
|
||||
|
||||
private fun createGarbageDeallocLoadWeak(weakRef: WeakReferenceProtocol) {
|
||||
autoreleasepool {
|
||||
val obj = object : DeallocLoadWeak() {}
|
||||
weakDeallocLoadWeak = weakRef.apply { referent = obj }
|
||||
obj.checkWeak()
|
||||
assertSame(obj, weakDeallocLoadWeak!!.referent)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKT41811WithGlobal() {
|
||||
// Attempt to make the state predictable:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
deallocRetainReleaseDeallocated = false
|
||||
assertFalse(deallocRetainReleaseDeallocated)
|
||||
|
||||
autoreleasepool {
|
||||
{
|
||||
globalDeallocRetainRelease = object: DeallocRetainRelease() {}
|
||||
}()
|
||||
}
|
||||
|
||||
assertFalse(deallocRetainReleaseDeallocated)
|
||||
|
||||
// Clean up local DeallocRetainRelease on Kotlin side
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertFalse(deallocRetainReleaseDeallocated)
|
||||
|
||||
// And drop the last reference to DeallocRetainRelease from ObjC global scope.
|
||||
globalDeallocRetainRelease = null
|
||||
|
||||
assertFalse(deallocRetainReleaseDeallocated)
|
||||
|
||||
// This will dispose `DeallocRetainRelease` on Kotlin side, which will cause `dealloc`
|
||||
// on ObjC side, which triggers `retain` and `release` of `self`. If these messages
|
||||
// were to reach Kotlin side, the `release` would have immediately scheduled the
|
||||
// second disposal of Kotlin object.
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertTrue(deallocRetainReleaseDeallocated)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#import "kt41811.h"
|
||||
#import "assert.h"
|
||||
|
||||
id retainObject = nil;
|
||||
BOOL deallocRetainReleaseDeallocated = NO;
|
||||
|
||||
@implementation DeallocRetainRelease
|
||||
-(void)dealloc {
|
||||
retainObject = self;
|
||||
assert(retainObject == self);
|
||||
retainObject = nil;
|
||||
|
||||
assert(!deallocRetainReleaseDeallocated);
|
||||
deallocRetainReleaseDeallocated = YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
DeallocRetainRelease* globalDeallocRetainRelease = nil;
|
||||
|
||||
@implementation ObjCWeakReference
|
||||
@end;
|
||||
|
||||
id <WeakReference> weakDeallocLoadWeak = nil;
|
||||
BOOL deallocLoadWeakDeallocated = NO;
|
||||
|
||||
@implementation DeallocLoadWeak
|
||||
-(void)checkWeak {
|
||||
assert(weakDeallocLoadWeak != nil);
|
||||
assert(weakDeallocLoadWeak.referent == self);
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
assert(weakDeallocLoadWeak != nil);
|
||||
assert(weakDeallocLoadWeak.referent == nil);
|
||||
|
||||
assert(!deallocLoadWeakDeallocated);
|
||||
deallocLoadWeakDeallocated = YES;
|
||||
}
|
||||
@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,50 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testObjCWeakRef() {
|
||||
val deallocListener = DeallocListener()
|
||||
assertFalse(deallocListener.deallocated)
|
||||
|
||||
// [deallocListener.deallocExecutorIsNil()] calls deallocExecutor getter, which retains the result and either
|
||||
// puts it to autoreleasepool or releases it immediately (Obj-C ARC optimization). It seems to depend on the platform.
|
||||
// Wrap the call to autoreleasepool to ensure the object will be released:
|
||||
autoreleasepool {
|
||||
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,22 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testOverrideInit1() {
|
||||
assertEquals(42, (TestOverrideInitImpl1.createWithValue(42) as TestOverrideInitImpl1).value)
|
||||
}
|
||||
|
||||
private class TestOverrideInitImpl1 @OverrideInit constructor(val value: Int) : TestOverrideInit(value) {
|
||||
companion object : TestOverrideInitMeta()
|
||||
}
|
||||
|
||||
// See https://youtrack.jetbrains.com/issue/KT-41910
|
||||
@Test fun testOverrideInitWithDefaultArguments() {
|
||||
assertEquals(42, (TestOverrideInitImpl2.createWithValue(42) as TestOverrideInitImpl2).value)
|
||||
assertEquals(123, TestOverrideInitImpl2(123).value)
|
||||
assertEquals(17, TestOverrideInitImpl2().value)
|
||||
}
|
||||
|
||||
private class TestOverrideInitImpl2 @OverrideInit constructor(val value: Int = 17) : 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,31 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
private class NSObjectImpl : NSObject() {
|
||||
var x = 111
|
||||
}
|
||||
|
||||
// Also see counterpart interop/objc/illegal_sharing.kt
|
||||
@Test fun testSharing() = withWorker {
|
||||
val obj = NSObjectImpl()
|
||||
val array = nsArrayOf(obj)
|
||||
|
||||
assertFalse(obj.isFrozen)
|
||||
|
||||
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 <Foundation/NSString.h>
|
||||
|
||||
struct CStructWithNSObjects {
|
||||
id any;
|
||||
|
||||
NSString* nsString;
|
||||
NSString* _Nonnull nonNullString;
|
||||
NSObject* object;
|
||||
|
||||
NSArray* array;
|
||||
NSMutableArray* mutableArray;
|
||||
|
||||
NSSet* set;
|
||||
|
||||
NSDictionary* dictionary;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
private class NSObjectSubClass : NSObject() {
|
||||
val x = 111
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStructWithNSObject() {
|
||||
memScoped {
|
||||
val struct = alloc<CStructWithNSObjects>()
|
||||
|
||||
struct.any = 5
|
||||
assertEquals(5, struct.any)
|
||||
struct.any = null
|
||||
assertEquals(null, struct.any)
|
||||
|
||||
struct.nsString = "hello"
|
||||
assertEquals("hello", struct.nsString)
|
||||
struct.nsString = null
|
||||
assertEquals(null, struct.nsString)
|
||||
|
||||
struct.nonNullString = "world"
|
||||
assertEquals("world", struct.nonNullString)
|
||||
|
||||
struct.`object` = NSObjectSubClass()
|
||||
assertEquals(111, (struct.`object` as NSObjectSubClass).x)
|
||||
struct.`object` = null
|
||||
assertEquals(null, struct.`object`)
|
||||
|
||||
struct.array = null
|
||||
assertEquals(null, struct.array)
|
||||
struct.array = listOf(1, 2, 3)
|
||||
assertEquals(listOf(1, 2, 3), struct.array)
|
||||
|
||||
struct.set = null
|
||||
assertEquals(null, struct.set)
|
||||
struct.set = setOf("hello", "world")
|
||||
assertEquals(setOf("hello", "world"), struct.set)
|
||||
|
||||
struct.dictionary = null
|
||||
assertEquals(null, struct.dictionary)
|
||||
struct.dictionary = mapOf("k1" to "v1", "k2" to "v2")
|
||||
assertEquals(mapOf("k1" to "v1", "k2" to "v2"), struct.dictionary)
|
||||
|
||||
struct.mutableArray = null
|
||||
assertEquals(null, struct.mutableArray)
|
||||
struct.mutableArray = mutableListOf(1, 2)
|
||||
struct.mutableArray!! += 3
|
||||
assertEquals(mutableListOf(1, 2, 3), struct.mutableArray)
|
||||
|
||||
// Check that subtyping via Nothing-returning functions does not break compiler.
|
||||
assertFailsWith<NotImplementedError> {
|
||||
struct.any = TODO()
|
||||
struct.nsString = TODO()
|
||||
struct.nonNullString = TODO()
|
||||
struct.`object` = TODO()
|
||||
struct.array = TODO()
|
||||
struct.set = TODO()
|
||||
struct.dictionary = TODO()
|
||||
struct.mutableArray = TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface WeakRefHolder : NSObject
|
||||
@property (weak) id obj;
|
||||
-(void)loadManyTimes;
|
||||
@end;
|
||||
@@ -0,0 +1,25 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test
|
||||
fun testTryRetainGC() {
|
||||
kotlin.native.internal.GC.collect()
|
||||
val weakRefHolder = WeakRefHolder()
|
||||
createGarbageNSObjects(weakRefHolder)
|
||||
weakRefHolder.obj = object : NSObject() {}
|
||||
// Loading weak ref takes a lock. If K/N runtime runs GC while the lock is taken,
|
||||
// then it releases garbage objects and thus Obj-C runtime might take a recursive lock
|
||||
// and abort in _os_unfair_lock_recursive_abort.
|
||||
weakRefHolder.loadManyTimes()
|
||||
}
|
||||
|
||||
private fun createGarbageNSObjects(weakRefHolder: WeakRefHolder) {
|
||||
autoreleasepool {
|
||||
repeat(100) {
|
||||
// Assigning the object to a weak reference so Obj-C would take a lock when deallocating it:
|
||||
weakRefHolder.obj = NSObject()
|
||||
}
|
||||
weakRefHolder.obj = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#import "tryRetainGC.h"
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
@implementation WeakRefHolder
|
||||
-(void)loadManyTimes {
|
||||
NSMutableArray* array = [NSMutableArray new];
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
[array addObject:self.obj];
|
||||
}
|
||||
}
|
||||
@end;
|
||||
@@ -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