github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/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 void go_seq_register_proxy(const char *descriptor, 92 void (*fn)(id, int, GoSeq *, GoSeq *)) { 93 if (proxies == NULL) { 94 proxies = [[goSeqDictionary alloc] init]; 95 } 96 // Copying moves the block to the heap. 97 id block = [^(id obj, int code, GoSeq *in, GoSeq *out) { 98 fn(obj, code, in, out); 99 } copy]; 100 101 [proxies put:block withKey:[NSString stringWithUTF8String:descriptor]]; 102 } 103 104 // RefTracker encapsulates a map of objective-C objects passed to Go and 105 // the reference number counter which is incremented whenever an objective-C 106 // object that implements a Go interface is created. 107 @interface RefTracker : NSObject { 108 int32_t _next; 109 NSMutableDictionary *_refs; // map: object ptr -> refnum 110 NSMutableDictionary *_objs; // map: refnum -> RefCounter* 111 } 112 113 - (id)init; 114 115 // decrements the counter of the objective-C object with the reference number. 116 // This is called whenever a Go proxy to this object is finalized. 117 // When the counter reaches 0, the object is removed from the map. 118 - (void)dec:(int32_t)refnum; 119 120 // returns the object of the reference number. 121 - (id)get:(int32_t)refnum; 122 123 // returns the reference number of the object and increments the ref count. 124 // This is called whenever an Objective-C object is sent to Go side. 125 - (int32_t)assignRefnumAndIncRefcount:(id)obj; 126 @end 127 128 RefTracker *tracker = NULL; 129 130 // mem_ensure ensures that m has at least size bytes free. 131 // If m is NULL, it is created. 132 static void mem_ensure(GoSeq *m, uint32_t size) { 133 size_t cap = m->cap; 134 if (cap > m->off + size) { 135 return; 136 } 137 if (cap == 0) { 138 cap = 64; 139 } 140 while (cap < m->off + size) { 141 cap *= 2; 142 } 143 m->buf = (uint8_t *)realloc((void *)m->buf, cap); 144 if (m->buf == NULL) { 145 LOG_FATAL(@"mem_ensure realloc failed, off=%zu, size=%u", m->off, size); 146 } 147 m->cap = cap; 148 } 149 150 static uint32_t align(uint32_t offset, uint32_t alignment) { 151 uint32_t pad = offset % alignment; 152 if (pad > 0) { 153 pad = alignment - pad; 154 } 155 return pad + offset; 156 } 157 158 static uint8_t *mem_read(GoSeq *m, uint32_t size, uint32_t alignment) { 159 if (size == 0) { 160 return NULL; 161 } 162 if (m == NULL) { 163 LOG_FATAL(@"mem_read on NULL GoSeq"); 164 } 165 uint32_t offset = align(m->off, alignment); 166 167 if (m->len - offset < size) { 168 LOG_FATAL(@"short read"); 169 } 170 uint8_t *res = m->buf + offset; 171 m->off = offset + size; 172 return res; 173 } 174 175 static uint8_t *mem_write(GoSeq *m, uint32_t size, uint32_t alignment) { 176 if (m->off != m->len) { 177 LOG_FATAL(@"write can only append to seq, size: (off=%zu len=%zu, size=%u)", 178 m->off, m->len, size); 179 } 180 uint32_t offset = align(m->off, alignment); 181 mem_ensure(m, offset - m->off + size); 182 uint8_t *res = m->buf + offset; 183 m->off = offset + size; 184 m->len = offset + size; 185 return res; 186 } 187 188 // extern 189 void go_seq_free(GoSeq *m) { 190 if (m != NULL) { 191 free(m->buf); 192 } 193 } 194 195 #define MEM_READ(seq, ty) ((ty *)mem_read(seq, sizeof(ty), sizeof(ty))) 196 #define MEM_WRITE(seq, ty) (*(ty *)mem_write(seq, sizeof(ty), sizeof(ty))) 197 198 int go_seq_readInt(GoSeq *seq) { 199 int64_t v = go_seq_readInt64(seq); 200 return v; // Assume that Go-side used WriteInt to encode 'int' value. 201 } 202 203 void go_seq_writeInt(GoSeq *seq, int v) { go_seq_writeInt64(seq, v); } 204 205 BOOL go_seq_readBool(GoSeq *seq) { 206 int8_t v = go_seq_readInt8(seq); 207 return v ? YES : NO; 208 } 209 210 void go_seq_writeBool(GoSeq *seq, BOOL v) { go_seq_writeInt8(seq, v ? 1 : 0); } 211 212 int8_t go_seq_readInt8(GoSeq *seq) { 213 int8_t *v = MEM_READ(seq, int8_t); 214 return v == NULL ? 0 : *v; 215 } 216 void go_seq_writeInt8(GoSeq *seq, int8_t v) { MEM_WRITE(seq, int8_t) = v; } 217 218 int16_t go_seq_readInt16(GoSeq *seq) { 219 int16_t *v = MEM_READ(seq, int16_t); 220 return v == NULL ? 0 : *v; 221 } 222 void go_seq_writeInt16(GoSeq *seq, int16_t v) { MEM_WRITE(seq, int16_t) = v; } 223 224 int32_t go_seq_readInt32(GoSeq *seq) { 225 int32_t *v = MEM_READ(seq, int32_t); 226 return v == NULL ? 0 : *v; 227 } 228 void go_seq_writeInt32(GoSeq *seq, int32_t v) { MEM_WRITE(seq, int32_t) = v; } 229 230 int64_t go_seq_readInt64(GoSeq *seq) { 231 int64_t *v = MEM_READ(seq, int64_t); 232 return v == NULL ? 0 : *v; 233 } 234 void go_seq_writeInt64(GoSeq *seq, int64_t v) { MEM_WRITE(seq, int64_t) = v; } 235 236 float go_seq_readFloat32(GoSeq *seq) { 237 float *v = MEM_READ(seq, float); 238 return v == NULL ? 0 : *v; 239 } 240 void go_seq_writeFloat32(GoSeq *seq, float v) { MEM_WRITE(seq, float) = v; } 241 242 double go_seq_readFloat64(GoSeq *seq) { 243 double *v = MEM_READ(seq, double); 244 return v == NULL ? 0 : *v; 245 } 246 void go_seq_writeFloat64(GoSeq *seq, double v) { MEM_WRITE(seq, double) = v; } 247 248 NSString *go_seq_readUTF8(GoSeq *seq) { 249 int32_t len = *MEM_READ(seq, int32_t); 250 if (len == 0) { // empty string. 251 return @""; 252 } 253 const void *buf = (const void *)mem_read(seq, len, 1); 254 return [[NSString alloc] initWithBytes:buf 255 length:len 256 encoding:NSUTF8StringEncoding]; 257 } 258 259 void go_seq_writeUTF8(GoSeq *seq, NSString *s) { 260 int32_t len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 261 MEM_WRITE(seq, int32_t) = len; 262 263 if (len == 0 && s.length > 0) { 264 LOG_INFO(@"unable to incode an NSString into UTF-8"); 265 return; 266 } 267 268 char *buf = (char *)mem_write(seq, len, 1); 269 NSUInteger used; 270 [s getBytes:buf 271 maxLength:len 272 usedLength:&used 273 encoding:NSUTF8StringEncoding 274 options:0 275 range:NSMakeRange(0, [s length]) 276 remainingRange:NULL]; 277 if (used < len) { 278 buf[used] = '\0'; 279 } 280 return; 281 } 282 283 NSData *go_seq_readByteArray(GoSeq *seq) { 284 int64_t sz = *MEM_READ(seq, int64_t); 285 if (sz == 0) { 286 return [NSData data]; 287 } 288 // BUG(hyangah): it is possible that *ptr is already GC'd by Go runtime. 289 void *ptr = (void *)(*MEM_READ(seq, int64_t)); 290 return [NSData dataWithBytes:ptr length:sz]; 291 } 292 293 void go_seq_writeByteArray(GoSeq *seq, NSData *data) { 294 int64_t sz = data.length; 295 MEM_WRITE(seq, int64_t) = sz; 296 if (sz == 0) { 297 return; 298 } 299 300 int64_t ptr = (int64_t)data.bytes; 301 MEM_WRITE(seq, int64_t) = ptr; 302 return; 303 } 304 305 typedef void (^proxyFn)(id, int, GoSeq *, GoSeq *); 306 307 // called from Go when Go tries to access an Objective-C object. 308 void go_seq_recv(int32_t refnum, const char *desc, int code, uint8_t *in_ptr, 309 size_t in_len, uint8_t **out_ptr, size_t *out_len) { 310 if (code == -1) { // special signal from seq.FinalizeRef in Go 311 [tracker dec:refnum]; 312 return; 313 } 314 GoSeq ins = {}; 315 ins.buf = in_ptr; // Memory allocated from Go 316 ins.off = 0; 317 ins.len = in_len; 318 ins.cap = in_len; 319 id obj = [tracker get:refnum]; 320 if (obj == NULL) { 321 LOG_FATAL(@"invalid object for ref %d", refnum); 322 return; 323 } 324 325 NSString *k = [NSString stringWithUTF8String:desc]; 326 327 proxyFn fn = NULL; 328 if (proxies != NULL) { 329 fn = [proxies get:k]; 330 } 331 if (fn == NULL) { 332 LOG_FATAL(@"cannot find a proxy function for %s", desc); 333 return; 334 } 335 GoSeq outs = {}; 336 fn(obj, code, &ins, &outs); 337 338 if (out_ptr == NULL) { 339 free(outs.buf); 340 } else { 341 *out_ptr = outs.buf; // Let Go side free this memory 342 *out_len = outs.len; 343 } 344 } 345 346 void go_seq_send(char *descriptor, int code, GoSeq *req, GoSeq *res) { 347 if (descriptor == NULL) { 348 LOG_FATAL(@"invalid NULL descriptor"); 349 } 350 uint8_t *req_buf = NULL; 351 size_t req_len = 0; 352 if (req != NULL) { 353 req_buf = req->buf; 354 req_len = req->len; 355 } 356 357 uint8_t **res_buf = NULL; 358 size_t *res_len = NULL; 359 if (res != NULL) { 360 res_buf = &res->buf; 361 res_len = &res->len; 362 } 363 364 GoString desc; 365 desc.p = descriptor; 366 desc.n = strlen(descriptor); 367 Send(desc, (GoInt)code, req_buf, req_len, res_buf, res_len); 368 } 369 370 #define IS_FROM_GO(refnum) ((refnum) < 0) 371 372 // init_seq is called when the Go side is initialized. 373 void init_seq() { tracker = [[RefTracker alloc] init]; } 374 375 GoSeqRef *go_seq_readRef(GoSeq *seq) { 376 int32_t refnum = go_seq_readInt32(seq); 377 if (IS_FROM_GO(refnum)) { 378 return [[GoSeqRef alloc] initWithRefnum:refnum obj:NULL]; 379 } 380 return [[GoSeqRef alloc] initWithRefnum:refnum obj:[tracker get:refnum]]; 381 } 382 383 // TODO(hyangah): make this go_seq_writeRef(GoSeq *seq, int32_t refnum, id obj) 384 // and get read of GoSeqRef. 385 void go_seq_writeRef(GoSeq *seq, GoSeqRef *v) { 386 int32_t refnum = v.refnum; 387 if (!IS_FROM_GO(refnum)) { 388 LOG_FATAL(@"go_seq_writeRef on objective-c objects is not permitted"); 389 } 390 go_seq_writeInt32(seq, refnum); 391 return; 392 } 393 394 void go_seq_writeObjcRef(GoSeq *seq, id obj) { 395 int32_t refnum = [tracker assignRefnumAndIncRefcount:obj]; 396 go_seq_writeInt32(seq, refnum); 397 } 398 399 @implementation GoSeqRef { 400 } 401 402 - (id)init { 403 LOG_FATAL(@"GoSeqRef init is disallowed"); 404 return nil; 405 } 406 407 // called when an object from Go is passed in. 408 - (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj { 409 self = [super init]; 410 if (self) { 411 _refnum = refnum; 412 _obj = obj; 413 } 414 return self; 415 } 416 417 - (void)dealloc { 418 if (IS_FROM_GO(_refnum)) { 419 DestroyRef(_refnum); 420 } 421 } 422 @end 423 424 // RefCounter is a pair of (GoSeqProxy, count). GoSeqProxy has a strong 425 // reference to an Objective-C object. The count corresponds to 426 // the number of Go proxy objects. 427 // 428 // RefTracker maintains a map of refnum to RefCounter, for every 429 // Objective-C objects passed to Go. This map allows the transact 430 // call to relay the method call to the right Objective-C object, and 431 // prevents the Objective-C objects from being deallocated 432 // while they are still referenced from Go side. 433 @interface RefCounter : NSObject { 434 } 435 @property(strong, readonly) id obj; 436 @property int cnt; 437 438 - (id)initWithObject:(id)obj; 439 @end 440 441 @implementation RefCounter { 442 } 443 - (id)initWithObject:(id)obj { 444 self = [super init]; 445 if (self) { 446 _obj = obj; 447 _cnt = 0; 448 } 449 return self; 450 } 451 452 @end 453 454 @implementation RefTracker { 455 } 456 457 - (id)init { 458 self = [super init]; 459 if (self) { 460 _next = 42; 461 _objs = [[NSMutableDictionary alloc] init]; 462 } 463 return self; 464 } 465 466 - (void)dec:(int32_t)refnum { // called whenever a go proxy object is finalized. 467 if (IS_FROM_GO(refnum)) { 468 LOG_FATAL(@"dec:invalid refnum for Objective-C objects"); 469 return; 470 } 471 @synchronized(self) { 472 id key = @(refnum); 473 RefCounter *counter = [_objs objectForKey:key]; 474 if (counter == NULL) { 475 LOG_FATAL(@"unknown refnum"); 476 return; 477 } 478 int n = counter.cnt; 479 if (n <= 0) { 480 LOG_FATAL(@"refcount underflow"); 481 } else if (n == 1) { 482 LOG_DEBUG(@"remove the reference %d", refnum); 483 NSValue *ptr = [NSValue valueWithPointer:(const void *)(counter.obj)]; 484 [_refs removeObjectForKey:ptr]; 485 [_objs removeObjectForKey:key]; 486 } else { 487 counter.cnt = n - 1; 488 } 489 } 490 } 491 492 - (id)get:(int32_t)refnum { 493 if (IS_FROM_GO(refnum)) { 494 LOG_FATAL(@"get:invalid refnum for Objective-C objects"); 495 return NULL; 496 } 497 @synchronized(self) { 498 RefCounter *counter = _objs[@(refnum)]; 499 if (counter == NULL) { 500 LOG_FATAL(@"unidentified object refnum: %d", refnum); 501 return NULL; 502 } 503 return counter.obj; 504 } 505 } 506 507 - (int32_t)assignRefnumAndIncRefcount:(id)obj { 508 @synchronized(self) { 509 NSValue *ptr = [NSValue valueWithPointer:(const void *)(obj)]; 510 NSNumber *refnum = [_refs objectForKey:ptr]; 511 if (refnum == NULL) { 512 refnum = @(_next++); 513 _refs[ptr] = refnum; 514 } 515 RefCounter *counter = [_objs objectForKey:refnum]; 516 if (counter == NULL) { 517 counter = [[RefCounter alloc] initWithObject:obj]; 518 counter.cnt = 1; 519 _objs[refnum] = counter; 520 } else { 521 counter.cnt++; 522 } 523 return (int32_t)([refnum intValue]); 524 } 525 } 526 527 @end