Adding upstream version 0.0~git20250520.a1d9079+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
590ac7ff5f
commit
20149b7f3a
456 changed files with 70406 additions and 0 deletions
197
bind/objc/SeqBench.m
Normal file
197
bind/objc/SeqBench.m
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "benchmark/Benchmark.h"
|
||||
|
||||
@interface AnI : NSObject <BenchmarkI> {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation AnI
|
||||
- (void)f {
|
||||
}
|
||||
@end
|
||||
|
||||
@interface Benchmarks : NSObject <BenchmarkBenchmarks> {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Benchmarks
|
||||
- (void)manyargs:(long)p0 p1:(long)p1 p2:(long)p2 p3:(long)p3 p4:(long)p4 p5:(long)p5 p6:(long)p6 p7:(long)p7 p8:(long)p8 p9:(long)p9 {
|
||||
}
|
||||
|
||||
- (id<BenchmarkI>)newI {
|
||||
return [[AnI alloc] init];
|
||||
}
|
||||
|
||||
- (void)noargs {
|
||||
}
|
||||
|
||||
- (void)onearg:(long)p0 {
|
||||
}
|
||||
|
||||
- (long)oneret {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)ref:(id<BenchmarkI>)p0 {
|
||||
}
|
||||
|
||||
- (void)slice:(NSData*)p0 {
|
||||
}
|
||||
|
||||
- (void)string:(NSString*)p0 {
|
||||
}
|
||||
|
||||
- (NSString*)stringRetLong {
|
||||
return BenchmarkLongString;
|
||||
}
|
||||
|
||||
- (NSString*)stringRetShort {
|
||||
return BenchmarkShortString;
|
||||
}
|
||||
|
||||
- (void (^)(void))lookupBenchmark:(NSString *)name {
|
||||
if ([name isEqualToString:@"Empty"]) {
|
||||
return ^() {
|
||||
};
|
||||
} else if ([name isEqualToString:@"Noargs"]) {
|
||||
return ^() {
|
||||
BenchmarkNoargs();
|
||||
};
|
||||
} else if ([name isEqualToString:@"Onearg"]) {
|
||||
return ^() {
|
||||
BenchmarkOnearg(0);
|
||||
};
|
||||
} else if ([name isEqualToString:@"Manyargs"]) {
|
||||
return ^() {
|
||||
BenchmarkManyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
};
|
||||
} else if ([name isEqualToString:@"Oneret"]) {
|
||||
return ^() {
|
||||
BenchmarkOneret();
|
||||
};
|
||||
} else if ([name isEqualToString:@"Refforeign"]) {
|
||||
id<BenchmarkI> objcRef = [[AnI alloc] init];
|
||||
return ^() {
|
||||
BenchmarkRef(objcRef);
|
||||
};
|
||||
} else if ([name isEqualToString:@"Refgo"]) {
|
||||
id<BenchmarkI> goRef = BenchmarkNewI();
|
||||
return ^() {
|
||||
BenchmarkRef(goRef);
|
||||
};
|
||||
} else if ([name isEqualToString:@"StringShort"]) {
|
||||
return ^() {
|
||||
BenchmarkString(BenchmarkShortString);
|
||||
};
|
||||
} else if ([name isEqualToString:@"StringLong"]) {
|
||||
return ^() {
|
||||
BenchmarkString(BenchmarkLongString);
|
||||
};
|
||||
} else if ([name isEqualToString:@"StringShortUnicode"]) {
|
||||
return ^() {
|
||||
BenchmarkString(BenchmarkShortStringUnicode);
|
||||
};
|
||||
} else if ([name isEqualToString:@"StringLongUnicode"]) {
|
||||
return ^() {
|
||||
BenchmarkString(BenchmarkLongStringUnicode);
|
||||
};
|
||||
} else if ([name isEqualToString:@"StringRetShort"]) {
|
||||
return ^() {
|
||||
BenchmarkStringRetShort();
|
||||
};
|
||||
} else if ([name isEqualToString:@"StringRetLong"]) {
|
||||
return ^() {
|
||||
BenchmarkStringRetLong();
|
||||
};
|
||||
} else if ([name isEqualToString:@"SliceShort"]) {
|
||||
NSData *s = [Benchmark shortSlice];
|
||||
return ^() {
|
||||
BenchmarkSlice(s);
|
||||
};
|
||||
} else if ([name isEqualToString:@"SliceLong"]) {
|
||||
NSData *s = [Benchmark longSlice];
|
||||
return ^() {
|
||||
BenchmarkSlice(s);
|
||||
};
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)run:(NSString*)name n:(long)n {
|
||||
void (^bench)(void) = [self lookupBenchmark:name];
|
||||
if (bench == nil) {
|
||||
NSLog(@"Error: no such benchmark: %@", name);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
bench();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)runDirect:(NSString*)name n:(long)n {
|
||||
void (^bench)(void) = [self lookupBenchmark:name];
|
||||
if (bench == nil) {
|
||||
NSLog(@"Error: no such benchmark: %@", name);
|
||||
return;
|
||||
}
|
||||
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
for (int i = 0; i < n; i++) {
|
||||
bench();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface benchmarks : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation benchmarks
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
self.continueAfterFailure = NO;
|
||||
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
|
||||
[[[XCUIApplication alloc] init] launch];
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testBenchmark {
|
||||
// Long running unit tests seem to hang. Use an XCTestExpectation and run the Go
|
||||
// benchmark suite on a GCD thread.
|
||||
XCTestExpectation *expectation =
|
||||
[self expectationWithDescription:@"Benchmark"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
Benchmarks *b = [[Benchmarks alloc] init];
|
||||
BenchmarkRunBenchmarks(b);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:5*60.0 handler:^(NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Timeout Error: %@", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
@end
|
22
bind/objc/SeqCustom.m
Normal file
22
bind/objc/SeqCustom.m
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
@import Testpkg;
|
||||
|
||||
@interface tests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation tests
|
||||
|
||||
- (void)testBasics {
|
||||
CustomTestpkgHi();
|
||||
}
|
||||
|
||||
@end
|
481
bind/objc/SeqTest.m
Normal file
481
bind/objc/SeqTest.m
Normal file
|
@ -0,0 +1,481 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "testpkg/Testpkg.h"
|
||||
|
||||
// Objective-C implementation of testpkg.I2.
|
||||
@interface Number : NSObject <TestpkgI2> {
|
||||
}
|
||||
@property int32_t value;
|
||||
|
||||
// TODO(hyangah): error:error is not good.
|
||||
- (BOOL)error:(BOOL)e error:(NSError **)error;
|
||||
- (int64_t)times:(int32_t)v;
|
||||
@end
|
||||
|
||||
// numI is incremented when the first numI objective-C implementation is
|
||||
// deallocated.
|
||||
static int numI = 0;
|
||||
|
||||
@implementation Number {
|
||||
}
|
||||
@synthesize value;
|
||||
|
||||
- (NSString *)stringError:(NSString *)s
|
||||
error:(NSError **)error {
|
||||
if ([s isEqualToString:@"number"]) {
|
||||
return @"OK";
|
||||
}
|
||||
*error = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:@{NSLocalizedDescriptionKey: @"NumberError"}];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (BOOL)error:(BOOL)triggerError error:(NSError **)error {
|
||||
if (!triggerError) {
|
||||
return YES;
|
||||
}
|
||||
if (error != NULL) {
|
||||
*error = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:NULL];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (int64_t)times:(int32_t)v {
|
||||
return v * value;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.value == 0) {
|
||||
numI++;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
// Objective-C implementation of testpkg.NullTest.
|
||||
@interface NullTest : NSObject <TestpkgNullTest> {
|
||||
}
|
||||
|
||||
- (TestpkgNullTest *)null;
|
||||
@end
|
||||
|
||||
@implementation NullTest {
|
||||
}
|
||||
|
||||
- (TestpkgNullTest *)null {
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
// Objective-C implementation of testpkg.InterfaceDupper.
|
||||
@interface IDup : NSObject <TestpkgInterfaceDupper> {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation IDup {
|
||||
}
|
||||
|
||||
- (id<TestpkgInterface>)iDup:(id<TestpkgInterface>)i {
|
||||
return i;
|
||||
}
|
||||
@end
|
||||
|
||||
// Objective-C implementation of testpkg.ConcreteDupper.
|
||||
@interface CDup : NSObject <TestpkgConcreteDupper> {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CDup {
|
||||
}
|
||||
|
||||
- (TestpkgConcrete *)cDup:(TestpkgConcrete *)c {
|
||||
return c;
|
||||
}
|
||||
@end
|
||||
|
||||
// Objective-C implementation of testpkg.EmptyThrower.
|
||||
@interface EmptyErrorer: NSObject <TestpkgEmptyErrorer> {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation EmptyErrorer {
|
||||
}
|
||||
|
||||
- (BOOL)emptyError:(NSError **)error {
|
||||
*error = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:NULL];
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface tests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation tests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testBasics {
|
||||
TestpkgHi();
|
||||
|
||||
TestpkgInt(42);
|
||||
}
|
||||
|
||||
- (void)testAdd {
|
||||
int64_t sum = TestpkgAdd(31, 21);
|
||||
XCTAssertEqual(sum, 52, @"TestpkgSum(31, 21) = %lld, want 52\n", sum);
|
||||
}
|
||||
|
||||
- (void)testHello:(NSString *)input {
|
||||
NSString *got = TestpkgAppendHello(input);
|
||||
NSString *want = [NSString stringWithFormat:@"Hello, %@!", input];
|
||||
XCTAssertEqualObjects(got, want, @"want %@\nTestpkgHello(%@)= %@", want, input, got);
|
||||
}
|
||||
|
||||
- (void)testHellos {
|
||||
[self testHello:@"세계"]; // korean, utf-8, world.
|
||||
unichar t[] = {
|
||||
0xD83D, 0xDCA9,
|
||||
}; // utf-16, pile of poo.
|
||||
[self testHello:[NSString stringWithCharacters:t length:2]];
|
||||
}
|
||||
|
||||
- (void)testString {
|
||||
NSString *input = @"";
|
||||
NSString *got = TestpkgStrDup(input);
|
||||
XCTAssertEqualObjects(got, input, @"want %@\nTestpkgEcho(%@)= %@", input, input, got);
|
||||
|
||||
input = @"FOO";
|
||||
got = TestpkgStrDup(input);
|
||||
XCTAssertEqualObjects(got, input, @"want %@\nTestpkgEcho(%@)= %@", input, input, got);
|
||||
}
|
||||
|
||||
- (void)testStruct {
|
||||
TestpkgS2 *s = TestpkgNewS2(10.0, 100.0);
|
||||
XCTAssertNotNil(s, @"TestpkgNewS2 returned NULL");
|
||||
|
||||
double x = [s x];
|
||||
double y = [s y];
|
||||
double sum = [s sum];
|
||||
XCTAssertTrue(x == 10.0 && y == 100.0 && sum == 110.0,
|
||||
@"TestpkgS2(10.0, 100.0).X=%f Y=%f SUM=%f; want 10, 100, 110", x, y, sum);
|
||||
|
||||
double sum2 = TestpkgCallSSum(s);
|
||||
XCTAssertEqual(sum, sum2, @"TestpkgCallSSum(s)=%f; want %f as returned by s.Sum", sum2, sum);
|
||||
|
||||
[s setX:7];
|
||||
[s setY:70];
|
||||
x = [s x];
|
||||
y = [s y];
|
||||
sum = [s sum];
|
||||
XCTAssertTrue(x == 7 && y == 70 && sum == 77,
|
||||
@"TestpkgS2(7, 70).X=%f Y=%f SUM=%f; want 7, 70, 77", x, y, sum);
|
||||
|
||||
NSString *first = @"trytwotested";
|
||||
NSString *second = @"test";
|
||||
NSString *got = [s tryTwoStrings:first second:second];
|
||||
NSString *want = [first stringByAppendingString:second];
|
||||
XCTAssertEqualObjects(got, want, @"TestpkgS_TryTwoStrings(%@, %@)= %@; want %@", first, second, got, want);
|
||||
}
|
||||
|
||||
- (void)testCollectS {
|
||||
@autoreleasepool {
|
||||
[self testStruct];
|
||||
}
|
||||
|
||||
TestpkgGC();
|
||||
long numS = TestpkgCollectS2(
|
||||
1, 10); // within 10 seconds, collect the S used in testStruct.
|
||||
XCTAssertEqual(numS, 1, @"%ld S objects were collected; S used in testStruct is supposed to "
|
||||
@"be collected.",
|
||||
numS);
|
||||
}
|
||||
- (void)testBytesAppend {
|
||||
NSString *a = @"Foo";
|
||||
NSString *b = @"Bar";
|
||||
NSData *data_a = [a dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *data_b = [b dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *gotData = TestpkgBytesAppend(data_a, data_b);
|
||||
NSString *got = [[NSString alloc] initWithData:gotData encoding:NSUTF8StringEncoding];
|
||||
NSString *want = [a stringByAppendingString:b];
|
||||
XCTAssertEqualObjects(got, want, @"want %@\nTestpkgBytesAppend(%@, %@) = %@", want, a, b, got);
|
||||
}
|
||||
|
||||
- (void)testInterface {
|
||||
// Test Go object implementing testpkg.I is handled correctly.
|
||||
id<TestpkgI2> goObj = TestpkgNewI();
|
||||
int64_t got = [goObj times:10];
|
||||
XCTAssertEqual(got, 100, @"TestpkgNewI().times(10) = %lld; want %d", got, 100);
|
||||
int32_t key = -1;
|
||||
TestpkgRegisterI(key, goObj);
|
||||
int64_t got2 = TestpkgMultiply(key, 10);
|
||||
XCTAssertEqual(got, got2, @"TestpkgMultiply(10 * 10) = %lld; want %lld", got2, got);
|
||||
TestpkgUnregisterI(key);
|
||||
|
||||
// Test Objective-C objects implementing testpkg.I is handled correctly.
|
||||
@autoreleasepool {
|
||||
for (int32_t i = 0; i < 10; i++) {
|
||||
Number *num = [[Number alloc] init];
|
||||
num.value = i;
|
||||
TestpkgRegisterI(i, num);
|
||||
}
|
||||
TestpkgGC();
|
||||
}
|
||||
|
||||
// Registered Objective-C objects are pinned on Go side which must
|
||||
// prevent deallocation from Objective-C.
|
||||
for (int32_t i = 0; i < 10; i++) {
|
||||
int64_t got = TestpkgMultiply(i, 2);
|
||||
XCTAssertEqual(got, i * 2,@"TestpkgMultiply(%d, 2) = %lld; want %d", i, got, i * 2);
|
||||
TestpkgUnregisterI(i);
|
||||
TestpkgGC();
|
||||
}
|
||||
// Unregistered all Objective-C objects.
|
||||
}
|
||||
|
||||
- (void)testCollectI {
|
||||
@autoreleasepool {
|
||||
[self testInterface];
|
||||
}
|
||||
XCTAssertEqual(numI, 1, @"%d I objects were collected; I used in testInterface is supposed "
|
||||
@"to be collected.", numI);
|
||||
}
|
||||
|
||||
- (void)testConst {
|
||||
XCTAssertEqualObjects(TestpkgAString, @"a string", @"TestpkgAString = %@, want 'a string'", TestpkgAString);
|
||||
XCTAssertEqual(TestpkgAnInt, 7, @"TestpkgAnInt = %lld, want 7", TestpkgAnInt);
|
||||
XCTAssertTrue(ABS(TestpkgAFloat - 0.12345) < 0.0001, @"TestpkgAFloat = %f, want 0.12345", TestpkgAFloat);
|
||||
XCTAssertTrue(TestpkgABool == YES, @"TestpkgABool = %@, want YES", TestpkgAFloat ? @"YES" : @"NO");
|
||||
XCTAssertEqual(TestpkgMinInt32, INT32_MIN, @"TestpkgMinInt32 = %d, want %d", TestpkgMinInt32, INT32_MIN);
|
||||
XCTAssertEqual(TestpkgMaxInt32, INT32_MAX, @"TestpkgMaxInt32 = %d, want %d", TestpkgMaxInt32, INT32_MAX);
|
||||
XCTAssertEqual(TestpkgMinInt64, INT64_MIN, @"TestpkgMinInt64 = %lld, want %lld", TestpkgMinInt64, INT64_MIN);
|
||||
XCTAssertEqual(TestpkgMaxInt64, INT64_MAX, @"TestpkgMaxInt64 = %lld, want %lld", TestpkgMaxInt64, INT64_MAX);
|
||||
XCTAssertTrue(ABS(TestpkgSmallestNonzeroFloat64 -
|
||||
4.940656458412465441765687928682213723651e-324) < 1e-323, @"TestpkgSmallestNonzeroFloat64 = %f, want %f",
|
||||
TestpkgSmallestNonzeroFloat64,
|
||||
4.940656458412465441765687928682213723651e-324);
|
||||
XCTAssertTrue(ABS(TestpkgMaxFloat64 -
|
||||
1.797693134862315708145274237317043567981e+308) < 0.0001, @"TestpkgMaxFloat64 = %f, want %f", TestpkgMaxFloat64,
|
||||
1.797693134862315708145274237317043567981e+308);
|
||||
XCTAssertTrue(ABS(TestpkgSmallestNonzeroFloat32 -
|
||||
1.401298464324817070923729583289916131280e-45) < 1e-44, @"TestpkgSmallestNonzeroFloat32 = %f, want %f",
|
||||
TestpkgSmallestNonzeroFloat32,
|
||||
1.401298464324817070923729583289916131280e-45);
|
||||
XCTAssertTrue(ABS(TestpkgMaxFloat32 - 3.40282346638528859811704183484516925440e+38) < 0.0001,
|
||||
@"TestpkgMaxFloat32 = %f, want %f", TestpkgMaxFloat32, 3.40282346638528859811704183484516925440e+38);
|
||||
XCTAssertTrue(ABS(TestpkgLog2E - 1 / 0.693147180559945309417232121458176568075500134360255254120680009) < 0.0001,
|
||||
@"TestpkgLog2E = %f, want %f", TestpkgLog2E, 1 / 0.693147180559945309417232121458176568075500134360255254120680009);
|
||||
}
|
||||
|
||||
- (void)testIssue12307 {
|
||||
Number *num = [[Number alloc] init];
|
||||
num.value = 1024;
|
||||
NSError *error;
|
||||
XCTAssertFalse(TestpkgCallIError(num, YES, &error), @"TestpkgCallIError(Number, YES) succeeded; want error");
|
||||
NSError *error2;
|
||||
XCTAssertTrue(TestpkgCallIError(num, NO, &error2), @"TestpkgCallIError(Number, NO) failed(%@); want success", error2);
|
||||
}
|
||||
|
||||
- (void)testErrorField {
|
||||
NSString *wantMsg = @"an error message";
|
||||
NSError *want = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:@{NSLocalizedDescriptionKey: wantMsg}];
|
||||
TestpkgNode *n = TestpkgNewNode(@"ErrTest");
|
||||
n.err = want;
|
||||
NSError *got = n.err;
|
||||
XCTAssertEqual(got, want, @"got different objects after roundtrip");
|
||||
NSString *gotMsg = TestpkgErrorMessage(want);
|
||||
XCTAssertEqualObjects(gotMsg, wantMsg, @"err = %@, want %@", gotMsg, wantMsg);
|
||||
}
|
||||
|
||||
- (void)testErrorDup {
|
||||
NSError *err = Testpkg.globalErr;
|
||||
XCTAssertTrue(TestpkgIsGlobalErr(err), @"A Go error must preserve its identity across the boundary");
|
||||
XCTAssertEqualObjects([err localizedDescription], @"global err", "A Go error message must be preserved");
|
||||
}
|
||||
|
||||
- (void)testVar {
|
||||
NSString *s = Testpkg.stringVar;
|
||||
XCTAssertEqualObjects(s, @"a string var", @"Testpkg.StringVar = %@, want 'a string var'", s);
|
||||
s = @"a new string var";
|
||||
Testpkg.stringVar = s;
|
||||
NSString *s2 = Testpkg.stringVar;
|
||||
XCTAssertEqualObjects(s2, s, @"Testpkg.stringVar = %@, want %@", s2, s);
|
||||
|
||||
int64_t i = Testpkg.intVar;
|
||||
XCTAssertEqual(i, 77, @"Testpkg.intVar = %lld, want 77", i);
|
||||
Testpkg.intVar = 777;
|
||||
i = Testpkg.intVar;
|
||||
XCTAssertEqual(i, 777, @"Testpkg.intVar = %lld, want 777", i);
|
||||
[Testpkg setIntVar:7777];
|
||||
i = [Testpkg intVar];
|
||||
XCTAssertEqual(i, 7777, @"Testpkg.intVar = %lld, want 7777", i);
|
||||
|
||||
TestpkgNode *n0 = Testpkg.nodeVar;
|
||||
XCTAssertEqualObjects(n0.v, @"a struct var", @"Testpkg.NodeVar = %@, want 'a struct var'", n0.v);
|
||||
TestpkgNode *n1 = TestpkgNewNode(@"a new struct var");
|
||||
Testpkg.nodeVar = n1;
|
||||
TestpkgNode *n2 = Testpkg.nodeVar;
|
||||
XCTAssertEqualObjects(n2.v, @"a new struct var", @"Testpkg.NodeVar = %@, want 'a new struct var'", n2.v);
|
||||
|
||||
Number *num = [[Number alloc] init];
|
||||
num.value = 12345;
|
||||
Testpkg.interfaceVar2 = num;
|
||||
id<TestpkgI2> iface = Testpkg.interfaceVar2;
|
||||
int64_t x = [iface times:10];
|
||||
int64_t y = [num times:10];
|
||||
XCTAssertEqual(x, y, @"Testpkg.InterfaceVar2 Times 10 = %lld, want %lld", x, y);
|
||||
}
|
||||
|
||||
- (void)testIssue12403 {
|
||||
Number *num = [[Number alloc] init];
|
||||
num.value = 1024;
|
||||
|
||||
NSError *error;
|
||||
NSString *ret = TestpkgCallIStringError(num, @"alphabet", &error);
|
||||
XCTAssertNil(ret, @"TestpkgCallIStringError(Number, 'alphabet') succeeded(%@); want error", ret);
|
||||
NSString *desc = [error localizedDescription];
|
||||
XCTAssertEqualObjects(desc, @"NumberError", @"TestpkgCallIStringError(Number, 'alphabet') returned unexpected error message %@", desc);
|
||||
NSError *error2;
|
||||
NSString *ret2 = TestpkgCallIStringError(num, @"number", &error2);
|
||||
XCTAssertNotNil(ret2, @"TestpkgCallIStringError(Number, 'number') failed(%@); want success", error2);
|
||||
XCTAssertEqualObjects(ret2, @"OK", @"TestpkgCallIStringError(Number, 'number') returned unexpected results %@", ret2);
|
||||
}
|
||||
|
||||
- (void)testStrDup:(NSString *)want {
|
||||
NSString *got = TestpkgStrDup(want);
|
||||
XCTAssertEqualObjects(want, got, @"StrDup returned %@; expected %@", got, want);
|
||||
}
|
||||
|
||||
- (void)testUnicodeStrings {
|
||||
[self testStrDup:@"abcxyz09{}"];
|
||||
[self testStrDup:@"Hello, 世界"];
|
||||
[self testStrDup:@"\uffff\U00010000\U00010001\U00012345\U0010ffff"];
|
||||
}
|
||||
|
||||
- (void)testByteArrayRead {
|
||||
NSData *arr = [NSMutableData dataWithLength:8];
|
||||
long n;
|
||||
XCTAssertTrue(TestpkgReadIntoByteArray(arr, &n, nil), @"ReadIntoByteArray failed");
|
||||
XCTAssertEqual(n, 8, @"ReadIntoByteArray wrote %ld bytes, expected %d", n, 8);
|
||||
const uint8_t *b = [arr bytes];
|
||||
for (int i = 0; i < [arr length]; i++) {
|
||||
XCTAssertEqual(b[i], i, @"ReadIntoByteArray wrote %d at %d; expected %d", b[i], i, i);
|
||||
}
|
||||
// Test that immutable data cannot be changed from Go
|
||||
const uint8_t buf[] = {42};
|
||||
arr = [NSData dataWithBytes:buf length:1];
|
||||
XCTAssertTrue(TestpkgReadIntoByteArray(arr, &n, nil), @"ReadIntoByteArray failed");
|
||||
XCTAssertEqual(n, 1, @"ReadIntoByteArray wrote %ld bytes, expected %d", n, 8);
|
||||
b = [arr bytes];
|
||||
XCTAssertEqual(b[0], 42, @"ReadIntoByteArray wrote to an immutable NSData; expected no change");
|
||||
}
|
||||
|
||||
- (void)testNilField {
|
||||
TestpkgNullFieldStruct *s = TestpkgNewNullFieldStruct();
|
||||
XCTAssertNil([s f], @"NullFieldStruct has non-nil field; expected nil");
|
||||
}
|
||||
|
||||
- (void)testNullReferences {
|
||||
NullTest *t = [[NullTest alloc] init];
|
||||
XCTAssertTrue(TestpkgCallWithNull(nil, t), @"Testpkg.CallWithNull failed");
|
||||
id<TestpkgI> i = TestpkgNewNullInterface();
|
||||
XCTAssertNil(i, @"NewNullInterface() returned %p; expected nil", i);
|
||||
TestpkgS *s = TestpkgNewNullStruct();
|
||||
XCTAssertNil(s, @"NewNullStruct() returned %p; expected nil", s);
|
||||
TestpkgIssue20330 *nullArger = TestpkgNewIssue20330();
|
||||
XCTAssertTrue([nullArger callWithNull:nil], @"Issue20330.CallWithNull failed");
|
||||
}
|
||||
|
||||
- (void)testReturnsError {
|
||||
NSError *error;
|
||||
NSString *value = TestpkgReturnsError(TRUE, &error);
|
||||
NSString *got = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
|
||||
NSString *want = @"Error";
|
||||
XCTAssertEqualObjects(got, want, @"want %@\nTestpkgReturnsError(TRUE) = (%@, %@)", want, value, got);
|
||||
}
|
||||
|
||||
- (void)testImportedPkg {
|
||||
XCTAssertEqualObjects(SecondpkgHelloString, SecondpkgHello(), @"imported string should match");
|
||||
id<SecondpkgI> i = TestpkgNewImportedI();
|
||||
SecondpkgS *s = TestpkgNewImportedS();
|
||||
XCTAssertEqual(8, [i f:8], @"numbers should match");
|
||||
XCTAssertEqual(8, [s f:8], @"numbers should match");
|
||||
i = TestpkgWithImportedI(i);
|
||||
s = TestpkgWithImportedS(s);
|
||||
i = [Testpkg importedVarI];
|
||||
s = [Testpkg importedVarS];
|
||||
[Testpkg setImportedVarI:i];
|
||||
[Testpkg setImportedVarS:s];
|
||||
TestpkgImportedFields *fields = TestpkgNewImportedFields();
|
||||
i = [fields i];
|
||||
s = [fields s];
|
||||
[fields setI:i];
|
||||
[fields setS:s];
|
||||
}
|
||||
|
||||
- (void)testRoundTripEquality {
|
||||
Number *want = [[Number alloc] init];
|
||||
Number *got = (Number *)TestpkgI2Dup(want);
|
||||
XCTAssertEqual(got, want, @"ObjC object passed through Go should not be wrapped");
|
||||
|
||||
IDup *idup = [[IDup alloc] init];
|
||||
XCTAssertTrue(TestpkgCallIDupper(idup), @"Go interface passed through ObjC should not be wrapped");
|
||||
CDup *cdup = [[CDup alloc] init];
|
||||
XCTAssertTrue(TestpkgCallCDupper(cdup), @"Go struct passed through ObjC should not be wrapped");
|
||||
}
|
||||
|
||||
- (void)testEmptyError {
|
||||
NSError *error;
|
||||
XCTAssertFalse(TestpkgEmptyError(&error), @"GoTestpkgEmptyError succeeded; want error");
|
||||
XCTAssertNotNil(error, @"TestpkgEmptyError returned nil error");
|
||||
id<TestpkgEmptyErrorer> empty = [[EmptyErrorer alloc] init];
|
||||
XCTAssertFalse(TestpkgCallEmptyError(empty, &error), @"TestpkgCallEmptyError succeeded; want error");
|
||||
XCTAssertNotNil(error, @"TestpkgCallEmptyError returned nil error");
|
||||
}
|
||||
|
||||
- (void)testSIGPIPE {
|
||||
TestpkgTestSIGPIPE();
|
||||
}
|
||||
|
||||
- (void)testTags {
|
||||
XCTAssertEqual(42, TestpkgTaggedConst, @"Tagged const must exist");
|
||||
}
|
||||
|
||||
- (void)testConstructors {
|
||||
id<TestpkgInterface> i = [[TestpkgConcrete alloc] init];
|
||||
[i f];
|
||||
|
||||
TestpkgS2 *s = [[TestpkgS2 alloc] init:1 y:2];
|
||||
XCTAssertEqual(3.0, [s sum]);
|
||||
XCTAssertEqualObjects(@"gostring", [s tryTwoStrings:@"go" second:@"string"]);
|
||||
|
||||
TestpkgS3 *s3 __attribute__((unused)) = [[TestpkgS3 alloc] init];
|
||||
|
||||
TestpkgS4 *s4 = [[TestpkgS4 alloc] initWithInt:123];
|
||||
XCTAssertEqual(123, s4.i);
|
||||
|
||||
s4 = [[TestpkgS4 alloc] initWithFloat: 123.456];
|
||||
XCTAssertEqual(123, s4.i);
|
||||
|
||||
s4 = [[TestpkgS4 alloc] initWithBoolAndError: false];
|
||||
XCTAssertEqual(0, s4.i);
|
||||
|
||||
s4 = [[TestpkgS4 alloc] initWithBoolAndError: true];
|
||||
XCTAssertEqual(s4, NULL);
|
||||
}
|
||||
|
||||
@end
|
98
bind/objc/SeqWrappers.m
Normal file
98
bind/objc/SeqWrappers.m
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
@import ObjectiveC.message;
|
||||
@import Foundation;
|
||||
@import XCTest;
|
||||
@import Objcpkg;
|
||||
|
||||
@interface TestNSObject : NSObject
|
||||
|
||||
- (NSString *)description;
|
||||
- (NSString *)super_description;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TestNSObject
|
||||
|
||||
- (NSString *)description {
|
||||
return @"hej";
|
||||
}
|
||||
|
||||
- (NSString *)super_description {
|
||||
return [super description];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface wrappers : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation wrappers
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testFunction {
|
||||
ObjcpkgFunc();
|
||||
}
|
||||
|
||||
- (void)testMethod {
|
||||
ObjcpkgMethod();
|
||||
}
|
||||
|
||||
- (void)testNew {
|
||||
ObjcpkgNew();
|
||||
}
|
||||
|
||||
- (void)testError {
|
||||
ObjcpkgError();
|
||||
}
|
||||
|
||||
- (void)testClass {
|
||||
ObjcpkgGoNSDate *d = [[ObjcpkgGoNSDate alloc] init];
|
||||
NSString *desc = [d description];
|
||||
XCTAssertEqual(d, [d getSelf], "GoNSDate self not identical");
|
||||
XCTAssertEqual(ObjcpkgHash, [d hash], "GoNSDate hash not identical");
|
||||
XCTAssertTrue([desc isEqualToString:ObjcpkgDescriptionStr], "GoNSDate description mismatch: %@", desc);
|
||||
ObjcpkgGoUIResponder *resp = [[ObjcpkgGoUIResponder alloc] init];
|
||||
[resp pressesBegan:nil withEvent:nil];
|
||||
XCTAssertTrue([resp called], "GoUIResponder.pressesBegan not called");
|
||||
}
|
||||
|
||||
- (void)testSuper {
|
||||
ObjcpkgGoNSObject *o = [[ObjcpkgGoNSObject alloc] init];
|
||||
struct objc_super _super = {
|
||||
.receiver = o,
|
||||
.super_class = [NSObject class],
|
||||
};
|
||||
NSString *superDesc = ((NSString *(*)(struct objc_super*, SEL))objc_msgSendSuper)(&_super, @selector(description));
|
||||
XCTAssertTrue([superDesc isEqualToString:[o description]], "GoNSObject description mismatch");
|
||||
[o setUseSelf:TRUE];
|
||||
XCTAssertTrue([ObjcpkgDescriptionStr isEqualToString:[o description]], "GoNSObject description mismatch");
|
||||
}
|
||||
|
||||
- (void)testIdentity {
|
||||
NSDate *d = [[NSDate alloc] init];
|
||||
NSDate *d2 = ObjcpkgDupNSDate(d);
|
||||
XCTAssertEqual(d, d2, @"ObjcpkgDupNSDate failed to duplicate ObjC instance");
|
||||
ObjcpkgGoNSDate *gd = [[ObjcpkgGoNSDate alloc] init];
|
||||
NSDate *gd2 = ObjcpkgDupNSDate(gd);
|
||||
XCTAssertEqual(gd, gd2, @"ObjcpkgDupNSDate failed to duplicate Go instance");
|
||||
NSDate *gd3 = ObjcpkgNewGoNSDate();
|
||||
NSDate *gd4 = ObjcpkgDupNSDate(gd3);
|
||||
XCTAssertEqual(gd4, gd3, @"ObjcpkgDupNSDate failed to duplicate instance created in Go");
|
||||
}
|
||||
@end
|
6
bind/objc/doc.go
Normal file
6
bind/objc/doc.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package objc implements the Objective-C language bindings.
|
||||
package objc
|
35
bind/objc/ref.h
Normal file
35
bind/objc/ref.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef __GO_REF_HDR__
|
||||
#define __GO_REF_HDR__
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
// GoSeqRef is an object tagged with an integer for passing back and
|
||||
// forth across the language boundary. A GoSeqRef may represent either
|
||||
// an instance of a Go object, or an Objective-C object passed to Go.
|
||||
// The explicit allocation of a GoSeqRef is used to pin a Go object
|
||||
// when it is passed to Objective-C. The Go seq package maintains a
|
||||
// reference to the Go object in a map keyed by the refnum along with
|
||||
// a reference count. When the reference count reaches zero, the Go
|
||||
// seq package will clear the corresponding entry in the map.
|
||||
@interface GoSeqRef : NSObject {
|
||||
}
|
||||
@property(readonly) int32_t refnum;
|
||||
@property(strong) id obj; // NULL when representing a Go object.
|
||||
|
||||
// new GoSeqRef object to proxy a Go object. The refnum must be
|
||||
// provided from Go side.
|
||||
- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj;
|
||||
|
||||
- (int32_t)incNum;
|
||||
|
||||
@end
|
||||
|
||||
@protocol goSeqRefInterface
|
||||
-(GoSeqRef*) _ref;
|
||||
@end
|
||||
|
||||
#endif
|
91
bind/objc/seq_darwin.go.support
Normal file
91
bind/objc/seq_darwin.go.support
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// Go support functions for Objective-C. Note that this
|
||||
// file is copied into and compiled with the generated
|
||||
// bindings.
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c -fobjc-arc -fmodules -fblocks -Werror
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "seq.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/mobile/bind/seq"
|
||||
)
|
||||
|
||||
// DestroyRef is called by Objective-C to inform Go it is done with a reference.
|
||||
//export DestroyRef
|
||||
func DestroyRef(refnum C.int32_t) {
|
||||
seq.Delete(int32(refnum))
|
||||
}
|
||||
|
||||
// encodeString copies a Go string and returns it as a nstring.
|
||||
func encodeString(s string) C.nstring {
|
||||
n := C.int(len(s))
|
||||
if n == 0 {
|
||||
return C.nstring{}
|
||||
}
|
||||
ptr := C.malloc(C.size_t(n))
|
||||
if ptr == nil {
|
||||
panic("encodeString: malloc failed")
|
||||
}
|
||||
copy((*[1<<31 - 1]byte)(ptr)[:n], s)
|
||||
return C.nstring{ptr: ptr, len: n}
|
||||
}
|
||||
|
||||
// decodeString converts a nstring to a Go string. The
|
||||
// data in str is freed after use.
|
||||
func decodeString(str C.nstring) string {
|
||||
if str.ptr == nil {
|
||||
return ""
|
||||
}
|
||||
s := C.GoStringN((*C.char)(str.ptr), str.len)
|
||||
C.free(str.ptr)
|
||||
return s
|
||||
}
|
||||
|
||||
// fromSlice converts a slice to a nbyteslice.
|
||||
// If cpy is set, a malloc'ed copy of the data is returned.
|
||||
func fromSlice(s []byte, cpy bool) C.nbyteslice {
|
||||
if s == nil || len(s) == 0 {
|
||||
return C.nbyteslice{}
|
||||
}
|
||||
ptr, n := unsafe.Pointer(&s[0]), C.int(len(s))
|
||||
if cpy {
|
||||
nptr := C.malloc(C.size_t(n))
|
||||
if nptr == nil {
|
||||
panic("fromSlice: malloc failed")
|
||||
}
|
||||
copy((*[1<<31 - 1]byte)(nptr)[:n], (*[1<<31 - 1]byte)(ptr)[:n])
|
||||
ptr = nptr
|
||||
}
|
||||
return C.nbyteslice{ptr: ptr, len: n}
|
||||
}
|
||||
|
||||
// toSlice takes a nbyteslice and returns a byte slice with the data. If cpy is
|
||||
// set, the slice contains a copy of the data. If not, the generated Go code
|
||||
// calls releaseByteSlice after use.
|
||||
func toSlice(s C.nbyteslice, cpy bool) []byte {
|
||||
if s.ptr == nil || s.len == 0 {
|
||||
return nil
|
||||
}
|
||||
var b []byte
|
||||
if cpy {
|
||||
b = C.GoBytes(s.ptr, C.int(s.len))
|
||||
C.free(s.ptr)
|
||||
} else {
|
||||
b = (*[1<<31 - 1]byte)(unsafe.Pointer(s.ptr))[:s.len:s.len]
|
||||
}
|
||||
return b
|
||||
}
|
63
bind/objc/seq_darwin.h
Normal file
63
bind/objc/seq_darwin.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef __GO_SEQ_DARWIN_HDR__
|
||||
#define __GO_SEQ_DARWIN_HDR__
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include "ref.h"
|
||||
#include "Universe.objc.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOG_DEBUG(...) NSLog(__VA_ARGS__);
|
||||
#else
|
||||
#define LOG_DEBUG(...) ;
|
||||
#endif
|
||||
|
||||
#define LOG_INFO(...) NSLog(__VA_ARGS__);
|
||||
#define LOG_FATAL(...) \
|
||||
{ \
|
||||
NSLog(__VA_ARGS__); \
|
||||
@throw \
|
||||
[NSException exceptionWithName:NSInternalInconsistencyException \
|
||||
reason:[NSString stringWithFormat:__VA_ARGS__] \
|
||||
userInfo:NULL]; \
|
||||
}
|
||||
|
||||
// Platform specific types
|
||||
typedef struct nstring {
|
||||
void *ptr;
|
||||
int len;
|
||||
} nstring;
|
||||
typedef struct nbyteslice {
|
||||
void *ptr;
|
||||
int len;
|
||||
} nbyteslice;
|
||||
typedef int nint;
|
||||
|
||||
extern void init_seq();
|
||||
// go_seq_dec_ref decrements the reference count for the
|
||||
// specified refnum. It is called from Go from a finalizer.
|
||||
extern void go_seq_dec_ref(int32_t refnum);
|
||||
// go_seq_inc_ref increments the reference count for the
|
||||
// specified refnum. It is called from Go just before converting
|
||||
// a proxy to its refnum.
|
||||
extern void go_seq_inc_ref(int32_t refnum);
|
||||
|
||||
extern int32_t go_seq_to_refnum(id obj);
|
||||
// go_seq_go_to_refnum is a special case of go_seq_to_refnum
|
||||
extern int32_t go_seq_go_to_refnum(GoSeqRef *ref);
|
||||
|
||||
extern GoSeqRef *go_seq_from_refnum(int32_t refnum);
|
||||
// go_seq_objc_from_refnum is a special case of go_seq_from_refnum for
|
||||
// Objective-C objects that implement a Go interface.
|
||||
extern id go_seq_objc_from_refnum(int32_t refnum);
|
||||
|
||||
extern nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy);
|
||||
extern nstring go_seq_from_objc_string(NSString *s);
|
||||
|
||||
extern NSData *go_seq_to_objc_bytearray(nbyteslice, int copy);
|
||||
extern NSString *go_seq_to_objc_string(nstring str);
|
||||
|
||||
#endif // __GO_SEQ_DARWIN_HDR__
|
381
bind/objc/seq_darwin.m.support
Normal file
381
bind/objc/seq_darwin.m.support
Normal file
|
@ -0,0 +1,381 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
#include "seq.h"
|
||||
#include "_cgo_export.h"
|
||||
|
||||
// * Objective-C implementation of a Go interface type
|
||||
//
|
||||
// For an interface testpkg.I, gobind defines a protocol GoSeqTestpkgI.
|
||||
// Reference tracker (tracker) maintains two maps:
|
||||
// 1) _refs: objective-C object pointer -> a refnum (starting from 42).
|
||||
// 2) _objs: refnum -> RefCounter.
|
||||
//
|
||||
// Whenever a user's object conforming the protocol is sent to Go (through
|
||||
// a function or method that takes I), _refs is consulted to find the refnum
|
||||
// of the object. If not found, the refnum is assigned and stored.
|
||||
//
|
||||
// _objs is also updated so that the RefCounter is incremented and the
|
||||
// user's object is pinned.
|
||||
//
|
||||
// When a Go side needs to call a method of the interface, the Go side
|
||||
// notifies the Objective-C side of the object's refnum. Upon receiving the
|
||||
// request, Objective-C side looks up the object from _objs map, and sends
|
||||
// the method to the object.
|
||||
//
|
||||
// The RefCount counts the references on objective-C objects from Go side,
|
||||
// and pins the objective-C objects until there is no more references from
|
||||
// Go side.
|
||||
//
|
||||
// * Objective-C proxy of a Go object (struct or interface type)
|
||||
//
|
||||
// For Go type object, a objective-C proxy instance is created whenever
|
||||
// the object reference is passed into objective-C.
|
||||
//
|
||||
// While crossing the language barrier there is a brief window where the foreign
|
||||
// proxy object might be finalized but the refnum is not yet translated to its object.
|
||||
// If the proxy object was the last reference to the foreign object, the refnum
|
||||
// will be invalid by the time it is looked up in the foreign reference tracker.
|
||||
//
|
||||
// To make sure the foreign object is kept live while its refnum is in transit,
|
||||
// increment its refererence count before crossing. The other side will decrement
|
||||
// it again immediately after the refnum is converted to its object.
|
||||
|
||||
// Note that this file is copied into and compiled with the generated
|
||||
// bindings.
|
||||
|
||||
// A simple thread-safe mutable dictionary.
|
||||
@interface goSeqDictionary : NSObject {
|
||||
}
|
||||
@property NSMutableDictionary *dict;
|
||||
@end
|
||||
|
||||
@implementation goSeqDictionary
|
||||
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
_dict = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)get:(id)key {
|
||||
@synchronized(self) {
|
||||
return [_dict objectForKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)put:(id)obj withKey:(id)key {
|
||||
@synchronized(self) {
|
||||
[_dict setObject:obj forKey:key];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
// NULL_REFNUM is also known to bind/seq/ref.go and bind/java/Seq.java
|
||||
#define NULL_REFNUM 41
|
||||
|
||||
// RefTracker encapsulates a map of objective-C objects passed to Go and
|
||||
// the reference number counter which is incremented whenever an objective-C
|
||||
// object that implements a Go interface is created.
|
||||
@interface RefTracker : NSObject {
|
||||
int32_t _next;
|
||||
NSMutableDictionary *_refs; // map: object ptr -> refnum
|
||||
NSMutableDictionary *_objs; // map: refnum -> RefCounter*
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
|
||||
// decrements the counter of the objective-C object with the reference number.
|
||||
// This is called whenever a Go proxy to this object is finalized.
|
||||
// When the counter reaches 0, the object is removed from the map.
|
||||
- (void)dec:(int32_t)refnum;
|
||||
|
||||
// increments the counter of the objective-C object with the reference number.
|
||||
// This is called whenever a Go proxy is converted to its refnum and send
|
||||
// across the language barrier.
|
||||
- (void)inc:(int32_t)refnum;
|
||||
|
||||
// returns the object of the reference number.
|
||||
- (id)get:(int32_t)refnum;
|
||||
|
||||
// returns the reference number of the object and increments the ref count.
|
||||
// This is called whenever an Objective-C object is sent to Go side.
|
||||
- (int32_t)assignRefnumAndIncRefcount:(id)obj;
|
||||
@end
|
||||
|
||||
static RefTracker *tracker = NULL;
|
||||
|
||||
#define IS_FROM_GO(refnum) ((refnum) < 0)
|
||||
|
||||
// init_seq is called when the Go side is initialized.
|
||||
void init_seq() { tracker = [[RefTracker alloc] init]; }
|
||||
|
||||
void go_seq_dec_ref(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
[tracker dec:refnum];
|
||||
}
|
||||
}
|
||||
|
||||
void go_seq_inc_ref(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
[tracker inc:refnum];
|
||||
}
|
||||
}
|
||||
|
||||
NSData *go_seq_to_objc_bytearray(nbyteslice s, int copy) {
|
||||
if (s.ptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
BOOL freeWhenDone = copy ? YES : NO;
|
||||
return [NSData dataWithBytesNoCopy:s.ptr length:s.len freeWhenDone:freeWhenDone];
|
||||
}
|
||||
|
||||
NSString *go_seq_to_objc_string(nstring str) {
|
||||
if (str.len == 0) { // empty string.
|
||||
return @"";
|
||||
}
|
||||
NSString * res = [[NSString alloc] initWithBytesNoCopy:str.ptr
|
||||
length:str.len
|
||||
encoding:NSUTF8StringEncoding
|
||||
freeWhenDone:YES];
|
||||
return res;
|
||||
}
|
||||
|
||||
id go_seq_objc_from_refnum(int32_t refnum) {
|
||||
id obj = [tracker get:refnum];
|
||||
// Go called IncForeignRef just before converting its proxy to its refnum. Decrement it here.
|
||||
// It's very important to decrement *after* fetching the reference from the tracker, in case
|
||||
// there are no other proxy references to the object.
|
||||
[tracker dec:refnum];
|
||||
return obj;
|
||||
}
|
||||
|
||||
GoSeqRef *go_seq_from_refnum(int32_t refnum) {
|
||||
if (refnum == NULL_REFNUM) {
|
||||
return nil;
|
||||
}
|
||||
if (IS_FROM_GO(refnum)) {
|
||||
return [[GoSeqRef alloc] initWithRefnum:refnum obj:NULL];
|
||||
}
|
||||
return [[GoSeqRef alloc] initWithRefnum:refnum obj:go_seq_objc_from_refnum(refnum)];
|
||||
}
|
||||
|
||||
int32_t go_seq_to_refnum(id obj) {
|
||||
if (obj == nil) {
|
||||
return NULL_REFNUM;
|
||||
}
|
||||
return [tracker assignRefnumAndIncRefcount:obj];
|
||||
}
|
||||
|
||||
int32_t go_seq_go_to_refnum(GoSeqRef *ref) {
|
||||
int32_t refnum = [ref incNum];
|
||||
if (!IS_FROM_GO(refnum)) {
|
||||
LOG_FATAL(@"go_seq_go_to_refnum on objective-c objects is not permitted");
|
||||
}
|
||||
return refnum;
|
||||
}
|
||||
|
||||
nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy) {
|
||||
struct nbyteslice res = {NULL, 0};
|
||||
int sz = data.length;
|
||||
if (sz == 0) {
|
||||
return res;
|
||||
}
|
||||
void *ptr;
|
||||
// If the argument was not a NSMutableData, copy the data so that
|
||||
// the NSData is not changed from Go. The corresponding free is called
|
||||
// by releaseByteSlice.
|
||||
if (copy || ![data isKindOfClass:[NSMutableData class]]) {
|
||||
void *arr_copy = malloc(sz);
|
||||
if (arr_copy == NULL) {
|
||||
LOG_FATAL(@"malloc failed");
|
||||
}
|
||||
memcpy(arr_copy, [data bytes], sz);
|
||||
ptr = arr_copy;
|
||||
} else {
|
||||
ptr = (void *)[data bytes];
|
||||
}
|
||||
res.ptr = ptr;
|
||||
res.len = sz;
|
||||
return res;
|
||||
}
|
||||
|
||||
nstring go_seq_from_objc_string(NSString *s) {
|
||||
nstring res = {NULL, 0};
|
||||
int len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
if (len == 0) {
|
||||
if (s.length > 0) {
|
||||
LOG_INFO(@"unable to encode an NSString into UTF-8");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char *buf = (char *)malloc(len);
|
||||
if (buf == NULL) {
|
||||
LOG_FATAL(@"malloc failed");
|
||||
}
|
||||
NSUInteger used;
|
||||
[s getBytes:buf
|
||||
maxLength:len
|
||||
usedLength:&used
|
||||
encoding:NSUTF8StringEncoding
|
||||
options:0
|
||||
range:NSMakeRange(0, [s length])
|
||||
remainingRange:NULL];
|
||||
res.ptr = buf;
|
||||
res.len = used;
|
||||
return res;
|
||||
}
|
||||
|
||||
@implementation GoSeqRef {
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
LOG_FATAL(@"GoSeqRef init is disallowed");
|
||||
}
|
||||
|
||||
- (int32_t)incNum {
|
||||
IncGoRef(_refnum);
|
||||
return _refnum;
|
||||
}
|
||||
|
||||
// called when an object from Go is passed in.
|
||||
- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_refnum = refnum;
|
||||
_obj = obj;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (IS_FROM_GO(_refnum)) {
|
||||
DestroyRef(_refnum);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
// RefCounter is a pair of (GoSeqProxy, count). GoSeqProxy has a strong
|
||||
// reference to an Objective-C object. The count corresponds to
|
||||
// the number of Go proxy objects.
|
||||
//
|
||||
// RefTracker maintains a map of refnum to RefCounter, for every
|
||||
// Objective-C objects passed to Go. This map allows the transact
|
||||
// call to relay the method call to the right Objective-C object, and
|
||||
// prevents the Objective-C objects from being deallocated
|
||||
// while they are still referenced from Go side.
|
||||
@interface RefCounter : NSObject {
|
||||
}
|
||||
@property(strong, readonly) id obj;
|
||||
@property int cnt;
|
||||
|
||||
- (id)initWithObject:(id)obj;
|
||||
@end
|
||||
|
||||
@implementation RefCounter {
|
||||
}
|
||||
- (id)initWithObject:(id)obj {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_obj = obj;
|
||||
_cnt = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RefTracker {
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_next = 42;
|
||||
_refs = [[NSMutableDictionary alloc] init];
|
||||
_objs = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dec:(int32_t)refnum { // called whenever a go proxy object is finalized.
|
||||
if (IS_FROM_GO(refnum)) {
|
||||
LOG_FATAL(@"dec:invalid refnum for Objective-C objects");
|
||||
}
|
||||
@synchronized(self) {
|
||||
id key = @(refnum);
|
||||
RefCounter *counter = [_objs objectForKey:key];
|
||||
if (counter == NULL) {
|
||||
LOG_FATAL(@"unknown refnum");
|
||||
}
|
||||
int n = counter.cnt;
|
||||
if (n <= 0) {
|
||||
LOG_FATAL(@"refcount underflow");
|
||||
} else if (n == 1) {
|
||||
LOG_DEBUG(@"remove the reference %d", refnum);
|
||||
NSValue *ptr = [NSValue valueWithPointer:(const void *)(counter.obj)];
|
||||
[_refs removeObjectForKey:ptr];
|
||||
[_objs removeObjectForKey:key];
|
||||
} else {
|
||||
counter.cnt = n - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// inc is called whenever a ObjC refnum crosses from Go to ObjC
|
||||
- (void)inc:(int32_t)refnum {
|
||||
if (IS_FROM_GO(refnum)) {
|
||||
LOG_FATAL(@"dec:invalid refnum for Objective-C objects");
|
||||
}
|
||||
@synchronized(self) {
|
||||
id key = @(refnum);
|
||||
RefCounter *counter = [_objs objectForKey:key];
|
||||
if (counter == NULL) {
|
||||
LOG_FATAL(@"unknown refnum");
|
||||
}
|
||||
counter.cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)get:(int32_t)refnum {
|
||||
if (IS_FROM_GO(refnum)) {
|
||||
LOG_FATAL(@"get:invalid refnum for Objective-C objects");
|
||||
}
|
||||
@synchronized(self) {
|
||||
RefCounter *counter = _objs[@(refnum)];
|
||||
if (counter == NULL) {
|
||||
LOG_FATAL(@"unidentified object refnum: %d", refnum);
|
||||
}
|
||||
return counter.obj;
|
||||
}
|
||||
}
|
||||
|
||||
- (int32_t)assignRefnumAndIncRefcount:(id)obj {
|
||||
@synchronized(self) {
|
||||
NSValue *ptr = [NSValue valueWithPointer:(const void *)(obj)];
|
||||
NSNumber *refnum = [_refs objectForKey:ptr];
|
||||
if (refnum == NULL) {
|
||||
refnum = @(_next++);
|
||||
_refs[ptr] = refnum;
|
||||
}
|
||||
RefCounter *counter = [_objs objectForKey:refnum];
|
||||
if (counter == NULL) {
|
||||
counter = [[RefCounter alloc] initWithObject:obj];
|
||||
counter.cnt = 1;
|
||||
_objs[refnum] = counter;
|
||||
} else {
|
||||
counter.cnt++;
|
||||
}
|
||||
return (int32_t)([refnum intValue]);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
1009
bind/objc/seq_test.go
Normal file
1009
bind/objc/seq_test.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue