@scriptc/runtime 0.0.10 → 0.0.12
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.
- package/package.json +1 -1
- package/src/scr_assert.c +13 -13
- package/src/scr_async.c +29 -9
- package/src/scr_async_dyn.c +24 -24
- package/src/scr_bytes.c +40 -3
- package/src/scr_bytes_io.c +5 -5
- package/src/scr_dc.c +8 -8
- package/src/scr_dgram.c +1 -1
- package/src/scr_dyn_handle.c +5 -5
- package/src/scr_dyn_invoke.c +15 -15
- package/src/scr_error.c +3 -3
- package/src/scr_events_emitter.c +59 -6
- package/src/scr_http.c +4 -4
- package/src/scr_http2.c +3 -3
- package/src/scr_inspect.c +3 -3
- package/src/scr_island.c +134 -25
- package/src/scr_json.c +103 -68
- package/src/scr_library.c +15 -0
- package/src/scr_net.c +1 -1
- package/src/scr_qs.c +1 -1
- package/src/scr_regex.c +39 -6
- package/src/scr_runtime.h +204 -131
- package/src/scr_stream.c +218 -11
- package/src/scr_tls.c +3 -3
- package/src/scr_web.c +1 -1
package/src/scr_dyn_invoke.c
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* Prototype-method dispatch on checked-dynamic receivers (scr_dyn_invoke)
|
|
2
|
-
* and its companions: JS String() over the
|
|
2
|
+
* and its companions: JS String() over the checked-dynamic tree (scr_dyn_display — join
|
|
3
3
|
* and the error texts need it standalone) and Object.defineProperties
|
|
4
|
-
* over
|
|
4
|
+
* over dyn values (scr_dyn_define_props). Linked only when the IR
|
|
5
5
|
* carries dynInvoke nodes or dyn.defineProps calls (cc.ts gates on
|
|
6
6
|
* moduleUsesDynInvoke — the scr_assert.c precedent), so dispatch-free
|
|
7
|
-
* binaries keep their exact size class. The
|
|
7
|
+
* binaries keep their exact size class. The checked-dynamic tree itself lives in
|
|
8
8
|
* scr_json.c; this unit uses only its public surface plus ScrJsonBuf.
|
|
9
9
|
*/
|
|
10
10
|
#include "scr_runtime.h"
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
/* ── checked-dynamic METHOD DISPATCH (scr_dyn_invoke) ──────────────────
|
|
18
18
|
*
|
|
19
|
-
* `recv.m(args)` where recv is a
|
|
20
|
-
*
|
|
19
|
+
* `recv.m(args)` where recv is a dyn value and `m` is a name a
|
|
20
|
+
* dyn-representable prototype declares (Array/String/Function shared
|
|
21
21
|
* names — push, slice, forEach, apply, ...): a stored-member read would
|
|
22
22
|
* silently mis-answer real methods, so the dispatch runs HERE, over the
|
|
23
23
|
* receiver's runtime kind. test/common's mustCall internals are the
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
* recv/args are BORROWED; the result is owned (+1). MAY THROW (returns
|
|
39
39
|
* NULL with the exception pending). */
|
|
40
40
|
|
|
41
|
-
/* JS String() over a
|
|
41
|
+
/* JS String() over a dyn value, runtime-side (join needs it standalone —
|
|
42
42
|
* the emitted sc_ds walker exists only in programs that spell
|
|
43
43
|
* String(unknown) themselves). Same rules: arrays join with "," and
|
|
44
|
-
* null/undefined elements print empty, the
|
|
44
|
+
* null/undefined elements print empty, the checked-dynamic tree's error encoding renders
|
|
45
45
|
* "name: message", functions render the native-code form, plain objects
|
|
46
46
|
* are [object Object]. */
|
|
47
47
|
static void scr_dyn_display_buf(ScrJsonBuf *b, const ScrDyn *d) {
|
|
@@ -192,7 +192,7 @@ static bool dyn_cb_check(ScrDyn *const *args, size_t argc) {
|
|
|
192
192
|
ScrDyn *cb = argc > 0 ? args[0] : scr_dyn_undefined();
|
|
193
193
|
if (cb->kind == SCR_DYN_FUNC) return true;
|
|
194
194
|
/* An ENGINE function is callable — scr_dyn_call's JSVAL arm routes it
|
|
195
|
-
* (the loops below call through scr_dyn_call, which converts the
|
|
195
|
+
* (the loops below call through scr_dyn_call, which converts the checked-dynamic tree
|
|
196
196
|
* element arguments per the uniform crossing). A wrapped NON-function
|
|
197
197
|
* falls through to the display path: String(cb) renders through the
|
|
198
198
|
* engine, exactly Node's message. */
|
|
@@ -215,7 +215,7 @@ static ScrDyn *scr_dynh_dispatch(ScrDyn *recv, const char *method, ScrDyn *const
|
|
|
215
215
|
return ops->invoke(recv->v.handle.ptr, recv, method, args, argc, what);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
/* JS Array.prototype.sort over a
|
|
218
|
+
/* JS Array.prototype.sort over a dyn array: the spec's snapshot-sort —
|
|
219
219
|
* elements copy (retained) into a work list, a stable merge sort orders
|
|
220
220
|
* it (undefined elements sink to the end before any comparator runs),
|
|
221
221
|
* and the ordered list writes back index by index, so a comparator that
|
|
@@ -339,7 +339,7 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
339
339
|
/* Island-held receivers: the ENGINE runs its own prototypes (JS-exact
|
|
340
340
|
* flatMap/map/forEach/filter — Array.prototype is the engine's) through
|
|
341
341
|
* scr_jsval_call_method; arguments cross per the uniform conversion
|
|
342
|
-
* (wrapped cells by reference,
|
|
342
|
+
* (wrapped cells by reference, dyn data deep-copied, FUNC boxes through
|
|
343
343
|
* the host shim), a missing member throws the engine's own TypeError,
|
|
344
344
|
* and the result wraps back scalar-normalized. `what` is unused — the
|
|
345
345
|
* engine's message names the failure. */
|
|
@@ -352,7 +352,7 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
352
352
|
if (recv->kind == SCR_DYN_OBJ) {
|
|
353
353
|
ScrDyn *m = scr_dyn_obj_get(recv, method, strlen(method));
|
|
354
354
|
if (m && (m->kind == SCR_DYN_FUNC ||
|
|
355
|
-
/* a WRAPPED engine function stored as a
|
|
355
|
+
/* a WRAPPED engine function stored as a dyn member: the
|
|
356
356
|
* routed call (scr_dyn_call's JSVAL arm) runs it. */
|
|
357
357
|
(m->kind == SCR_DYN_JSVAL && scr_dyn_isl_typeof_is(m, "function")))) {
|
|
358
358
|
/* JS binds the receiver for the call (`obj.method()` — this === obj):
|
|
@@ -584,8 +584,8 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
584
584
|
return scr_dyn_retain(scr_dyn_undefined()); /* forEach */
|
|
585
585
|
}
|
|
586
586
|
if (dyn_name_is(method, "flatMap")) {
|
|
587
|
-
/* JS Array.prototype.flatMap over a
|
|
588
|
-
* flatten. Native
|
|
587
|
+
/* JS Array.prototype.flatMap over a dyn array: map + a depth-1
|
|
588
|
+
* flatten. Native dyn-array results flatten element-by-element; a
|
|
589
589
|
* WRAPPED engine array flattens through the routed keyed reads
|
|
590
590
|
* (elements wrap back scalar-normalized); everything else pushes
|
|
591
591
|
* as a single element (JS keeps non-array callback results whole).
|
|
@@ -682,9 +682,9 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
682
682
|
return NULL;
|
|
683
683
|
}
|
|
684
684
|
|
|
685
|
-
/* Object.defineProperties over
|
|
685
|
+
/* Object.defineProperties over dyn values (see scr_runtime.h). Value
|
|
686
686
|
* descriptors only: writable/enumerable/configurable accepted and IGNORED
|
|
687
|
-
* (
|
|
687
|
+
* (dyn properties are plain data properties — SEMANTICS.md); get/set
|
|
688
688
|
* throw the loud unsupported Error, never a silent drop. */
|
|
689
689
|
ScrDyn *scr_dyn_define_props(ScrDyn *target, ScrDyn *descs) {
|
|
690
690
|
/* Island-held operands ARE objects to Node — the non-object TypeError
|
package/src/scr_error.c
CHANGED
|
@@ -44,9 +44,9 @@ static void scr_error_gcfree(void *obj) {
|
|
|
44
44
|
scr_cyc_free(e);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/* DOMException teardown: the ScrError fields plus the owned cause
|
|
47
|
+
/* DOMException teardown: the ScrError fields plus the owned cause dyn
|
|
48
48
|
* value. The cause is a ScrDyn — scr_json.c territory — and this file
|
|
49
|
-
* must stay linkable WITHOUT the
|
|
49
|
+
* must stay linkable WITHOUT the checked-dynamic tree (the runtime C-unit tests link
|
|
50
50
|
* subsets), so the drop goes through a hook scr_json.c installs before
|
|
51
51
|
* any cause can exist (scr_domex_new is the only producer). */
|
|
52
52
|
static void (*scr_domex_cause_drop)(void *obj) = NULL;
|
|
@@ -273,7 +273,7 @@ void scr_undef_global_read(ScrStr *name) {
|
|
|
273
273
|
/* ── DOMException ─────────────────────────────────────────────────────
|
|
274
274
|
* The web-standard error shape (a Node global since v17): the ScrError
|
|
275
275
|
* prefix plus the WebIDL slots (legacy numeric code, the options form's
|
|
276
|
-
* cause). Construction resolves WebIDL's optionality over borrowed
|
|
276
|
+
* cause). Construction resolves WebIDL's optionality over borrowed dyn
|
|
277
277
|
* arguments — one place, shared by the compiled `new DOMException(...)`
|
|
278
278
|
* and the runtime's own throw sites (atob/btoa). */
|
|
279
279
|
|
package/src/scr_events_emitter.c
CHANGED
|
@@ -85,6 +85,12 @@ typedef struct ScrEeBucket {
|
|
|
85
85
|
struct ScrEeReg {
|
|
86
86
|
ScrEeBucket *head; /* singly linked, first-registration order */
|
|
87
87
|
double max; /* -1 = unset (follows the default), 0 = unlimited */
|
|
88
|
+
/* Node's kShapeMode: set when names were PRE-CREATED (the stream
|
|
89
|
+
* constructors). In shape mode removeListener KEEPS an emptied name's
|
|
90
|
+
* rank (Node writes events[type] = undefined instead of deleting) —
|
|
91
|
+
* only removeAllListeners deletes: the named form drops the one key,
|
|
92
|
+
* the no-argument wipe resets everything and leaves shape mode. */
|
|
93
|
+
bool shape;
|
|
88
94
|
};
|
|
89
95
|
|
|
90
96
|
/* The vtable of BARE `new EventEmitter()` instances; the emitted main()
|
|
@@ -145,6 +151,21 @@ static ScrEeBucket *scr_ee_bucket_ensure(ScrEeReg *reg, ScrStr *name) {
|
|
|
145
151
|
return b;
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
/* Reserve a name's first-registration RANK without any listener: Node's
|
|
155
|
+
* stream classes pre-create their known _events keys (a V8 shape
|
|
156
|
+
* optimization eventNames() order observes — 'error'/'data'/... list
|
|
157
|
+
* before user events added earlier). An empty bucket is ABSENT for every
|
|
158
|
+
* read (n == 0 everywhere); the first add fills it in place, and a bucket
|
|
159
|
+
* emptied by removal still drops — Node deleting the key. */
|
|
160
|
+
void scr_emitter_reserve(ScrEmitter *em, const char *name) {
|
|
161
|
+
ScrEeReg *reg = scr_ee_reg_ensure(em);
|
|
162
|
+
reg->shape = true;
|
|
163
|
+
if (scr_ee_bucket_find(reg, name, strlen(name))) return;
|
|
164
|
+
ScrStr *n = scr_str_new(name, strlen(name));
|
|
165
|
+
scr_ee_bucket_ensure(reg, n);
|
|
166
|
+
scr_str_release(n);
|
|
167
|
+
}
|
|
168
|
+
|
|
148
169
|
/* Drops an emptied bucket from the list (Node deletes the _events key; a
|
|
149
170
|
* later add re-appends at the end). */
|
|
150
171
|
static void scr_ee_bucket_drop(ScrEeReg *reg, ScrEeBucket *b) {
|
|
@@ -402,13 +423,14 @@ void scr_ee_inv_fixed4(ScrClosure *cb, va_list ap) {
|
|
|
402
423
|
|
|
403
424
|
/* Removes one entry (by index) from a bucket's live list and fires the
|
|
404
425
|
* 'removeListener' meta event AFTER the removal, Node's order. Drops the
|
|
405
|
-
* bucket when emptied
|
|
426
|
+
* bucket when emptied — except in shape mode, where the emptied name
|
|
427
|
+
* keeps its rank (Node's events[type] = undefined). */
|
|
406
428
|
static void scr_ee_remove_at(ScrEmitter *em, ScrEeBucket *b, size_t i) {
|
|
407
429
|
ScrEeEntry *e = b->ls[i];
|
|
408
430
|
memmove(b->ls + i, b->ls + i + 1, (b->n - i - 1) * sizeof *b->ls);
|
|
409
431
|
b->n--;
|
|
410
432
|
ScrStr *name = scr_str_retain(b->name);
|
|
411
|
-
if (b->n == 0) scr_ee_bucket_drop(em->reg, b);
|
|
433
|
+
if (b->n == 0 && !em->reg->shape) scr_ee_bucket_drop(em->reg, b);
|
|
412
434
|
scr_ee_entry_unref(e);
|
|
413
435
|
scr_ee_emit_meta(em, "removeListener", name);
|
|
414
436
|
scr_str_release(name);
|
|
@@ -444,7 +466,7 @@ ScrEmitter *scr_emitter_off_dyn(ScrEmitter *em, ScrStr *name, const ScrDyn *cb)
|
|
|
444
466
|
* the simple '-quoted spelling — listener mistakes are words, not escape
|
|
445
467
|
* zoos). */
|
|
446
468
|
void scr_emitter_check_listener(const ScrDyn *cb) {
|
|
447
|
-
/* The shared gate lives in the always-linked
|
|
469
|
+
/* The shared gate lives in the always-linked dyn core (scr_json.c) so
|
|
448
470
|
* the handle dispatchers' .on paths render the same shapes without
|
|
449
471
|
* linking this unit. */
|
|
450
472
|
scr_dyn_check_listener(cb, "listener");
|
|
@@ -456,6 +478,13 @@ void scr_emitter_check_listener(const ScrDyn *cb) {
|
|
|
456
478
|
* firing the meta event — and the whole-emitter form does every other
|
|
457
479
|
* event first (bucket order), 'removeListener' itself LAST. Returns em +1. */
|
|
458
480
|
static void scr_ee_remove_all_named(ScrEmitter *em, ScrEeBucket *b, bool meta) {
|
|
481
|
+
if (b->n == 0) {
|
|
482
|
+
/* An empty rank-holding bucket reaches here only from the WIPE scan
|
|
483
|
+
* (the named form skips undefined keys — Node no-ops and the rank
|
|
484
|
+
* stays): drop it, the wipe resets _events wholesale. */
|
|
485
|
+
scr_ee_bucket_drop(em->reg, b);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
459
488
|
if (!meta) {
|
|
460
489
|
for (size_t i = 0; i < b->n; i++) scr_ee_entry_unref(b->ls[i]);
|
|
461
490
|
b->n = 0;
|
|
@@ -497,7 +526,26 @@ ScrEmitter *scr_emitter_remove_all(ScrEmitter *em, ScrStr *name, bool all) {
|
|
|
497
526
|
bool meta = scr_ee_has(reg, "removeListener");
|
|
498
527
|
if (!all) {
|
|
499
528
|
ScrEeBucket *b = scr_ee_bucket_find(reg, name->data, name->len);
|
|
500
|
-
|
|
529
|
+
/* A rank-holding empty bucket is Node's `events[type] === undefined`:
|
|
530
|
+
* the named form no-ops there (the rank survives). A POPULATED name
|
|
531
|
+
* deletes its key even in shape mode — but through the meta path each
|
|
532
|
+
* listener leaves via removeListener, whose shape rule KEEPS the
|
|
533
|
+
* emptied rank (Node's exact split). */
|
|
534
|
+
if (b && b->n > 0) {
|
|
535
|
+
scr_ee_remove_all_named(em, b, meta);
|
|
536
|
+
if (!meta) {
|
|
537
|
+
/* Node's non-meta named branch: the count hitting zero replaces
|
|
538
|
+
* _events wholesale — every rank (pre-created included) goes,
|
|
539
|
+
* though kShapeMode itself stays set (Node's exact quirk). */
|
|
540
|
+
bool any_live = false;
|
|
541
|
+
for (ScrEeBucket *w = reg->head; w; w = w->next) {
|
|
542
|
+
if (w->n > 0) { any_live = true; break; }
|
|
543
|
+
}
|
|
544
|
+
if (!any_live) {
|
|
545
|
+
while (reg->head) scr_ee_bucket_drop(reg, reg->head);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
501
549
|
return scr_emitter_retain(em);
|
|
502
550
|
}
|
|
503
551
|
/* Every event except 'removeListener' first, in bucket order; then
|
|
@@ -515,6 +563,10 @@ ScrEmitter *scr_emitter_remove_all(ScrEmitter *em, ScrStr *name, bool all) {
|
|
|
515
563
|
}
|
|
516
564
|
ScrEeBucket *rl = scr_ee_bucket_find(reg, "removeListener", 14);
|
|
517
565
|
if (rl) scr_ee_remove_all_named(em, rl, meta);
|
|
566
|
+
/* The wipe replaces _events wholesale in Node — pre-created ranks are
|
|
567
|
+
* gone and the emitter leaves shape mode. */
|
|
568
|
+
while (reg->head) scr_ee_bucket_drop(reg, reg->head);
|
|
569
|
+
reg->shape = false;
|
|
518
570
|
return scr_emitter_retain(em);
|
|
519
571
|
}
|
|
520
572
|
|
|
@@ -633,12 +685,13 @@ double scr_emitter_listener_count_fn(ScrEmitter *em, ScrStr *name, ScrClosure *f
|
|
|
633
685
|
}
|
|
634
686
|
|
|
635
687
|
/* eventNames(): +1 string[] of the bucket names in first-registration
|
|
636
|
-
* order (exactly Node's _events key order).
|
|
688
|
+
* order (exactly Node's _events key order). Reserved-but-empty buckets
|
|
689
|
+
* (the stream pre-created keys) are invisible until a listener lands. */
|
|
637
690
|
ScrArr *scr_emitter_event_names(ScrEmitter *em) {
|
|
638
691
|
ScrArr *out = scr_arr_new(SCR_ELEM_STR, 0);
|
|
639
692
|
if (em->reg) {
|
|
640
693
|
for (ScrEeBucket *b = em->reg->head; b; b = b->next) {
|
|
641
|
-
scr_arr_push_ref(out, scr_str_retain(b->name));
|
|
694
|
+
if (b->n > 0) scr_arr_push_ref(out, scr_str_retain(b->name));
|
|
642
695
|
}
|
|
643
696
|
}
|
|
644
697
|
return out;
|
package/src/scr_http.c
CHANGED
|
@@ -2774,7 +2774,7 @@ void scr_http_resp_thunk_res(ScrClosure *cb, ScrHttpReq *res) {
|
|
|
2774
2774
|
*
|
|
2775
2775
|
* The receiver surface behind `server.on('request', mustCall((req, res)
|
|
2776
2776
|
* => ...))`: the mustCall wrapper makes the listener dyn, req/res box
|
|
2777
|
-
* into the
|
|
2777
|
+
* into the checked-dynamic tree as HANDLE values, and member uses inside the listener
|
|
2778
2778
|
* body dispatch HERE — onto the very entry points the static lowerings
|
|
2779
2779
|
* use, with per-argument gates shaped like Node's (the libCall signature
|
|
2780
2780
|
* table in validate.ts is the modeled surface; this dispatcher mirrors
|
|
@@ -2785,7 +2785,7 @@ void scr_http_resp_thunk_res(ScrClosure *cb, ScrHttpReq *res) {
|
|
|
2785
2785
|
* - a member the class HAS but this table does not model: a LOUD
|
|
2786
2786
|
* "not supported yet" Error — never a silent wrong answer;
|
|
2787
2787
|
* - anything else: Node's "<spelling> is not a function" for calls,
|
|
2788
|
-
* the undefined singleton for reads (the
|
|
2788
|
+
* the undefined singleton for reads (the checked-dynamic tree's own-property answer;
|
|
2789
2789
|
* SEMANTICS.md documents the remainder).
|
|
2790
2790
|
*
|
|
2791
2791
|
* Ownership: invoke/get/set receive BORROWED handles/args and answer
|
|
@@ -3009,7 +3009,7 @@ static ScrDyn *scr_http_dynh_req_get(void *h, const char *key, size_t key_len) {
|
|
|
3009
3009
|
double st = scr_http_req_status(r);
|
|
3010
3010
|
/* Node's IncomingMessage constructor sets statusCode = null; only a
|
|
3011
3011
|
* client response assigns a number (the static lane's typed surface
|
|
3012
|
-
* spells this number|undefined — the
|
|
3012
|
+
* spells this number|undefined — the checked-dynamic tree can afford Node's null). */
|
|
3013
3013
|
return st < 0 ? scr_dyn_new_null() : scr_dyn_new_num(st);
|
|
3014
3014
|
}
|
|
3015
3015
|
if (strcmp(key, "statusMessage") == 0) {
|
|
@@ -3049,7 +3049,7 @@ static bool scr_http_dynh_req_set(void *h, const char *key, size_t key_len, cons
|
|
|
3049
3049
|
|
|
3050
3050
|
/* ── ServerResponse (SCR_DYNH_HTTP_RES) ──────────────────────────────── */
|
|
3051
3051
|
|
|
3052
|
-
/* writeHead's header-object form over a
|
|
3052
|
+
/* writeHead's header-object form over a dyn OBJ: flatten into the
|
|
3053
3053
|
* [k0, v0, k1, v1, ...] pairs the static lowering feeds
|
|
3054
3054
|
* scr_http_res_write_head_pairs (repeats append — array values expand to
|
|
3055
3055
|
* consecutive same-name pairs). Numbers format through String(n). NULL =
|
package/src/scr_http2.c
CHANGED
|
@@ -2589,7 +2589,7 @@ void scr_http2_settings_thunk0(ScrClosure *cb, ScrDyn *settings) {
|
|
|
2589
2589
|
}
|
|
2590
2590
|
|
|
2591
2591
|
/* The typed lane's DYN spellings (session receivers with mustCall-style
|
|
2592
|
-
* callbacks): the raw
|
|
2592
|
+
* callbacks): the raw dyn function crosses the lib boundary and fires
|
|
2593
2593
|
* through the same runtime-built listener closures the dyn handle ops
|
|
2594
2594
|
* use — Node's exact payload shapes. */
|
|
2595
2595
|
static void scr_h2_dyn_fire_settings(ScrClosure *cb, ScrDyn *settings /*+1*/);
|
|
@@ -2689,7 +2689,7 @@ void scr_http2_goaway_thunk0(ScrClosure *cb, double code, double last) {
|
|
|
2689
2689
|
* parameter, or a handle stored in an untyped binding) dispatch their
|
|
2690
2690
|
* members here, the scr_http.c req/res ops precedent: `.on(...)` wires a
|
|
2691
2691
|
* runtime-built listener closure over the dyn callback, header events
|
|
2692
|
-
* build a
|
|
2692
|
+
* build a dyn headers object from the pairs, method calls decode dyn
|
|
2693
2693
|
* args onto the static entry points, and property reads box the scalar
|
|
2694
2694
|
* answers. Real-but-unmodeled members throw the loud ladder; names the
|
|
2695
2695
|
* surface never had throw Node's "<x> is not a function". */
|
|
@@ -2791,7 +2791,7 @@ static void scr_h2_dyn_fire_goaway(ScrClosure *cb, double code, double last) {
|
|
|
2791
2791
|
scr_dyn_release(fn);
|
|
2792
2792
|
}
|
|
2793
2793
|
|
|
2794
|
-
/* Flatten a
|
|
2794
|
+
/* Flatten a dyn headers object into the flat pairs array (numbers via
|
|
2795
2795
|
* String(n); the :status/:method/... pseudo-headers pass through). */
|
|
2796
2796
|
static ScrArr *scr_h2_dom_to_pairs(const ScrDyn *headers) {
|
|
2797
2797
|
ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, headers->v.obj.len * 2);
|
package/src/scr_inspect.c
CHANGED
|
@@ -700,7 +700,7 @@ ScrStr *scr_insp_error(ScrError *e, double recurse, double depth) {
|
|
|
700
700
|
return out;
|
|
701
701
|
}
|
|
702
702
|
|
|
703
|
-
/* ── dyn values (the checked-dynamic JSON
|
|
703
|
+
/* ── dyn values (the checked-dynamic JSON dyn) ───────────────────────── */
|
|
704
704
|
|
|
705
705
|
/* Property-name rendering: bare when the key matches Node's keyStrRegExp
|
|
706
706
|
* (/^[a-zA-Z_][a-zA-Z_0-9]*$/, '__proto__' excepted), quoted otherwise. */
|
|
@@ -735,7 +735,7 @@ ScrStr *scr_insp_key(ScrStr *k) {
|
|
|
735
735
|
/* The full inspect over a dyn tree — the one runtime type whose SHAPE
|
|
736
736
|
* lives in the value, so the traversal lives here instead of a
|
|
737
737
|
* synthesized helper. Same engine, same defaults. dyn-boxed bytes render
|
|
738
|
-
* in the
|
|
738
|
+
* in the checked-dynamic tree's documented Uint8Array identity (SEMANTICS.md). */
|
|
739
739
|
ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
|
740
740
|
switch (d->kind) {
|
|
741
741
|
case SCR_DYN_NULL:
|
|
@@ -837,7 +837,7 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
|
|
837
837
|
return scr_str_new("", 0);
|
|
838
838
|
}
|
|
839
839
|
case SCR_DYN_JSVAL: {
|
|
840
|
-
/* An island value inside the
|
|
840
|
+
/* An island value inside the checked-dynamic tree: Node prints the engine object's
|
|
841
841
|
* property dump — fence loudly, naming the engine typeof (the
|
|
842
842
|
* scr_insp_jsval wording for bare 'any' composites). */
|
|
843
843
|
ScrStr *t = scr_dyn_typeof(d); /* routes to the engine; +1 */
|
package/src/scr_island.c
CHANGED
|
@@ -383,6 +383,7 @@ enum {
|
|
|
383
383
|
ISL_H_OBJWALK,
|
|
384
384
|
ISL_H_HASOWN,
|
|
385
385
|
ISL_H_ASSIGN,
|
|
386
|
+
ISL_H_ITERDRAIN,
|
|
386
387
|
ISL_H_COUNT,
|
|
387
388
|
};
|
|
388
389
|
|
|
@@ -437,7 +438,22 @@ static const char isl_prelude[] =
|
|
|
437
438
|
* semantics (own-key order, getters running, ToObject refusals). */
|
|
438
439
|
"(o,m)=>m===0?Object.keys(o):m===1?Object.values(o):Object.entries(o),"
|
|
439
440
|
"(o,k)=>Object.hasOwn(o,k),"
|
|
440
|
-
"(t,s)=>Object.assign(t,s)
|
|
441
|
+
"(t,s)=>Object.assign(t,s),"
|
|
442
|
+
/* ISL_H_ITERDRAIN: the ENGINE's own iterator protocol drained into a
|
|
443
|
+
* fresh array (for-of/destructuring/spread over a wrapped value —
|
|
444
|
+
* generators, Maps, Sets, Symbol.iterator implementations all step
|
|
445
|
+
* exactly as Node runs them). The guard front-runs the not-iterable
|
|
446
|
+
* TypeError in the CALLER's wording: m=1 is V8's spread-call text,
|
|
447
|
+
* s (when defined) the compile-time source spelling verbatim, else
|
|
448
|
+
* the kind wording (iterN's). */
|
|
449
|
+
"(v,m,s)=>{if(v===undefined||v===null||typeof v[Symbol.iterator]!==\"function\"){"
|
|
450
|
+
"if(m===1)throw new TypeError(\"Spread syntax requires ...iterable[Symbol.iterator] to be a function\");"
|
|
451
|
+
"if(s!==undefined)throw new TypeError(s);"
|
|
452
|
+
"let d;if(v===undefined)d=\"undefined\";else if(v===null)d=\"object null\";"
|
|
453
|
+
"else if(typeof v===\"number\")d=\"number \"+v;else if(typeof v===\"boolean\")d=\"boolean \"+v;"
|
|
454
|
+
"else if(typeof v===\"function\")d=\"function\";else d=\"object\";"
|
|
455
|
+
"throw new TypeError(d+\" is not iterable (cannot read property Symbol(Symbol.iterator))\")}"
|
|
456
|
+
"const o=[];for(const x of v)o.push(x);return o}]";
|
|
441
457
|
|
|
442
458
|
static void isl_free_boot(void);
|
|
443
459
|
static void isl_prom_wraps_teardown(void);
|
|
@@ -963,11 +979,11 @@ ScrJsval *scr_jsval_from_json(const ScrStr *json) {
|
|
|
963
979
|
return isl_cell_new(v);
|
|
964
980
|
}
|
|
965
981
|
|
|
966
|
-
/* ── marshal in: a CHECKED-DYNAMIC (
|
|
982
|
+
/* ── marshal in: a CHECKED-DYNAMIC (dyn) value ────────────────────────
|
|
967
983
|
* `unknown` flowing into 'any'-typed code (`const cfg = isJson ?
|
|
968
|
-
* JSON.parse(text) : islandParser(text)`): the
|
|
984
|
+
* JSON.parse(text) : islandParser(text)`): the dyn tree rebuilds as
|
|
969
985
|
* engine values — a DEEP COPY, the jsMarshal aliasing stance. Data kinds
|
|
970
|
-
* only: a
|
|
986
|
+
* only: a dyn carrying a boxed function, a native handle, or a promise
|
|
971
987
|
* throws the catchable TypeError naming the kind (the box's thunk calls
|
|
972
988
|
* STATIC code the engine cannot re-enter through a data copy). */
|
|
973
989
|
static const char *isl_dyn_unmarshalable(const ScrDyn *d) {
|
|
@@ -996,14 +1012,14 @@ static const char *isl_dyn_unmarshalable(const ScrDyn *d) {
|
|
|
996
1012
|
}
|
|
997
1013
|
}
|
|
998
1014
|
|
|
999
|
-
static JSValue isl_dynfn_new(const ScrDyn *d); /* the
|
|
1015
|
+
static JSValue isl_dynfn_new(const ScrDyn *d); /* the checked-dynamic tree-function shim, below */
|
|
1000
1016
|
|
|
1001
1017
|
static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
1002
1018
|
switch (d->kind) {
|
|
1003
1019
|
case SCR_DYN_FUNC:
|
|
1004
|
-
/* A boxed
|
|
1005
|
-
* its uniform call thunk (ScrDynThunk): engine args wrap as
|
|
1006
|
-
* values, the thunk runs, the
|
|
1020
|
+
/* A boxed dyn function enters as ONE generic host-function shim over
|
|
1021
|
+
* its uniform call thunk (ScrDynThunk): engine args wrap as dyn
|
|
1022
|
+
* values, the thunk runs, the dyn result converts back. Each
|
|
1007
1023
|
* crossing mints a fresh engine function (documented). */
|
|
1008
1024
|
return isl_dynfn_new(d);
|
|
1009
1025
|
case SCR_DYN_UNDEF:
|
|
@@ -1017,7 +1033,7 @@ static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
|
1017
1033
|
case SCR_DYN_STR:
|
|
1018
1034
|
return JS_NewStringLen(isl_ctx, d->v.str->data, d->v.str->len);
|
|
1019
1035
|
case SCR_DYN_BYTES:
|
|
1020
|
-
/* Only u8 payloads reach the
|
|
1036
|
+
/* Only u8 payloads reach the checked-dynamic tree today (scr_json.c's stringify note). */
|
|
1021
1037
|
return JS_NewUint8ArrayCopy(isl_ctx, d->v.bytes->data, d->v.bytes->len);
|
|
1022
1038
|
case SCR_DYN_ARR: {
|
|
1023
1039
|
JSValue arr = JS_NewArray(isl_ctx);
|
|
@@ -1047,7 +1063,7 @@ static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
|
1047
1063
|
return obj;
|
|
1048
1064
|
}
|
|
1049
1065
|
case SCR_DYN_JSVAL:
|
|
1050
|
-
/* An engine value riding inside
|
|
1066
|
+
/* An engine value riding inside dyn data: embed the SAME engine
|
|
1051
1067
|
* value by reference — the identity round trip, member position. */
|
|
1052
1068
|
return JS_DupValue(isl_ctx, d->v.jsval.cell->v);
|
|
1053
1069
|
default:
|
|
@@ -1057,7 +1073,7 @@ static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
|
1057
1073
|
}
|
|
1058
1074
|
|
|
1059
1075
|
ScrJsval *scr_jsval_from_dyn(const ScrDyn *d) {
|
|
1060
|
-
/* The identity round trip: an engine value that crossed into the
|
|
1076
|
+
/* The identity round trip: an engine value that crossed into the checked-dynamic tree
|
|
1061
1077
|
* (scr_dyn_from_jsval) and back is the SAME engine value, by reference
|
|
1062
1078
|
* — the one direction that used to throw (SEMANTICS.md supersedes the
|
|
1063
1079
|
* "one unbridgeable mix"). */
|
|
@@ -1079,10 +1095,10 @@ ScrJsval *scr_jsval_from_dyn(const ScrDyn *d) {
|
|
|
1079
1095
|
return isl_cell_new(v);
|
|
1080
1096
|
}
|
|
1081
1097
|
|
|
1082
|
-
/* ── the jsval→
|
|
1098
|
+
/* ── the jsval→dyn crossing (SCR_DYN_JSVAL's constructor + ops) ───────
|
|
1083
1099
|
* The reverse direction: an 'any'-typed engine value flowing into an
|
|
1084
|
-
* 'unknown'/'object'/JS-residue slot wraps BY REFERENCE as the
|
|
1085
|
-
* island kind. The
|
|
1100
|
+
* 'unknown'/'object'/JS-residue slot wraps BY REFERENCE as the checked-dynamic tree's
|
|
1101
|
+
* island kind. The dyn core (scr_json.c) stays island-free — it routes
|
|
1086
1102
|
* the armed operations (typeof/truthy/String()/===, the narrowing
|
|
1087
1103
|
* tests) through these installed ops; everything un-armed there keeps
|
|
1088
1104
|
* the loud "not supported yet" ladder. */
|
|
@@ -1101,7 +1117,7 @@ static bool isl_dynjs_is_error(ScrJsval *cell) { return JS_IsError(cell->v); }
|
|
|
1101
1117
|
|
|
1102
1118
|
static const ScrDynJsvalOps isl_dynjs_ops;
|
|
1103
1119
|
|
|
1104
|
-
/* The jsval→
|
|
1120
|
+
/* The jsval→dyn wrap over a RAW engine value (BORROWED) — the scalar
|
|
1105
1121
|
* normalization the cell constructor applies, shared by the routed-op
|
|
1106
1122
|
* result conversions (which hold a JSValue, not a cell). Composites mint
|
|
1107
1123
|
* a fresh cell over the SAME engine value (identity is the value — the
|
|
@@ -1249,7 +1265,7 @@ static ScrDyn *isl_dynjs_obj_walk(ScrJsval *cell, int mode) {
|
|
|
1249
1265
|
isl_bridge_exception();
|
|
1250
1266
|
return NULL;
|
|
1251
1267
|
}
|
|
1252
|
-
/* The engine array unpacks into a NATIVE
|
|
1268
|
+
/* The engine array unpacks into a NATIVE dyn array (keys are dyn
|
|
1253
1269
|
* strings, values wrap per element, entries become native pairs) so
|
|
1254
1270
|
* the results iterate/index/measure at native speed. */
|
|
1255
1271
|
int64_t len = 0;
|
|
@@ -1309,6 +1325,36 @@ static bool isl_dynjs_assign(ScrJsval *cell, const ScrDyn *src) {
|
|
|
1309
1325
|
|
|
1310
1326
|
static ScrStr *isl_dynjs_to_json(ScrJsval *cell) { return scr_jsval_to_json(cell); }
|
|
1311
1327
|
|
|
1328
|
+
static ScrDyn *isl_dynjs_iter_drain(ScrJsval *cell, bool spread, const ScrStr *spell) {
|
|
1329
|
+
isl_entry();
|
|
1330
|
+
JSValue m = JS_NewInt32(isl_ctx, spread ? 1 : 0);
|
|
1331
|
+
JSValue s = spell && spell->len > 0
|
|
1332
|
+
? JS_NewStringLen(isl_ctx, spell->data, spell->len)
|
|
1333
|
+
: JS_UNDEFINED;
|
|
1334
|
+
JSValue argv[3] = {cell->v, m, s};
|
|
1335
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_ITERDRAIN], JS_UNDEFINED, 3, argv);
|
|
1336
|
+
JS_FreeValue(isl_ctx, s);
|
|
1337
|
+
if (JS_IsException(r)) {
|
|
1338
|
+
isl_bridge_exception();
|
|
1339
|
+
return NULL;
|
|
1340
|
+
}
|
|
1341
|
+
/* The drained engine array unpacks into a fresh dyn array — elements
|
|
1342
|
+
* wrap back scalar-normalized (composites stay engine values by
|
|
1343
|
+
* reference), exactly the obj_walk unpack. */
|
|
1344
|
+
int64_t len = 0;
|
|
1345
|
+
JSValue lv = JS_GetPropertyStr(isl_ctx, r, "length");
|
|
1346
|
+
JS_ToInt64(isl_ctx, &len, lv);
|
|
1347
|
+
JS_FreeValue(isl_ctx, lv);
|
|
1348
|
+
ScrDyn *out = scr_dyn_new_arr();
|
|
1349
|
+
for (int64_t i = 0; i < len; i++) {
|
|
1350
|
+
JSValue e = JS_GetPropertyUint32(isl_ctx, r, (uint32_t)i);
|
|
1351
|
+
scr_dyn_arr_push(out, isl_dyn_from_value(e));
|
|
1352
|
+
JS_FreeValue(isl_ctx, e);
|
|
1353
|
+
}
|
|
1354
|
+
JS_FreeValue(isl_ctx, r);
|
|
1355
|
+
return out;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1312
1358
|
static const ScrDynJsvalOps isl_dynjs_ops = {
|
|
1313
1359
|
isl_dynjs_release,
|
|
1314
1360
|
isl_dynjs_typeof,
|
|
@@ -1326,14 +1372,15 @@ static const ScrDynJsvalOps isl_dynjs_ops = {
|
|
|
1326
1372
|
isl_dynjs_has_own,
|
|
1327
1373
|
isl_dynjs_assign,
|
|
1328
1374
|
isl_dynjs_to_json,
|
|
1375
|
+
isl_dynjs_iter_drain,
|
|
1329
1376
|
};
|
|
1330
1377
|
|
|
1331
1378
|
ScrDyn *scr_dyn_from_jsval(ScrJsval *cell) {
|
|
1332
1379
|
isl_entry();
|
|
1333
1380
|
JSValue v = cell->v;
|
|
1334
|
-
/* Scalar normalization: engine-reported scalars become the NATIVE
|
|
1381
|
+
/* Scalar normalization: engine-reported scalars become the NATIVE dyn
|
|
1335
1382
|
* kinds at wrap time (the strict exits cannot fail on them), so every
|
|
1336
|
-
* scalar path in the
|
|
1383
|
+
* scalar path in the dyn core — ===, typeof tests, JSON of leaves —
|
|
1337
1384
|
* stays untouched and the JSVAL kind never competes with them. */
|
|
1338
1385
|
if (JS_IsUndefined(v)) return scr_dyn_retain(scr_dyn_undefined());
|
|
1339
1386
|
if (JS_IsNull(v)) return scr_dyn_new_null();
|
|
@@ -1653,7 +1700,7 @@ static const JSClassDef isl_hostfn_class = {
|
|
|
1653
1700
|
.finalizer = isl_hostfn_finalizer,
|
|
1654
1701
|
};
|
|
1655
1702
|
|
|
1656
|
-
static JSClassID isl_dynfn_class_id; /* the
|
|
1703
|
+
static JSClassID isl_dynfn_class_id; /* the checked-dynamic tree-function shim, below */
|
|
1657
1704
|
static const JSClassDef isl_dynfn_class;
|
|
1658
1705
|
|
|
1659
1706
|
static void isl_register_hostfn_class(void) {
|
|
@@ -1818,13 +1865,13 @@ ScrJsval *scr_jsval_from_closure(ScrClosure *c, int arity,
|
|
|
1818
1865
|
return isl_cell_new(fn);
|
|
1819
1866
|
}
|
|
1820
1867
|
|
|
1821
|
-
/* ── the generic
|
|
1868
|
+
/* ── the generic dyn-function shim (a boxed SCR_DYN_FUNC entering the
|
|
1822
1869
|
* island) ─────────────────────────────────────────────────────────────
|
|
1823
1870
|
* ONE shim serves every boxed function because the box's call thunk is a
|
|
1824
|
-
* single uniform C signature (ScrDynThunk): engine arguments wrap as
|
|
1825
|
-
* values (scalar-normalizing — the jsval→
|
|
1871
|
+
* single uniform C signature (ScrDynThunk): engine arguments wrap as dyn
|
|
1872
|
+
* values (scalar-normalizing — the jsval→dyn constructor's stance), the
|
|
1826
1873
|
* thunk validates them against the closure's declared parameter types
|
|
1827
|
-
* and runs it, and the
|
|
1874
|
+
* and runs it, and the dyn result converts back through the from_dyn
|
|
1828
1875
|
* rules (wrapped cells by reference, data as a deep copy, nested
|
|
1829
1876
|
* functions through this same shim). OWNERSHIP: the engine function's
|
|
1830
1877
|
* opaque box owns ONE reference on the whole ScrDyn FUNC node (closure
|
|
@@ -1875,7 +1922,7 @@ static JSValue isl_dynfn_invoke(JSContext *ctx, JSValueConst this_val, int argc,
|
|
|
1875
1922
|
return isl_throw_pending(ctx);
|
|
1876
1923
|
}
|
|
1877
1924
|
if (!r) return JS_UNDEFINED;
|
|
1878
|
-
/* The thunk's
|
|
1925
|
+
/* The thunk's dyn result converts back per the from_dyn rules; a kind
|
|
1879
1926
|
* with no crossing (a handle, a promise) throws the same catchable
|
|
1880
1927
|
* TypeError from_dyn would. */
|
|
1881
1928
|
const char *bad = isl_dyn_unmarshalable(r);
|
|
@@ -1888,7 +1935,7 @@ static JSValue isl_dynfn_invoke(JSContext *ctx, JSValueConst this_val, int argc,
|
|
|
1888
1935
|
return out; /* JS_EXCEPTION passes through as the engine throw */
|
|
1889
1936
|
}
|
|
1890
1937
|
|
|
1891
|
-
/* A fresh engine function over a boxed
|
|
1938
|
+
/* A fresh engine function over a boxed dyn function (borrows d — the
|
|
1892
1939
|
* opaque box retains it). */
|
|
1893
1940
|
static JSValue isl_dynfn_new(const ScrDyn *d) {
|
|
1894
1941
|
JSValue boxv = JS_NewObjectClass(isl_ctx, isl_dynfn_class_id);
|
|
@@ -1959,11 +2006,13 @@ static void isl_prom_wrap_entry(ScrFiber *self, void *arg) {
|
|
|
1959
2006
|
bool b = false;
|
|
1960
2007
|
ScrStr *s = NULL;
|
|
1961
2008
|
ScrJsval *j = NULL;
|
|
2009
|
+
ScrArr *ja = NULL;
|
|
1962
2010
|
switch (w->payload) {
|
|
1963
2011
|
case SCR_ISLP_F64: f = scr_await_f64(w->p); break;
|
|
1964
2012
|
case SCR_ISLP_BOOL: b = scr_await_bool(w->p); break;
|
|
1965
2013
|
case SCR_ISLP_STR: s = scr_await_str(w->p); break;
|
|
1966
2014
|
case SCR_ISLP_JSVAL: j = (ScrJsval *)scr_await_ref(w->p); break;
|
|
2015
|
+
case SCR_ISLP_JSVAL_ARR: ja = (ScrArr *)scr_await_ref(w->p); break;
|
|
1967
2016
|
default: scr_await_void(w->p); break;
|
|
1968
2017
|
}
|
|
1969
2018
|
bool rejected = scr_exc_pending();
|
|
@@ -1977,11 +2026,26 @@ static void isl_prom_wrap_entry(ScrFiber *self, void *arg) {
|
|
|
1977
2026
|
case SCR_ISLP_BOOL: v = JS_NewBool(isl_ctx, b); break;
|
|
1978
2027
|
case SCR_ISLP_STR: v = s ? JS_NewStringLen(isl_ctx, s->data, s->len) : JS_UNDEFINED; break;
|
|
1979
2028
|
case SCR_ISLP_JSVAL: v = j ? JS_DupValue(isl_ctx, j->v) : JS_UNDEFINED; break;
|
|
2029
|
+
case SCR_ISLP_JSVAL_ARR:
|
|
2030
|
+
/* A native array of engine cells fulfills: a fresh engine array
|
|
2031
|
+
* over the SAME engine values (identity crosses, spine a copy). */
|
|
2032
|
+
if (ja) {
|
|
2033
|
+
v = JS_NewArray(isl_ctx);
|
|
2034
|
+
for (size_t i = 0; i < ja->len; i++) {
|
|
2035
|
+
ScrJsval *cell = (ScrJsval *)scr_arr_get_ref(ja, (double)i); /* +1 */
|
|
2036
|
+
JS_SetPropertyUint32(isl_ctx, v, (uint32_t)i, JS_DupValue(isl_ctx, cell->v));
|
|
2037
|
+
scr_jsval_release(cell);
|
|
2038
|
+
}
|
|
2039
|
+
} else {
|
|
2040
|
+
v = JS_UNDEFINED;
|
|
2041
|
+
}
|
|
2042
|
+
break;
|
|
1980
2043
|
default: v = JS_UNDEFINED; break;
|
|
1981
2044
|
}
|
|
1982
2045
|
}
|
|
1983
2046
|
if (s) scr_str_release(s);
|
|
1984
2047
|
if (j) scr_jsval_release(j);
|
|
2048
|
+
if (ja) scr_arr_release(ja);
|
|
1985
2049
|
JSValue r = JS_Call(isl_ctx, rejected ? w->reject : w->resolve, JS_UNDEFINED, 1, &v);
|
|
1986
2050
|
JS_FreeValue(isl_ctx, v);
|
|
1987
2051
|
if (JS_IsException(r)) {
|
|
@@ -2095,6 +2159,20 @@ static JSValue isl_bridge_settle(JSContext *ctx, JSValueConst this_val, int argc
|
|
|
2095
2159
|
/* fulfill_ref takes the +1 cell; awaiters retain their own out. */
|
|
2096
2160
|
scr_promise_fulfill_ref(b->p, isl_cell_new(JS_DupValue(ctx, v)),
|
|
2097
2161
|
scr_jsval_retain_v, scr_jsval_release_v, NULL);
|
|
2162
|
+
} else if (b->payload == SCR_ISLP_JSVAL_ARR) {
|
|
2163
|
+
/* An `any[]`-declared fulfillment (the inferred loadPlugins return):
|
|
2164
|
+
* the engine array exits Array.isArray-gated, elements BY REFERENCE
|
|
2165
|
+
* (the jsval-element-array exit). A lying fulfillment (non-array)
|
|
2166
|
+
* REJECTS the static promise with the boundary TypeError —
|
|
2167
|
+
* trust-but-verify at the settle, like every dyn→static edge. */
|
|
2168
|
+
ScrJsval *cell = isl_cell_new(JS_DupValue(ctx, v));
|
|
2169
|
+
ScrArr *arr = scr_jsval_exit_jsval_arr(cell);
|
|
2170
|
+
scr_jsval_release(cell);
|
|
2171
|
+
if (!arr) {
|
|
2172
|
+
scr_promise_reject_pending(b->p);
|
|
2173
|
+
} else {
|
|
2174
|
+
scr_promise_fulfill_ref(b->p, arr, scr_arr_retain_v, scr_arr_release_v, NULL);
|
|
2175
|
+
}
|
|
2098
2176
|
} else {
|
|
2099
2177
|
scr_promise_fulfill_void(b->p);
|
|
2100
2178
|
}
|
|
@@ -9567,6 +9645,37 @@ ScrBytes *scr_jsval_exit_bytes(ScrJsval *v) {
|
|
|
9567
9645
|
return b;
|
|
9568
9646
|
}
|
|
9569
9647
|
|
|
9648
|
+
/* Validated exit of an engine value into an `any[]`-declared slot (the
|
|
9649
|
+
* jsval-element-array spelling — withPlugins' `loadPlugins(plugins)`
|
|
9650
|
+
* boundary): the engine's Array.isArray gates (a non-array refuses with
|
|
9651
|
+
* the catchable boundary TypeError), then elements copy BY REFERENCE
|
|
9652
|
+
* into a native array of engine cells — identity preserved, length a
|
|
9653
|
+
* snapshot (the exit's aliasing stance: element IDENTITY crosses, the
|
|
9654
|
+
* spine is a copy). +1, or NULL with the exception pending. */
|
|
9655
|
+
ScrArr *scr_jsval_exit_jsval_arr(ScrJsval *v) {
|
|
9656
|
+
isl_entry();
|
|
9657
|
+
if (JS_IsArray(v->v) <= 0) {
|
|
9658
|
+
isl_exit_fail("an array", v);
|
|
9659
|
+
return NULL;
|
|
9660
|
+
}
|
|
9661
|
+
JSValue lv = JS_GetPropertyStr(isl_ctx, v->v, "length");
|
|
9662
|
+
int64_t len = 0;
|
|
9663
|
+
JS_ToInt64(isl_ctx, &len, lv);
|
|
9664
|
+
JS_FreeValue(isl_ctx, lv);
|
|
9665
|
+
ScrArr *out = scr_arr_new_ref(&scr_jsval_retain_v, &scr_jsval_release_v, NULL,
|
|
9666
|
+
len > 0 ? (size_t)len : 0);
|
|
9667
|
+
for (int64_t i = 0; i < len; i++) {
|
|
9668
|
+
JSValue e = JS_GetPropertyUint32(isl_ctx, v->v, (uint32_t)i); /* getters run */
|
|
9669
|
+
if (JS_IsException(e)) {
|
|
9670
|
+
isl_bridge_exception();
|
|
9671
|
+
scr_arr_release(out);
|
|
9672
|
+
return NULL;
|
|
9673
|
+
}
|
|
9674
|
+
scr_arr_push_ref(out, isl_cell_new(e)); /* ownership moves in */
|
|
9675
|
+
}
|
|
9676
|
+
return out;
|
|
9677
|
+
}
|
|
9678
|
+
|
|
9570
9679
|
/* Composite exit: engine JSON.stringify, feeding the existing
|
|
9571
9680
|
* json.parse + dynCheck walker pipeline on the static side. A value
|
|
9572
9681
|
* JSON cannot represent (function, undefined, symbol at the top) comes
|