@scriptc/runtime 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -119,6 +119,12 @@ static void scr_dyn_display_buf(ScrJsonBuf *b, const ScrDyn *d) {
119
119
  * pending and appends nothing — the loud path). */
120
120
  scr_dyn_isl_tostr_buf(b, d);
121
121
  return;
122
+ case SCR_DYN_TYPED_REF: {
123
+ ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
124
+ scr_dyn_display_buf(b, materialized);
125
+ scr_dyn_release(materialized);
126
+ return;
127
+ }
122
128
  }
123
129
  }
124
130
 
@@ -306,6 +312,13 @@ static bool dyn_arr_proto_unimpl(const char *m) {
306
312
  for (size_t i = 0; names[i]; i++) if (dyn_name_is(m, names[i])) return true;
307
313
  return false;
308
314
  }
315
+ static bool dyn_arr_proto_mutates(const char *m) {
316
+ static const char *names[] = {
317
+ "push", "pop", "shift", "unshift", "reverse", "sort", NULL
318
+ };
319
+ for (size_t i = 0; names[i]; i++) if (dyn_name_is(m, names[i])) return true;
320
+ return false;
321
+ }
309
322
  static bool dyn_bytes_proto_real(const char *m) {
310
323
  static const char *names[] = { "slice", "at", "indexOf", "lastIndexOf", "includes", "join",
311
324
  "forEach", "map", "filter", "some", "every", "find", "findIndex", "reverse", "fill", "set",
@@ -315,7 +328,19 @@ static bool dyn_bytes_proto_real(const char *m) {
315
328
  return false;
316
329
  }
317
330
 
318
- ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc, const char *what) {
331
+ static ScrDyn *scr_dyn_invoke_impl(
332
+ ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc,
333
+ const char *what, ScrDyn *callback_recv);
334
+
335
+ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method,
336
+ ScrDyn *const *args, size_t argc,
337
+ const char *what) {
338
+ return scr_dyn_invoke_impl(recv, method, args, argc, what, NULL);
339
+ }
340
+
341
+ static ScrDyn *scr_dyn_invoke_impl(
342
+ ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc,
343
+ const char *what, ScrDyn *callback_recv) {
319
344
  if (recv->kind == SCR_DYN_UNDEF || recv->kind == SCR_DYN_NULL) {
320
345
  ScrJsonBuf b;
321
346
  scr_jb_init(&b);
@@ -347,6 +372,26 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
347
372
  return scr_dyn_jsval_ops()->invoke(recv->v.jsval.cell, method, args, argc, what);
348
373
  }
349
374
 
375
+ /* Live Web-boundary capsules expose the prototype of their materialized
376
+ * value. Run the ordinary dispatch against the stable snapshot, then
377
+ * commit successful mutations back into the original static value.
378
+ * Mutators such as reverse/sort return their receiver, so translate that
379
+ * snapshot identity back to the externally visible capsule. */
380
+ if (recv->kind == SCR_DYN_TYPED_REF) {
381
+ ScrDyn *materialized = scr_dyn_typed_ref_materialize(recv);
382
+ bool mutates = materialized->kind == SCR_DYN_ARR &&
383
+ dyn_arr_proto_mutates(method);
384
+ ScrDyn *result = scr_dyn_invoke_impl(
385
+ materialized, method, args, argc, what, recv);
386
+ if (mutates && !scr_exc_pending()) scr_dyn_typed_ref_commit(recv);
387
+ if (result == materialized) {
388
+ scr_dyn_release(result);
389
+ result = scr_dyn_retain(recv);
390
+ }
391
+ scr_dyn_release(materialized);
392
+ return result;
393
+ }
394
+
350
395
  /* OBJ: the own member calls (own properties shadow prototypes in JS
351
396
  * too); anything else is Node's is-not-a-function. */
352
397
  if (recv->kind == SCR_DYN_OBJ) {
@@ -556,11 +601,17 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
556
601
  if (!dyn_cb_check(args, argc)) return NULL;
557
602
  ScrDyn *cb = args[0];
558
603
  ScrDyn *out = (dyn_name_is(method, "map") || dyn_name_is(method, "filter")) ? scr_dyn_new_arr() : NULL;
559
- /* JS iterates the LIVE array (a push mid-loop is visited); reading
560
- * len fresh each step matches that. */
561
- for (size_t i = 0; i < recv->v.arr.len; i++) {
604
+ /* Array iteration methods snapshot length before the first callback.
605
+ * A shrinking callback can remove a later dense entry; an expanding
606
+ * callback must not make the new tail observable to this pass. Live
607
+ * Web-boundary capsules are the externally visible receiver, so pass
608
+ * that capsule as callback arg 3: mutations through the callback's
609
+ * array argument then commit directly to the original static array. */
610
+ size_t n = len;
611
+ ScrDyn *visible_recv = callback_recv ? callback_recv : recv;
612
+ for (size_t i = 0; i < n && i < recv->v.arr.len; i++) {
562
613
  ScrDyn *item = scr_dyn_retain(recv->v.arr.items[i]);
563
- ScrDyn *r = dyn_call_cb(cb, item, i, recv);
614
+ ScrDyn *r = dyn_call_cb(cb, item, i, visible_recv);
564
615
  if (!r) { scr_dyn_release(item); scr_dyn_release(out); return NULL; }
565
616
  if (dyn_name_is(method, "map")) {
566
617
  scr_dyn_arr_push(out, r); /* ownership moves in */
@@ -597,7 +648,8 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
597
648
  size_t n = recv->v.arr.len;
598
649
  for (size_t i = 0; i < n && i < recv->v.arr.len; i++) {
599
650
  ScrDyn *item = scr_dyn_retain(recv->v.arr.items[i]);
600
- ScrDyn *r = dyn_call_cb(cb, item, i, recv);
651
+ ScrDyn *r = dyn_call_cb(
652
+ cb, item, i, callback_recv ? callback_recv : recv);
601
653
  scr_dyn_release(item);
602
654
  if (!r) { scr_dyn_release(out); return NULL; }
603
655
  if (r->kind == SCR_DYN_ARR) {