|
216 | 216 | return nullptr;
|
217 | 217 | }
|
218 | 218 |
|
| 219 | +static const char *FastEnumerationIteratorFactorySource = R"( |
| 220 | + (function () { |
| 221 | + return { |
| 222 | + stack: new Array(16), |
| 223 | + stacklen: -1, |
| 224 | + stackptr: -1, |
| 225 | + done: false, |
| 226 | +
|
| 227 | + next() { |
| 228 | + if (this.stackptr < 0 && !this.done) { |
| 229 | + this.stacklen = this._fillStack(this.stack); |
| 230 | + if (this.stacklen == 0) { |
| 231 | + this.done = true; |
| 232 | + this.stackptr = -1; |
| 233 | + } else { |
| 234 | + this.stackptr = 0; |
| 235 | + } |
| 236 | + } |
| 237 | +
|
| 238 | + if (this.done) { |
| 239 | + return { done: true }; |
| 240 | + } |
| 241 | +
|
| 242 | + const result = { value: this.stack[this.stackptr++], done: false }; |
| 243 | + if (this.stackptr >= this.stacklen) { |
| 244 | + this.stackptr = -1; |
| 245 | + } |
| 246 | +
|
| 247 | + return result; |
| 248 | + }, |
| 249 | + }; |
| 250 | + }) |
| 251 | +)"; |
| 252 | + |
| 253 | +void initFastEnumeratorIteratorFactory(napi_env env, |
| 254 | + ObjCBridgeState *bridgeState) { |
| 255 | + napi_value result, script; |
| 256 | + napi_create_string_utf8(env, FastEnumerationIteratorFactorySource, |
| 257 | + NAPI_AUTO_LENGTH, &script); |
| 258 | + napi_run_script(env, script, &result); |
| 259 | + bridgeState->createFastEnumeratorIterator = make_ref(env, result); |
| 260 | +} |
| 261 | + |
| 262 | +class FastEnumerationIterator { |
| 263 | +public: |
| 264 | + FastEnumerationIterator(id<NSFastEnumeration> collection) |
| 265 | + : collection(collection) {} |
| 266 | + |
| 267 | + static void finalize(napi_env env, void *data, void *hint) { |
| 268 | + FastEnumerationIterator *iterator = (FastEnumerationIterator *)data; |
| 269 | + delete iterator; |
| 270 | + } |
| 271 | + |
| 272 | + static napi_value fillStack(napi_env env, napi_callback_info cbinfo) { |
| 273 | + ObjCBridgeState *bridgeState = ObjCBridgeState::InstanceData(env); |
| 274 | + |
| 275 | + napi_value jsThis; |
| 276 | + void *data; |
| 277 | + size_t argc = 1; |
| 278 | + napi_value stackArray; |
| 279 | + |
| 280 | + napi_get_cb_info(env, cbinfo, &argc, &stackArray, &jsThis, &data); |
| 281 | + |
| 282 | + FastEnumerationIterator *self = nil; |
| 283 | + napi_unwrap(env, jsThis, (void **)&self); |
| 284 | + |
| 285 | + NSUInteger count = |
| 286 | + [self->collection countByEnumeratingWithState:&self->state |
| 287 | + objects:self->stackbuf |
| 288 | + count:16]; |
| 289 | + |
| 290 | + for (NSUInteger index = 0; index < count; index++) { |
| 291 | + id obj = self->state.itemsPtr[index]; |
| 292 | + napi_value jsObj = bridgeState->getObject(env, obj); |
| 293 | + napi_set_element(env, stackArray, index, jsObj); |
| 294 | + } |
| 295 | + |
| 296 | + napi_value result; |
| 297 | + napi_create_int32(env, count, &result); |
| 298 | + |
| 299 | + return result; |
| 300 | + } |
| 301 | + |
| 302 | + napi_value toJS(napi_env env) { |
| 303 | + ObjCBridgeState *bridgeState = ObjCBridgeState::InstanceData(env); |
| 304 | + |
| 305 | + napi_value createIterator = |
| 306 | + get_ref_value(env, bridgeState->createFastEnumeratorIterator); |
| 307 | + |
| 308 | + napi_value result; |
| 309 | + napi_call_function(env, createIterator, createIterator, 0, nullptr, |
| 310 | + &result); |
| 311 | + |
| 312 | + napi_property_descriptor fillStack = { |
| 313 | + .utf8name = "_fillStack", |
| 314 | + .name = nil, |
| 315 | + .method = FastEnumerationIterator::fillStack, |
| 316 | + .getter = nil, |
| 317 | + .setter = nil, |
| 318 | + .value = nil, |
| 319 | + .attributes = napi_enumerable, |
| 320 | + .data = nil, |
| 321 | + }; |
| 322 | + |
| 323 | + napi_define_properties(env, result, 1, &fillStack); |
| 324 | + |
| 325 | + napi_ref ref; |
| 326 | + napi_wrap(env, result, this, FastEnumerationIterator::finalize, nullptr, |
| 327 | + &ref); |
| 328 | + |
| 329 | + return result; |
| 330 | + } |
| 331 | + |
| 332 | + id<NSFastEnumeration> collection; |
| 333 | + NSFastEnumerationState state = {0}; |
| 334 | + id stackbuf[16]; |
| 335 | + BOOL firstLoop = YES; |
| 336 | + long mutationsPtrValue; |
| 337 | +}; |
| 338 | + |
| 339 | +NAPI_FUNCTION(fastEnumeration) { |
| 340 | + napi_value jsThis; |
| 341 | + void *data; |
| 342 | + size_t argc = 0; |
| 343 | + |
| 344 | + napi_get_cb_info(env, cbinfo, &argc, nil, &jsThis, &data); |
| 345 | + |
| 346 | + id self = nil; |
| 347 | + napi_unwrap(env, jsThis, (void **)&self); |
| 348 | + |
| 349 | + if (self == nil) { |
| 350 | + napi_value result; |
| 351 | + napi_create_string_utf8(env, "(nil)", NAPI_AUTO_LENGTH, &result); |
| 352 | + return result; |
| 353 | + } |
| 354 | + |
| 355 | + if (![self conformsToProtocol:@protocol(NSFastEnumeration)]) { |
| 356 | + napi_throw_error(env, nil, "Object does not conform to NSFastEnumeration"); |
| 357 | + return nullptr; |
| 358 | + } |
| 359 | + |
| 360 | + auto iterator = new FastEnumerationIterator((id<NSFastEnumeration>)self); |
| 361 | + return iterator->toJS(env); |
| 362 | +} |
| 363 | + |
219 | 364 | std::string NativeObjectName = "NativeObject";
|
220 | 365 |
|
221 | 366 | // Bridge an Objective-C class to JavaScript on the fly. Runtime introspection
|
|
312 | 457 | this->prototype = make_ref(env, prototype);
|
313 | 458 |
|
314 | 459 | if (isNativeObject) {
|
315 |
| - napi_property_descriptor property = { |
316 |
| - .utf8name = nil, |
317 |
| - .name = jsSymbolFor(env, "nodejs.util.inspect.custom"), |
318 |
| - .method = JS_CustomInspect, |
319 |
| - .getter = nil, |
320 |
| - .setter = nil, |
321 |
| - .value = nil, |
322 |
| - .attributes = napi_enumerable, |
323 |
| - .data = nil, |
324 |
| - }; |
325 |
| - |
326 |
| - napi_define_properties(env, prototype, 1, &property); |
327 |
| - |
328 |
| - napi_value global, Symbol, SymbolDispose; |
| 460 | + napi_value global, Symbol, SymbolDispose, SymbolIterator; |
329 | 461 | napi_get_global(env, &global);
|
330 | 462 | napi_get_named_property(env, global, "Symbol", &Symbol);
|
| 463 | + napi_get_named_property(env, Symbol, "iterator", &SymbolIterator); |
331 | 464 | napi_get_named_property(env, Symbol, "dispose", &SymbolDispose);
|
332 | 465 | napi_valuetype type;
|
333 | 466 | napi_typeof(env, SymbolDispose, &type);
|
334 | 467 |
|
| 468 | + napi_property_descriptor properties[] = { |
| 469 | + { |
| 470 | + .utf8name = nil, |
| 471 | + .name = jsSymbolFor(env, "nodejs.util.inspect.custom"), |
| 472 | + .method = JS_CustomInspect, |
| 473 | + .getter = nil, |
| 474 | + .setter = nil, |
| 475 | + .value = nil, |
| 476 | + .attributes = napi_enumerable, |
| 477 | + .data = nil, |
| 478 | + }, |
| 479 | + { |
| 480 | + .utf8name = "toString", |
| 481 | + .name = nil, |
| 482 | + .method = JS_CustomInspect, |
| 483 | + .getter = nil, |
| 484 | + .setter = nil, |
| 485 | + .value = nil, |
| 486 | + .attributes = napi_enumerable, |
| 487 | + .data = nil, |
| 488 | + }, |
| 489 | + { |
| 490 | + .utf8name = nil, |
| 491 | + .name = SymbolIterator, |
| 492 | + .method = JS_fastEnumeration, |
| 493 | + .getter = nil, |
| 494 | + .setter = nil, |
| 495 | + .value = nil, |
| 496 | + .attributes = napi_enumerable, |
| 497 | + .data = nil, |
| 498 | + }}; |
| 499 | + |
| 500 | + napi_define_properties(env, prototype, 3, properties); |
| 501 | + |
335 | 502 | if (type == napi_symbol) {
|
336 |
| - property.name = SymbolDispose; |
337 |
| - property.method = JS_releaseObject; |
| 503 | + properties[0].name = SymbolDispose; |
| 504 | + properties[0].method = JS_releaseObject; |
338 | 505 |
|
339 |
| - napi_define_properties(env, prototype, 1, &property); |
| 506 | + napi_define_properties(env, prototype, 1, properties); |
340 | 507 | }
|
341 | 508 |
|
342 | 509 | return;
|
|
0 commit comments