github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/bind/objc/seq_darwin.m (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 #include <stdio.h> 6 #include <stdint.h> 7 #include <string.h> 8 #include <Foundation/Foundation.h> 9 #include "seq.h" 10 #include "_cgo_export.h" 11 12 #ifdef DEBUG 13 #define LOG_DEBUG(...) NSLog(__VA_ARGS__); 14 #else 15 #define LOG_DEBUG(...) ; 16 #endif 17 18 #define LOG_INFO(...) NSLog(__VA_ARGS__); 19 #define LOG_FATAL(...) \ 20 { \ 21 NSLog(__VA_ARGS__); \ 22 @throw \ 23 [NSException exceptionWithName:NSInternalInconsistencyException \ 24 reason:[NSString stringWithFormat:__VA_ARGS__] \ 25 userInfo:NULL]; \ 26 } 27 28 // * Objective-C implementation of a Go interface type 29 // 30 // For an interface testpkg.I, gobind defines a protocol GoSeqTestpkgI. 31 // Reference tracker (tracker) maintains two maps: 32 // 1) _refs: objective-C object pointer -> a refnum (starting from 42). 33 // 2) _objs: refnum -> RefCounter. 34 // 35 // Whenever a user's object conforming the protocol is sent to Go (through 36 // a function or method that takes I), _refs is consulted to find the refnum 37 // of the object. If not found, the refnum is assigned and stored. 38 // 39 // _objs is also updated so that the RefCounter is incremented and the 40 // user's object is pinned. 41 // 42 // When a Go side needs to call a method of the interface, the Go side 43 // notifies the Objective-C side of the object's refnum, and the method code 44 // as gobind assigned. Upon receiving the request, Objective-C side looks 45 // up the object from _objs map, and looks up the proxy global function 46 // registered in 'proxies'. The global function deserializes/serializes 47 // the parameters and sends the method to the object. 48 // 49 // The RefCount counts the references on objective-C objects from Go side, 50 // and pins the objective-C objects until there is no more reference from 51 // Go side. 52 // 53 // * Objective-C proxy of a Go object (struct or interface type) 54 // 55 // For Go type object, a objective-C proxy instance is created whenever 56 // the object reference is passed into objective-C. 57 58 // A simple thread-safe mutable dictionary. 59 @interface goSeqDictionary : NSObject { 60 } 61 @property NSMutableDictionary *dict; 62 @end 63 64 @implementation goSeqDictionary 65 66 - (id)init { 67 if (self = [super init]) { 68 _dict = [[NSMutableDictionary alloc] init]; 69 } 70 return self; 71 } 72 73 - (id)get:(id)key { 74 @synchronized(self) { 75 return [_dict objectForKey:key]; 76 } 77 } 78 79 - (void)put:(id)obj withKey:(id)key { 80 @synchronized(self) { 81 [_dict setObject:obj forKey:key]; 82 } 83 } 84 @end 85 86 // The proxies maps Go interface name (e.g. go.testpkg.I) to the proxy function 87 // gobind generates for interfaces defined in a module. The function is 88 // registered by calling go_seq_register_proxy from a global contructor funcion. 89 static goSeqDictionary *proxies = NULL; 90 91 __attribute__((constructor)) static void go_seq_very_init() { 92 proxies = [[goSeqDictionary alloc] init]; 93 } 94 95 void go_seq_register_proxy(const char *descriptor, 96 void (*fn)(id, int, GoSeq *, GoSeq *)) { 97 [proxies put:^(id obj, int code, GoSeq *in, GoSeq *out) { 98 fn(obj, code, in, out); 99 } withKey:[NSString stringWithUTF8String:descriptor]]; 100 } 101 102 // RefTracker encapsulates a map of objective-C objects passed to Go and 103 // the reference number counter which is incremented whenever an objective-C 104 // object that implements a Go interface is created. 105 @interface RefTracker : NSObject { 106 int32_t _next; 107 NSMutableDictionary *_refs; // map: object ptr -> refnum 108 NSMutableDictionary *_objs; // map: refnum -> RefCounter* 109 } 110 111 - (id)init; 112 113 // decrements the counter of the objective-C object with the reference number. 114 // This is called whenever a Go proxy to this object is finalized. 115 // When the counter reaches 0, the object is removed from the map. 116 - (void)dec:(int32_t)refnum; 117 118 // returns the object of the reference number. 119 - (id)get:(int32_t)refnum; 120 121 // returns the reference number of the object and increments the ref count. 122 // This is called whenever an Objective-C object is sent to Go side. 123 - (int32_t)assignRefnumAndIncRefcount:(id)obj; 124 @end 125 126 RefTracker *tracker = NULL; 127 128 // mem_ensure ensures that m has at least size bytes free. 129 // If m is NULL, it is created. 130 static void mem_ensure(GoSeq *m, uint32_t size) { 131 size_t cap = m->cap; 132 if (cap > m->off + size) { 133 return; 134 } 135 if (cap == 0) { 136 cap = 64; 137 } 138 while (cap < m->off + size) { 139 cap *= 2; 140 } 141 m->buf = (uint8_t *)realloc((void *)m->buf, cap); 142 if (m->buf == NULL) { 143 LOG_FATAL(@"mem_ensure realloc failed, off=%zu, size=%u", m->off, size); 144 } 145 m->cap = cap; 146 } 147 148 static uint32_t align(uint32_t offset, uint32_t alignment) { 149 uint32_t pad = offset % alignment; 150 if (pad > 0) { 151 pad = alignment - pad; 152 } 153 return pad + offset; 154 } 155 156 static uint8_t *mem_read(GoSeq *m, uint32_t size, uint32_t alignment) { 157 if (size == 0) { 158 return NULL; 159 } 160 if (m == NULL) { 161 LOG_FATAL(@"mem_read on NULL GoSeq"); 162 } 163 uint32_t offset = align(m->off, alignment); 164 165 if (m->len - offset < size) { 166 LOG_FATAL(@"short read"); 167 } 168 uint8_t *res = m->buf + offset; 169 m->off = offset + size; 170 return res; 171 } 172 173 static uint8_t *mem_write(GoSeq *m, uint32_t size, uint32_t alignment) { 174 if (m->off != m->len) { 175 LOG_FATAL(@"write can only append to seq, size: (off=%zu len=%zu, size=%u)", 176 m->off, m->len, size); 177 } 178 uint32_t offset = align(m->off, alignment); 179 mem_ensure(m, offset - m->off + size); 180 uint8_t *res = m->buf + offset; 181 m->off = offset + size; 182 m->len = offset + size; 183 return res; 184 } 185 186 // extern 187 void go_seq_free(GoSeq *m) { 188 if (m != NULL) { 189 free(m->buf); 190 } 191 } 192 193 #define MEM_READ(seq, ty) ((ty *)mem_read(seq, sizeof(ty), sizeof(ty))) 194 #define MEM_WRITE(seq, ty) (*(ty *)mem_write(seq, sizeof(ty), sizeof(ty))) 195 196 int go_seq_readInt(GoSeq *seq) { 197 int64_t v = go_seq_readInt64(seq); 198 return v; // Assume that Go-side used WriteInt to encode 'int' value. 199 } 200 201 void go_seq_writeInt(GoSeq *seq, int v) { go_seq_writeInt64(seq, v); } 202 203 BOOL go_seq_readBool(GoSeq *seq) { 204 int8_t v = go_seq_readInt8(seq); 205 return v ? YES : NO; 206 } 207 208 void go_seq_writeBool(GoSeq *seq, BOOL v) { go_seq_writeInt8(seq, v ? 1 : 0); } 209 210 int8_t go_seq_readInt8(GoSeq *seq) { 211 int8_t *v = MEM_READ(seq, int8_t); 212 return v == NULL ? 0 : *v; 213 } 214 void go_seq_writeInt8(GoSeq *seq, int8_t v) { MEM_WRITE(seq, int8_t) = v; } 215 216 int16_t go_seq_readInt16(GoSeq *seq) { 217 int16_t *v = MEM_READ(seq, int16_t); 218 return v == NULL ? 0 : *v; 219 } 220 void go_seq_writeInt16(GoSeq *seq, int16_t v) { MEM_WRITE(seq, int16_t) = v; } 221 222 int32_t go_seq_readInt32(GoSeq *seq) { 223 int32_t *v = MEM_READ(seq, int32_t); 224 return v == NULL ? 0 : *v; 225 } 226 void go_seq_writeInt32(GoSeq *seq, int32_t v) { MEM_WRITE(seq, int32_t) = v; } 227 228 int64_t go_seq_readInt64(GoSeq *seq) { 229 int64_t *v = MEM_READ(seq, int64_t); 230 return v == NULL ? 0 : *v; 231 } 232 void go_seq_writeInt64(GoSeq *seq, int64_t v) { MEM_WRITE(seq, int64_t) = v; } 233 234 float go_seq_readFloat32(GoSeq *seq) { 235 float *v = MEM_READ(seq, float); 236 return v == NULL ? 0 : *v; 237 } 238 void go_seq_writeFloat32(GoSeq *seq, float v) { MEM_WRITE(seq, float) = v; } 239 240 double go_seq_readFloat64(GoSeq *seq) { 241 double *v = MEM_READ(seq, double); 242 return v == NULL ? 0 : *v; 243 } 244 void go_seq_writeFloat64(GoSeq *seq, double v) { MEM_WRITE(seq, double) = v; } 245 246 NSString *go_seq_readUTF8(GoSeq *seq) { 247 int32_t len = *MEM_READ(seq, int32_t); 248 if (len == 0) { 249 return NULL; 250 } 251 const void *buf = (const void *)mem_read(seq, len, 1); 252 return [[NSString alloc] initWithBytes:buf 253 length:len 254 encoding:NSUTF8StringEncoding]; 255 } 256 257 void go_seq_writeUTF8(GoSeq *seq, NSString *s) { 258 int32_t len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 259 MEM_WRITE(seq, int32_t) = len; 260 261 if (len == 0 && s.length > 0) { 262 LOG_INFO(@"unable to incode an NSString into UTF-8"); 263 return; 264 } 265 266 char *buf = (char *)mem_write(seq, len, 1); 267 NSUInteger used; 268 [s getBytes:buf 269 maxLength:len 270 usedLength:&used 271 encoding:NSUTF8StringEncoding 272 options:0 273 range:NSMakeRange(0, [s length]) 274 remainingRange:NULL]; 275 if (used < len) { 276 buf[used] = '\0'; 277 } 278 return; 279 } 280 281 NSData *go_seq_readByteArray(GoSeq *seq) { 282 int64_t sz = *MEM_READ(seq, int64_t); 283 if (sz == 0) { 284 return [NSData data]; 285 } 286 // BUG(hyangah): it is possible that *ptr is already GC'd by Go runtime. 287 void *ptr = (void *)(*MEM_READ(seq, int64_t)); 288 return [NSData dataWithBytes:ptr length:sz]; 289 } 290 291 void go_seq_writeByteArray(GoSeq *seq, NSData *data) { 292 int64_t sz = data.length; 293 MEM_WRITE(seq, int64_t) = sz; 294 if (sz == 0) { 295 return; 296 } 297 298 int64_t ptr = (int64_t)data.bytes; 299 MEM_WRITE(seq, int64_t) = ptr; 300 return; 301 } 302 303 typedef void (^proxyFn)(id, int, GoSeq *, GoSeq *); 304 305 // called from Go when Go tries to access an Objective-C object. 306 void go_seq_recv(int32_t refnum, const char *desc, int code, uint8_t *in_ptr, 307 size_t in_len, uint8_t **out_ptr, size_t *out_len) { 308 if (code == -1) { // special signal from seq.FinalizeRef in Go 309 [tracker dec:refnum]; 310 return; 311 } 312 GoSeq ins = {}; 313 ins.buf = in_ptr; // Memory allocated from Go 314 ins.off = 0; 315 ins.len = in_len; 316 ins.cap = in_len; 317 id obj = [tracker get:refnum]; 318 if (obj == NULL) { 319 LOG_FATAL(@"invalid object for ref %d", refnum); 320 return; 321 } 322 323 NSString *k = [NSString stringWithUTF8String:desc]; 324 325 proxyFn fn = [proxies get:k]; 326 if (fn == NULL) { 327 LOG_FATAL(@"cannot find a proxy function for %s", desc); 328 return; 329 } 330 GoSeq outs = {}; 331 fn(obj, code, &ins, &outs); 332 333 if (out_ptr == NULL) { 334 free(outs.buf); 335 } else { 336 *out_ptr = outs.buf; // Let Go side free this memory 337 *out_len = outs.len; 338 } 339 } 340 341 void go_seq_send(char *descriptor, int code, GoSeq *req, GoSeq *res) { 342 if (descriptor == NULL) { 343 LOG_FATAL(@"invalid NULL descriptor"); 344 } 345 uint8_t *req_buf = NULL; 346 size_t req_len = 0; 347 if (req != NULL) { 348 req_buf = req->buf; 349 req_len = req->len; 350 } 351 352 uint8_t **res_buf = NULL; 353 size_t *res_len = NULL; 354 if (res != NULL) { 355 res_buf = &res->buf; 356 res_len = &res->len; 357 } 358 359 GoString desc; 360 desc.p = descriptor; 361 desc.n = strlen(descriptor); 362 Send(desc, (GoInt)code, req_buf, req_len, res_buf, res_len); 363 } 364 365 #define IS_FROM_GO(refnum) ((refnum) < 0) 366 367 // init_seq is called when the Go side is initialized. 368 void init_seq() { tracker = [[RefTracker alloc] init]; } 369 370 GoSeqRef *go_seq_readRef(GoSeq *seq) { 371 int32_t refnum = go_seq_readInt32(seq); 372 if (IS_FROM_GO(refnum)) { 373 return [[GoSeqRef alloc] initWithRefnum:refnum obj:NULL]; 374 } 375 return [[GoSeqRef alloc] initWithRefnum:refnum obj:[tracker get:refnum]]; 376 } 377 378 // TODO(hyangah): make this go_seq_writeRef(GoSeq *seq, int32_t refnum, id obj) 379 // and get read of GoSeqRef. 380 void go_seq_writeRef(GoSeq *seq, GoSeqRef *v) { 381 int32_t refnum = v.refnum; 382 if (!IS_FROM_GO(refnum)) { 383 LOG_FATAL(@"go_seq_writeRef on objective-c objects is not permitted"); 384 } 385 go_seq_writeInt32(seq, refnum); 386 return; 387 } 388 389 void go_seq_writeObjcRef(GoSeq *seq, id obj) { 390 int32_t refnum = [tracker assignRefnumAndIncRefcount:obj]; 391 go_seq_writeInt32(seq, refnum); 392 } 393 394 @implementation GoSeqRef { 395 } 396 397 - (id)init { 398 LOG_FATAL(@"GoSeqRef init is disallowed"); 399 return nil; 400 } 401 402 // called when an object from Go is passed in. 403 - (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj { 404 self = [super init]; 405 if (self) { 406 _refnum = refnum; 407 _obj = obj; 408 } 409 return self; 410 } 411 412 - (void)dealloc { 413 if (IS_FROM_GO(_refnum)) { 414 DestroyRef(_refnum); 415 } 416 } 417 @end 418 419 // RefCounter is a pair of (GoSeqProxy, count). GoSeqProxy has a strong 420 // reference to an Objective-C object. The count corresponds to 421 // the number of Go proxy objects. 422 // 423 // RefTracker maintains a map of refnum to RefCounter, for every 424 // Objective-C objects passed to Go. This map allows the transact 425 // call to relay the method call to the right Objective-C object, and 426 // prevents the Objective-C objects from being deallocated 427 // while they are still referenced from Go side. 428 @interface RefCounter : NSObject { 429 } 430 @property(strong, readonly) id obj; 431 @property int cnt; 432 433 - (id)initWithObject:(id)obj; 434 @end 435 436 @implementation RefCounter { 437 } 438 - (id)initWithObject:(id)obj { 439 self = [super init]; 440 if (self) { 441 _obj = obj; 442 _cnt = 0; 443 } 444 return self; 445 } 446 447 @end 448 449 @implementation RefTracker { 450 } 451 452 - (id)init { 453 self = [super init]; 454 if (self) { 455 _next = 42; 456 _objs = [[NSMutableDictionary alloc] init]; 457 } 458 return self; 459 } 460 461 - (void)dec:(int32_t)refnum { // called whenever a go proxy object is finalized. 462 if (IS_FROM_GO(refnum)) { 463 LOG_FATAL(@"dec:invalid refnum for Objective-C objects"); 464 return; 465 } 466 @synchronized(self) { 467 id key = @(refnum); 468 RefCounter *counter = [_objs objectForKey:key]; 469 if (counter == NULL) { 470 LOG_FATAL(@"unknown refnum"); 471 return; 472 } 473 int n = counter.cnt; 474 if (n <= 0) { 475 LOG_FATAL(@"refcount underflow"); 476 } else if (n == 1) { 477 LOG_DEBUG(@"remove the reference %d", refnum); 478 NSValue *ptr = [NSValue valueWithPointer:(const void *)(counter.obj)]; 479 [_refs removeObjectForKey:ptr]; 480 [_objs removeObjectForKey:key]; 481 } else { 482 counter.cnt = n - 1; 483 } 484 } 485 } 486 487 - (id)get:(int32_t)refnum { 488 if (IS_FROM_GO(refnum)) { 489 LOG_FATAL(@"get:invalid refnum for Objective-C objects"); 490 return NULL; 491 } 492 @synchronized(self) { 493 RefCounter *counter = _objs[@(refnum)]; 494 if (counter == NULL) { 495 LOG_FATAL(@"unidentified object refnum: %d", refnum); 496 return NULL; 497 } 498 return counter.obj; 499 } 500 } 501 502 - (int32_t)assignRefnumAndIncRefcount:(id)obj { 503 @synchronized(self) { 504 NSValue *ptr = [NSValue valueWithPointer:(const void *)(obj)]; 505 NSNumber *refnum = [_refs objectForKey:ptr]; 506 if (refnum == NULL) { 507 refnum = @(_next++); 508 _refs[ptr] = refnum; 509 } 510 RefCounter *counter = [_objs objectForKey:refnum]; 511 if (counter == NULL) { 512 counter = [[RefCounter alloc] initWithObject:obj]; 513 counter.cnt = 1; 514 _objs[refnum] = counter; 515 } else { 516 counter.cnt++; 517 } 518 return (int32_t)([refnum intValue]); 519 } 520 } 521 522 @end