@scriptc/runtime 0.0.11 → 0.0.13
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 +53 -8
- package/src/scr_async_dyn.c +163 -47
- 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 +42 -18
- package/src/scr_error.c +3 -3
- package/src/scr_events_emitter.c +1 -1
- package/src/scr_http.c +1080 -34
- package/src/scr_http2.c +53 -3
- package/src/scr_inspect.c +3 -3
- package/src/scr_island.c +134 -25
- package/src/scr_json.c +124 -68
- package/src/scr_net.c +254 -35
- package/src/scr_qs.c +1 -1
- package/src/scr_runtime.h +278 -124
- package/src/scr_stream.c +4 -4
- package/src/scr_tls.c +42 -5
- package/src/scr_tls_ca.c +288 -0
- package/src/scr_web.c +1 -1
package/src/scr_json.c
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* JSON + dynamic values (see scr_runtime.h for the API contract).
|
|
2
2
|
*
|
|
3
|
-
* - The ScrDyn
|
|
3
|
+
* - The ScrDyn dyn is the runtime shape of `unknown`: refcounted, owning
|
|
4
4
|
* its children; releasing the root frees the tree recursively.
|
|
5
5
|
* - scr_json_parse is a full RFC 8259 recursive-descent parser: null/
|
|
6
6
|
* true/false, numbers (strtod after a strict grammar check — doubles
|
|
@@ -270,7 +270,7 @@ void scr_jb_edge_idx(ScrJsonBuf *b, size_t i) {
|
|
|
270
270
|
|
|
271
271
|
/* ── circular guard for the typed→dyn converters (sc_td_*) ────────────
|
|
272
272
|
* A recursive-typed value crossing into a checked-dynamic slot DEEP-
|
|
273
|
-
* COPIES into the
|
|
273
|
+
* COPIES into the checked-dynamic tree; a cyclic value has no finite copy. Node never
|
|
274
274
|
* copies (an unknown-typed binding shares the reference), so there is no
|
|
275
275
|
* Node-exact error to throw — the conversion TRAPS loudly instead
|
|
276
276
|
* (SEMANTICS.md documents the divergence). The emitted converters over
|
|
@@ -300,7 +300,7 @@ void scr_dyn_from_leave(void) {
|
|
|
300
300
|
if (g_td_nseen > 0) g_td_nseen--;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
/* ──
|
|
303
|
+
/* ── dyn lifecycle ─────────────────────────────────────────────────────
|
|
304
304
|
* Parse/release churn (a JSON round-trip loop allocates and frees every
|
|
305
305
|
* node each iteration) runs on freelists instead of calloc/free: one list
|
|
306
306
|
* per shape so arr/obj nodes keep their items/entries buffer across
|
|
@@ -431,7 +431,7 @@ void scr_dyn_arr_push(ScrDyn *arr, ScrDyn *item) {
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
/* Spread completion for a runtime-arity argument list (`f(...xs)` in the
|
|
434
|
-
* checked-dynamic tier): JS's spread over the
|
|
434
|
+
* checked-dynamic tier): JS's spread over the checked-dynamic tree's iterable kinds —
|
|
435
435
|
* arrays element-by-element (retained), strings by code POINT (the string
|
|
436
436
|
* iterator; astral chars arrive unsplit), bytes by byte; every other kind
|
|
437
437
|
* throws V8's exact SPREAD-CALL TypeError (catchable, pending — callers
|
|
@@ -473,16 +473,24 @@ void scr_dyn_arr_push_spread(ScrDyn *arr, const ScrDyn *src, const char *what) {
|
|
|
473
473
|
return;
|
|
474
474
|
}
|
|
475
475
|
if (src->kind == SCR_DYN_JSVAL) {
|
|
476
|
-
/*
|
|
477
|
-
*
|
|
478
|
-
|
|
476
|
+
/* A wrapped engine value spreads through the ENGINE's own iterator
|
|
477
|
+
* protocol (the routed iter_drain — Symbol.iterator implementations,
|
|
478
|
+
* generators, Maps step exactly as Node runs them); a non-iterable
|
|
479
|
+
* throws V8's spread-call text from the guard, an iterating throw
|
|
480
|
+
* bridges with the engine's message. */
|
|
481
|
+
ScrDyn *pack = scr_dyn_jsval_ops()->iter_drain(src->v.jsval.cell, true, NULL);
|
|
482
|
+
if (!pack) return; /* pending */
|
|
483
|
+
for (size_t i = 0; i < pack->v.arr.len; i++) {
|
|
484
|
+
scr_dyn_arr_push(arr, scr_dyn_retain(pack->v.arr.items[i]));
|
|
485
|
+
}
|
|
486
|
+
scr_dyn_release(pack);
|
|
479
487
|
return;
|
|
480
488
|
}
|
|
481
489
|
static const char msg[] = "Spread syntax requires ...iterable[Symbol.iterator] to be a function";
|
|
482
490
|
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
483
491
|
}
|
|
484
492
|
|
|
485
|
-
/* Destructuring pack over a
|
|
493
|
+
/* Destructuring pack over a dyn source (`const [a, b] = d`, a destructured
|
|
486
494
|
* dyn callback param): the spread walk's iterable kinds collect into a
|
|
487
495
|
* FRESH array — arrays element-by-element (retained), strings by code
|
|
488
496
|
* point, bytes by byte — and every other kind throws V8's DESTRUCTURING
|
|
@@ -499,12 +507,14 @@ ScrDyn *scr_dyn_iter_pack(const ScrDyn *src, const ScrStr *msg) {
|
|
|
499
507
|
scr_dyn_arr_push_spread(out, src, ""); /* iterable kinds never consult `what` */
|
|
500
508
|
return out;
|
|
501
509
|
}
|
|
502
|
-
/*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
510
|
+
/* A wrapped engine value packs through the ENGINE's own iterator
|
|
511
|
+
* protocol (the routed iter_drain): elements wrap back scalar-
|
|
512
|
+
* normalized; a non-iterable throws the destructuring kind wording
|
|
513
|
+
* from the engine-side guard, an iterating throw bridges with the
|
|
514
|
+
* engine's message. The compile-time spelling is not threaded through
|
|
515
|
+
* (the engine's wording names the value's own kind). */
|
|
505
516
|
if (src->kind == SCR_DYN_JSVAL) {
|
|
506
|
-
|
|
507
|
-
return NULL;
|
|
517
|
+
return scr_dyn_jsval_ops()->iter_drain(src->v.jsval.cell, false, msg);
|
|
508
518
|
}
|
|
509
519
|
if (msg != NULL && msg->len > 0) {
|
|
510
520
|
scr_throw_error(SCR_ERR_TYPE, scr_str_new(msg->data, msg->len));
|
|
@@ -531,6 +541,19 @@ ScrDyn *scr_dyn_iter_pack(const ScrDyn *src, const ScrStr *msg) {
|
|
|
531
541
|
return NULL;
|
|
532
542
|
}
|
|
533
543
|
|
|
544
|
+
/* The for-of-over-dyn pack accessors: the emitted index loop drives them
|
|
545
|
+
* over a scr_dyn_iter_pack result (ARR by construction — the defensive
|
|
546
|
+
* arms cover nothing reachable from that lowering). Never throw. */
|
|
547
|
+
double scr_dyn_arr_len(const ScrDyn *d) {
|
|
548
|
+
return d->kind == SCR_DYN_ARR ? (double)d->v.arr.len : 0;
|
|
549
|
+
}
|
|
550
|
+
ScrDyn *scr_dyn_arr_at(const ScrDyn *d, double i) {
|
|
551
|
+
if (d->kind != SCR_DYN_ARR || i < 0 || i >= (double)d->v.arr.len) {
|
|
552
|
+
return scr_dyn_retain(scr_dyn_undefined());
|
|
553
|
+
}
|
|
554
|
+
return scr_dyn_retain(d->v.arr.items[(size_t)i]);
|
|
555
|
+
}
|
|
556
|
+
|
|
534
557
|
/* Takes ownership of key (malloc'd) and value. Duplicate keys: the LATER
|
|
535
558
|
* value wins (like JS JSON.parse) — the old value is released and the new
|
|
536
559
|
* key buffer freed (the surviving entry keeps its original, equal key). */
|
|
@@ -557,7 +580,7 @@ static void scr_dyn_obj_put(ScrDyn *obj, char *key, size_t key_len, ScrDyn *valu
|
|
|
557
580
|
e->value = value;
|
|
558
581
|
}
|
|
559
582
|
|
|
560
|
-
/* ──
|
|
583
|
+
/* ── dyn construction (compiler-emitted converters & overflow reads) ───── */
|
|
561
584
|
|
|
562
585
|
/* THE undefined value: one immortal node (rc == SIZE_MAX skips every
|
|
563
586
|
* retain/release and the freelists never see it). */
|
|
@@ -622,7 +645,7 @@ ScrBytes *scr_dyn_bytes_copy_out(const ScrDyn *d) {
|
|
|
622
645
|
static bool scr_dyn_chunk_utf8;
|
|
623
646
|
void scr_dyn_chunk_enc(bool utf8) { scr_dyn_chunk_utf8 = utf8; }
|
|
624
647
|
|
|
625
|
-
/* One 'data' payload as the
|
|
648
|
+
/* One 'data' payload as the dyn value the current window dictates:
|
|
626
649
|
* a Buffer-flavored bytes box, or a string inside a setEncoding window. */
|
|
627
650
|
ScrDyn *scr_dyn_new_chunk(const ScrBytes *b) {
|
|
628
651
|
if (scr_dyn_chunk_utf8) {
|
|
@@ -634,7 +657,7 @@ ScrDyn *scr_dyn_new_chunk(const ScrBytes *b) {
|
|
|
634
657
|
return scr_dyn_new_buffer_copy(b);
|
|
635
658
|
}
|
|
636
659
|
|
|
637
|
-
/* A boxed static function value (the compiler's static→
|
|
660
|
+
/* A boxed static function value (the compiler's static→dyn converters).
|
|
638
661
|
* Ownership of the closure MOVES in; sig/name are static literals. */
|
|
639
662
|
ScrDyn *scr_dyn_new_func(ScrClosure *clo, ScrDynThunk thunk, uint32_t arity, const char *sig, const char *name) {
|
|
640
663
|
ScrDyn *d = scr_dyn_alloc(SCR_DYN_FUNC);
|
|
@@ -654,7 +677,7 @@ ScrDyn *scr_dyn_new_func(ScrClosure *clo, ScrDynThunk thunk, uint32_t arity, con
|
|
|
654
677
|
ScrDyn *scr_dyn_call(const ScrDyn *d, ScrDyn *const *args, size_t argc, const char *what) {
|
|
655
678
|
if (d->kind == SCR_DYN_JSVAL) {
|
|
656
679
|
/* An ENGINE callee: the call routes through scr_jsval_call with the
|
|
657
|
-
* uniform argument conversion (wrapped cells by reference,
|
|
680
|
+
* uniform argument conversion (wrapped cells by reference, dyn data
|
|
658
681
|
* deep-copied, FUNC boxes through the host shim); a non-callable
|
|
659
682
|
* engine value throws the ENGINE's own TypeError, bridged catchably.
|
|
660
683
|
* `what` is unused — the engine's message names the failure. */
|
|
@@ -672,14 +695,14 @@ ScrDyn *scr_dyn_call(const ScrDyn *d, ScrDyn *const *args, size_t argc, const ch
|
|
|
672
695
|
return d->v.fn.thunk(d->v.fn.clo, args, argc);
|
|
673
696
|
}
|
|
674
697
|
|
|
675
|
-
/* scr_dyn_call over a
|
|
698
|
+
/* scr_dyn_call over a dyn ARRAY's elements — the spread-application form
|
|
676
699
|
* (`f(...args)` after the emitted argument array is built). Borrows both;
|
|
677
700
|
* result owned (+1), or NULL with the exception pending. */
|
|
678
701
|
ScrDyn *scr_dyn_apply(const ScrDyn *d, const ScrDyn *args, const char *what) {
|
|
679
702
|
return scr_dyn_call(d, args->v.arr.items, args->v.arr.len, what);
|
|
680
703
|
}
|
|
681
704
|
|
|
682
|
-
/* ── native handles in the
|
|
705
|
+
/* ── native handles in the checked-dynamic tree (SCR_DYN_HANDLE) ───────────────────────
|
|
683
706
|
* Per-tag ops stamped by the owning units at main() (scr_http_dyn_install
|
|
684
707
|
* / scr_net_dyn_install — the scr_net_install hook story), so this
|
|
685
708
|
* always-linked core never references gated units. A missing tag at use
|
|
@@ -710,10 +733,10 @@ const ScrDynHandleOps *scr_dyn_handle_ops_of(const ScrDyn *d) {
|
|
|
710
733
|
return scr_dyn_handle_ops(d->v.handle.tag);
|
|
711
734
|
}
|
|
712
735
|
|
|
713
|
-
/* errors.js's determineSpecificType over a
|
|
736
|
+
/* errors.js's determineSpecificType over a dyn value — the "Received
|
|
714
737
|
* ..." tail of Node's ERR_INVALID_ARG_TYPE messages. Renders into buf
|
|
715
738
|
* when the shape needs a payload; returns the text either way. Lives
|
|
716
|
-
* beside the
|
|
739
|
+
* beside the dyn core (not the gated handle unit) because the always-
|
|
717
740
|
* linked argument validators (bytes, fs) render through it too. */
|
|
718
741
|
const char *scr_dyn_specific_type(const ScrDyn *cb, char *detail, size_t cap) {
|
|
719
742
|
const char *d = detail;
|
|
@@ -886,7 +909,7 @@ ScrDyn *scr_dyn_alloc_promise(void (*release_fn)(ScrPromise *p)) {
|
|
|
886
909
|
return scr_dyn_alloc(SCR_DYN_PROMISE);
|
|
887
910
|
}
|
|
888
911
|
|
|
889
|
-
/* ── island values in the
|
|
912
|
+
/* ── island values in the checked-dynamic tree (SCR_DYN_JSVAL) ─────────────────────────
|
|
890
913
|
* The gated constructor (scr_dyn_from_jsval, scr_island.c) builds through
|
|
891
914
|
* this allocator view and installs the engine-routing ops — the
|
|
892
915
|
* scr_dyn_alloc_promise story: a dynamic-free link never references
|
|
@@ -973,7 +996,7 @@ ScrDyn *scr_dyn_handle_key_get(const ScrDyn *d, const ScrStr *k) {
|
|
|
973
996
|
scr_dyn_release(r);
|
|
974
997
|
return NULL;
|
|
975
998
|
}
|
|
976
|
-
/* Unmodeled names answer undefined — the
|
|
999
|
+
/* Unmodeled names answer undefined — the checked-dynamic tree's own-property stance
|
|
977
1000
|
* (real-but-unmodeled members fence loudly inside ops->get instead;
|
|
978
1001
|
* SEMANTICS.md documents the remainder). */
|
|
979
1002
|
return r ? r : scr_dyn_retain(scr_dyn_undefined());
|
|
@@ -1066,7 +1089,7 @@ void scr_dyn_obj_set(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *value
|
|
|
1066
1089
|
scr_dyn_obj_put(obj, copy, key_len, value);
|
|
1067
1090
|
}
|
|
1068
1091
|
|
|
1069
|
-
/* ToBoolean over a
|
|
1092
|
+
/* ToBoolean over a dyn value (`v || dflt`, `if (v)` on a dyn operand):
|
|
1070
1093
|
* bool by value; number falsy exactly for 0, -0, and NaN; string falsy
|
|
1071
1094
|
* exactly when empty; obj/arr/bytes/func always true; undefined and null
|
|
1072
1095
|
* always false — JS-exact for every kind. Borrowed; never throws. */
|
|
@@ -1089,13 +1112,13 @@ bool scr_dyn_truthy(const ScrDyn *d) {
|
|
|
1089
1112
|
}
|
|
1090
1113
|
}
|
|
1091
1114
|
|
|
1092
|
-
/* Bare `typeof v` on a dyn value: the
|
|
1115
|
+
/* Bare `typeof v` on a dyn value: the dyn kind's JS answer (+1 string).
|
|
1093
1116
|
* null answers "object" — JS's oldest wart, preserved. */
|
|
1094
1117
|
ScrStr *scr_dyn_typeof(const ScrDyn *d) {
|
|
1095
1118
|
const char *s;
|
|
1096
1119
|
/* An island value answers the ENGINE's typeof — "object" for the
|
|
1097
1120
|
* wrapped objects/arrays, "function" for engine functions (row 1 of
|
|
1098
|
-
* the jsval→
|
|
1121
|
+
* the jsval→dyn op table; scalars normalized away at wrap time). */
|
|
1099
1122
|
if (d->kind == SCR_DYN_JSVAL) return scr_dyn_jsval_ops()->type_of(d->v.jsval.cell);
|
|
1100
1123
|
switch (d->kind) {
|
|
1101
1124
|
case SCR_DYN_UNDEF: s = "undefined"; break;
|
|
@@ -1114,9 +1137,9 @@ ScrStr *scr_dyn_typeof(const ScrDyn *d) {
|
|
|
1114
1137
|
return scr_str_new(s, strlen(s));
|
|
1115
1138
|
}
|
|
1116
1139
|
|
|
1117
|
-
/* ── JSON.stringify over a
|
|
1140
|
+
/* ── JSON.stringify over a dyn value (util.format's %j) ───────────────
|
|
1118
1141
|
* The RUNTIME walk the type-directed serializers deliberately avoid for
|
|
1119
|
-
* static values — a dyn value has no static type, so the
|
|
1142
|
+
* static values — a dyn value has no static type, so the checked-dynamic tree's own kinds
|
|
1120
1143
|
* drive it, JS-exactly: insertion-ordered objects with undefined/function
|
|
1121
1144
|
* members OMITTED, arrays rendering those as null, Buffer's toJSON shape
|
|
1122
1145
|
* ({"type":"Buffer","data":[...]}), shortest-roundtrip numbers, escaped
|
|
@@ -1191,7 +1214,7 @@ static bool scr_dyn_json_write(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1191
1214
|
case SCR_DYN_JSVAL: {
|
|
1192
1215
|
/* The ENGINE's own JSON.stringify text splices in (toJSON protocols,
|
|
1193
1216
|
* cycle TypeErrors — the engine's, bridged catchably). An engine
|
|
1194
|
-
* FUNCTION is absent under stringify, like the
|
|
1217
|
+
* FUNCTION is absent under stringify, like the checked-dynamic tree's FUNC kind. */
|
|
1195
1218
|
if (scr_dyn_isl_typeof_is(d, "function")) return false;
|
|
1196
1219
|
ScrStr *j = scr_dyn_jsval_ops()->to_json(d->v.jsval.cell);
|
|
1197
1220
|
if (!j) return true; /* pending exception; caller checks */
|
|
@@ -1226,11 +1249,11 @@ ScrStr *scr_dyn_format_j(const ScrDyn *d) {
|
|
|
1226
1249
|
return scr_jb_finish(&b);
|
|
1227
1250
|
}
|
|
1228
1251
|
|
|
1229
|
-
/* An %Error instance as a
|
|
1252
|
+
/* An %Error instance as a dyn object ({name, message[, code]}) — the
|
|
1230
1253
|
* checked-dynamic boundary's error shape (the exception-snapshot
|
|
1231
1254
|
* convention emit-walkers uses). Borrows e; +1 result.
|
|
1232
1255
|
*
|
|
1233
|
-
* IDENTITY-CACHED: one error instance boxes to ONE
|
|
1256
|
+
* IDENTITY-CACHED: one error instance boxes to ONE dyn node, however many
|
|
1234
1257
|
* times it crosses (Node passes the error OBJECT through, so `found.error
|
|
1235
1258
|
* === thrown` and re-crossings compare reference-equal — the tracing
|
|
1236
1259
|
* suite's shape). The cache retains both sides for the process (like the
|
|
@@ -1262,7 +1285,7 @@ ScrDyn *scr_dyn_from_error(const ScrError *e) {
|
|
|
1262
1285
|
if (scr_errdyn_cache[i].err == e) return scr_dyn_retain(scr_errdyn_cache[i].dyn);
|
|
1263
1286
|
}
|
|
1264
1287
|
ScrDyn *d = scr_dyn_new_obj();
|
|
1265
|
-
scr_dyn_obj_set(d, "%error", 6, scr_dyn_new_bool(true)); /* the
|
|
1288
|
+
scr_dyn_obj_set(d, "%error", 6, scr_dyn_new_bool(true)); /* the checked-dynamic tree's error marker */
|
|
1266
1289
|
scr_dyn_obj_set(d, "name", 4, scr_dyn_new_str(e->name));
|
|
1267
1290
|
scr_dyn_obj_set(d, "message", 7, scr_dyn_new_str(e->message));
|
|
1268
1291
|
if (e->code) scr_dyn_obj_set(d, "code", 4, scr_dyn_new_str(e->code));
|
|
@@ -1293,13 +1316,13 @@ ScrDyn *scr_dyn_from_error(const ScrError *e) {
|
|
|
1293
1316
|
|
|
1294
1317
|
/* The %Error EXTRACTION (dynCheck of `u as Error` / an instanceof-Error
|
|
1295
1318
|
* narrow, and the dyn-boxed thunk's Error-typed parameters): the REVERSE
|
|
1296
|
-
* of scr_dyn_from_error, riding the same identity cache — a
|
|
1319
|
+
* of scr_dyn_from_error, riding the same identity cache — a dyn error
|
|
1297
1320
|
* that came from a runtime ScrError answers THAT instance (+1), so an
|
|
1298
1321
|
* error crossing out and back compares reference-equal (the tracing
|
|
1299
1322
|
* suite's shape); an alien %error object rebuilds a runtime error from
|
|
1300
1323
|
* its name/message/code (the vtable kind resolves from the name so a
|
|
1301
1324
|
* later `instanceof TypeError` still answers) and ENTERS the cache, so
|
|
1302
|
-
* its next boxing answers the same
|
|
1325
|
+
* its next boxing answers the same dyn node. The dyn node is borrowed. */
|
|
1303
1326
|
ScrError *scr_error_from_dyn(const ScrDyn *d) {
|
|
1304
1327
|
ScrError *hit = scr_errdyn_err_of(d);
|
|
1305
1328
|
if (hit) return hit;
|
|
@@ -1355,7 +1378,7 @@ void scr_errdyn_put(ScrError *e, ScrDyn *d) {
|
|
|
1355
1378
|
* dyn method surface — a stream's 'data'/for-await chunk is the common
|
|
1356
1379
|
* receiver): bytes decode per the encoding (Node's Buffer.toString,
|
|
1357
1380
|
* utf8 default), strings answer themselves, numbers/booleans format
|
|
1358
|
-
* JS-exactly, arrays join their
|
|
1381
|
+
* JS-exactly, arrays join their dyn elements with ',' (recursively via
|
|
1359
1382
|
* JS's Array.prototype.toString), plain objects answer
|
|
1360
1383
|
* "[object Object]", and undefined/null throw Node's TypeError. Borrows
|
|
1361
1384
|
* both; +1 result. */
|
|
@@ -1417,7 +1440,7 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
|
|
|
1417
1440
|
return scr_str_new(f, sizeof f - 1);
|
|
1418
1441
|
}
|
|
1419
1442
|
case SCR_DYN_JSVAL: {
|
|
1420
|
-
/* The engine's own ToString (row 2 of the jsval→
|
|
1443
|
+
/* The engine's own ToString (row 2 of the jsval→dyn op table): the
|
|
1421
1444
|
* real prototype chain runs — user toString included, its throw
|
|
1422
1445
|
* bridging. A bridged failure follows this function's existing
|
|
1423
1446
|
* throw shape (pending exception + the empty-string dummy). */
|
|
@@ -1453,7 +1476,7 @@ ScrStr *scr_dyn_to_string_method(const ScrDyn *d, const ScrStr *enc, const ScrSt
|
|
|
1453
1476
|
return scr_dyn_to_string(d, enc);
|
|
1454
1477
|
}
|
|
1455
1478
|
|
|
1456
|
-
/* JS String() over the
|
|
1479
|
+
/* JS String() over the dyn kind — the WebIDL ToString the web globals
|
|
1457
1480
|
* (atob/btoa, DOMException's name resolution) run on their arguments:
|
|
1458
1481
|
* the unit kinds RENDER ("null"/"undefined") where the .toString() twin
|
|
1459
1482
|
* above throws Node's property-read TypeError; every other kind matches
|
|
@@ -1464,7 +1487,7 @@ ScrStr *scr_dyn_string_coerce(const ScrDyn *d) {
|
|
|
1464
1487
|
return scr_dyn_to_string(d, NULL);
|
|
1465
1488
|
}
|
|
1466
1489
|
|
|
1467
|
-
/* JS ToString over a
|
|
1490
|
+
/* JS ToString over a dyn value WITH the object protocol (the WHATWG
|
|
1468
1491
|
* USVString conversions — URLSearchParams names/values): an OBJ whose
|
|
1469
1492
|
* own 'toString' member is callable is invoked with zero arguments (its
|
|
1470
1493
|
* throw propagates, catchably); a non-primitive answer falls through to
|
|
@@ -1504,16 +1527,37 @@ ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) {
|
|
|
1504
1527
|
* ignore silently — the loud choice, SEMANTICS.md). Receiver, key, and
|
|
1505
1528
|
* value are all BORROWED (the member retains the value in). */
|
|
1506
1529
|
static const char *scr_dyn_kind_name(const ScrDyn *d);
|
|
1530
|
+
/* `key in v` with a RUNTIME key (the compile-time dynHasKey fold, per
|
|
1531
|
+
* value): OBJ answers own-member presence, ARR answers 'length' or a
|
|
1532
|
+
* valid dense index, every other kind false (tsc admits `in` only on
|
|
1533
|
+
* object-typed operands). Borrows both; never throws. */
|
|
1534
|
+
bool scr_dyn_has_key(const ScrDyn *v, const ScrStr *key) {
|
|
1535
|
+
if (v->kind == SCR_DYN_OBJ) return scr_dyn_obj_get(v, key->data, key->len) != NULL;
|
|
1536
|
+
if (v->kind == SCR_DYN_ARR) {
|
|
1537
|
+
if (key->len == 6 && memcmp(key->data, "length", 6) == 0) return true;
|
|
1538
|
+
if (key->len == 0 || key->len > 15) return false;
|
|
1539
|
+
size_t idx = 0;
|
|
1540
|
+
for (size_t i = 0; i < key->len; i++) {
|
|
1541
|
+
char c = key->data[i];
|
|
1542
|
+
if (c < '0' || c > '9') return false;
|
|
1543
|
+
if (i > 0 && idx == 0) return false; /* a leading zero is no canonical index */
|
|
1544
|
+
idx = idx * 10 + (size_t)(c - '0');
|
|
1545
|
+
}
|
|
1546
|
+
return idx < v->v.arr.len;
|
|
1547
|
+
}
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1507
1551
|
void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
1508
1552
|
if (recv->kind == SCR_DYN_OBJ) {
|
|
1509
1553
|
scr_dyn_obj_set(recv, key->data, key->len, scr_dyn_retain(value));
|
|
1510
1554
|
return;
|
|
1511
1555
|
}
|
|
1512
1556
|
if (recv->kind == SCR_DYN_ARR) {
|
|
1513
|
-
/* An INDEX write on a
|
|
1557
|
+
/* An INDEX write on a dyn array (`args[i] = v` — the variadic-rest
|
|
1514
1558
|
* rebuild): a canonical numeric key sets/extends the element, holes
|
|
1515
1559
|
* padding with undefined exactly like JS length growth. Non-index
|
|
1516
|
-
* keys keep the throw below (
|
|
1560
|
+
* keys keep the throw below (dyn arrays carry no expando table). */
|
|
1517
1561
|
size_t idx = 0;
|
|
1518
1562
|
int is_index = key->len > 0 && !(key->len > 1 && key->data[0] == '0');
|
|
1519
1563
|
for (size_t i = 0; is_index && i < key->len; i++) {
|
|
@@ -1573,7 +1617,7 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
|
1573
1617
|
scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
|
|
1574
1618
|
}
|
|
1575
1619
|
|
|
1576
|
-
/* Node's JSON.stringify over a
|
|
1620
|
+
/* Node's JSON.stringify over a dyn: object members holding undefined DROP,
|
|
1577
1621
|
* array slots holding undefined print null. A bare undefined never arrives
|
|
1578
1622
|
* (the record serializer drops the entry first); print null defensively. */
|
|
1579
1623
|
void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
@@ -1594,7 +1638,7 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1594
1638
|
return;
|
|
1595
1639
|
case SCR_DYN_BYTES: {
|
|
1596
1640
|
/* Node's JSON.stringify over a typed array: the index-keyed object
|
|
1597
|
-
* form — {"0":1,"1":2}. u8 payloads only reach the
|
|
1641
|
+
* form — {"0":1,"1":2}. u8 payloads only reach the checked-dynamic tree today. */
|
|
1598
1642
|
scr_jb_putc(b, '{');
|
|
1599
1643
|
for (size_t i = 0; i < d->v.bytes->len; i++) {
|
|
1600
1644
|
if (i > 0) scr_jb_putc(b, ',');
|
|
@@ -1633,7 +1677,7 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1633
1677
|
case SCR_DYN_JSVAL: {
|
|
1634
1678
|
/* The ENGINE's own JSON.stringify text splices in (toJSON protocols,
|
|
1635
1679
|
* cycle TypeErrors — all the engine's, bridged catchably). An engine
|
|
1636
|
-
* FUNCTION serializes like the
|
|
1680
|
+
* FUNCTION serializes like the checked-dynamic tree's FUNC kind (dropped from objects
|
|
1637
1681
|
* by the member loop below; null defensively elsewhere). */
|
|
1638
1682
|
if (scr_dyn_isl_typeof_is(d, "function")) {
|
|
1639
1683
|
scr_jb_puts(b, "null");
|
|
@@ -2232,9 +2276,9 @@ ScrDyn *scr_json_parse(ScrStr *text) {
|
|
|
2232
2276
|
void *scr_dyn_retain_v(void *d) { return scr_dyn_retain((ScrDyn *)d); }
|
|
2233
2277
|
void scr_dyn_release_v(void *d) { scr_dyn_release((ScrDyn *)d); }
|
|
2234
2278
|
|
|
2235
|
-
/* JS === over two
|
|
2279
|
+
/* JS === over two dyn values: scalars by value (NaN false, ±0 equal via
|
|
2236
2280
|
* C ==; strings bytewise), units by kind, everything reference-shaped by
|
|
2237
|
-
* node IDENTITY (the
|
|
2281
|
+
* node IDENTITY (the checked-dynamic tree's object identity). Never throws. */
|
|
2238
2282
|
bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
2239
2283
|
if (a->kind != b->kind) return false;
|
|
2240
2284
|
switch (a->kind) {
|
|
@@ -2261,7 +2305,7 @@ bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
|
2261
2305
|
/* Identity is the ENGINE VALUE, not the box or even the cell: two
|
|
2262
2306
|
* wraps of one engine value compare ===-equal (the engine's own
|
|
2263
2307
|
* strict equality answers). Mixed kinds already answered false above
|
|
2264
|
-
* — a
|
|
2308
|
+
* — a dyn copy is a different object, which is Node's answer too. */
|
|
2265
2309
|
return a == b || scr_dyn_jsval_ops()->strict_eq(a->v.jsval.cell, b->v.jsval.cell);
|
|
2266
2310
|
default: return a == b;
|
|
2267
2311
|
}
|
|
@@ -2290,10 +2334,10 @@ ScrDyn *scr_dyn_fn_get(const ScrDyn *d, const char *key, size_t key_len) {
|
|
|
2290
2334
|
return NULL;
|
|
2291
2335
|
}
|
|
2292
2336
|
|
|
2293
|
-
/* ── structuredClone over the
|
|
2337
|
+
/* ── structuredClone over the checked-dynamic tree ─────────────────────────────────────
|
|
2294
2338
|
* The JSON-safe subset plus bytes, deep. Functions and handle kinds
|
|
2295
2339
|
* throw the spec's catchable DataCloneError; cycles throw the scriptc
|
|
2296
|
-
* fence (the
|
|
2340
|
+
* fence (the checked-dynamic tree cannot represent them — Node clones cycles; documented
|
|
2297
2341
|
* divergence). Option validation throws Node's exact TypeErrors and is
|
|
2298
2342
|
* shared with scr_domex_clone. */
|
|
2299
2343
|
|
|
@@ -2353,7 +2397,7 @@ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
|
|
|
2353
2397
|
for (const ScrScParent *p = up; p != NULL; p = p->up) {
|
|
2354
2398
|
if (p->node == v) {
|
|
2355
2399
|
static const char msg[] =
|
|
2356
|
-
"structuredClone of cyclic values (the
|
|
2400
|
+
"structuredClone of cyclic values (the checked-dynamic tree cannot represent cycles) is not supported yet";
|
|
2357
2401
|
scr_throw_error_msg(SCR_ERR_ERROR, msg, sizeof msg - 1);
|
|
2358
2402
|
return NULL;
|
|
2359
2403
|
}
|
|
@@ -2392,7 +2436,7 @@ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
|
|
|
2392
2436
|
case SCR_DYN_FUNC:
|
|
2393
2437
|
case SCR_DYN_HANDLE:
|
|
2394
2438
|
default: {
|
|
2395
|
-
/* Node renders the value's source text; the
|
|
2439
|
+
/* Node renders the value's source text; the checked-dynamic tree has none — the
|
|
2396
2440
|
* String() rendering stands in ("function () { [native code] } could
|
|
2397
2441
|
* not be cloned."). */
|
|
2398
2442
|
ScrStr *what = scr_dyn_string_coerce(v);
|
|
@@ -2434,7 +2478,7 @@ ScrDyn *scr_structured_clone_missing(void) {
|
|
|
2434
2478
|
|
|
2435
2479
|
bool scr_dyn_err_instanceof(const ScrDyn *d, double kind) {
|
|
2436
2480
|
/* A JSVAL node never came from a runtime ScrError, so the cache miss
|
|
2437
|
-
* below answers false — the documented contract ("a
|
|
2481
|
+
* below answers false — the documented contract ("a dyn value that
|
|
2438
2482
|
* never came from an error answers false"). An ENGINE TypeError held
|
|
2439
2483
|
* in 'unknown' thus answers false where Node answers true: covered by
|
|
2440
2484
|
* lane dom-jsval-long-tail (needs the engine's class instanceof). */
|
|
@@ -2448,7 +2492,7 @@ bool scr_dyn_err_instanceof(const ScrDyn *d, double kind) {
|
|
|
2448
2492
|
return false;
|
|
2449
2493
|
}
|
|
2450
2494
|
|
|
2451
|
-
/* ── Object.keys/values/entries over the
|
|
2495
|
+
/* ── Object.keys/values/entries over the checked-dynamic tree ──────────────────────────
|
|
2452
2496
|
* JS own-key order: array-index keys ascending first, then the rest in
|
|
2453
2497
|
* insertion order. entries answers [key, value] pairs; values RETAIN
|
|
2454
2498
|
* the member nodes (reference semantics, like JS). Strings/arrays/bytes
|
|
@@ -2471,7 +2515,7 @@ static bool scr_dyn_key_is_index(const char *key, size_t len, double *out) {
|
|
|
2471
2515
|
|
|
2472
2516
|
typedef enum { SCR_OBJWALK_KEYS, SCR_OBJWALK_VALUES, SCR_OBJWALK_ENTRIES } ScrObjWalk;
|
|
2473
2517
|
|
|
2474
|
-
/* A fresh key string boxed into the
|
|
2518
|
+
/* A fresh key string boxed into the checked-dynamic tree: scr_dyn_new_str RETAINS its
|
|
2475
2519
|
* argument, so the local +1 drops right after. */
|
|
2476
2520
|
static ScrDyn *scr_dyn_objwalk_key(const char *key, size_t key_len) {
|
|
2477
2521
|
ScrStr *k = scr_str_new(key, key_len);
|
|
@@ -2507,15 +2551,15 @@ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
|
2507
2551
|
}
|
|
2508
2552
|
if (v->kind == SCR_DYN_JSVAL) {
|
|
2509
2553
|
/* The ENGINE walks its own object (own-key order, getters running,
|
|
2510
|
-
* Object.entries' pairs) and the results come back as a NATIVE
|
|
2511
|
-
* array — keys are
|
|
2554
|
+
* Object.entries' pairs) and the results come back as a NATIVE dyn
|
|
2555
|
+
* array — keys are dyn strings, values wrap per element. */
|
|
2512
2556
|
return scr_dyn_jsval_ops()->obj_walk(v->v.jsval.cell, (int)mode);
|
|
2513
2557
|
}
|
|
2514
2558
|
ScrDyn *out = scr_dyn_new_arr();
|
|
2515
2559
|
if (v->kind == SCR_DYN_OBJ) {
|
|
2516
2560
|
size_t n = v->v.obj.len;
|
|
2517
2561
|
/* Two passes: array-index keys ascending, then the rest in insertion
|
|
2518
|
-
* order (JS's own-key order). Index keys are rare in
|
|
2562
|
+
* order (JS's own-key order). Index keys are rare in dyn objects —
|
|
2519
2563
|
* the ascending pass is a simple selection scan. */
|
|
2520
2564
|
bool *is_index = malloc(n ? n * sizeof *is_index : 1);
|
|
2521
2565
|
double *idx = malloc(n ? n * sizeof *idx : 1);
|
|
@@ -2525,7 +2569,7 @@ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
|
2525
2569
|
size_t index_count = 0;
|
|
2526
2570
|
for (size_t i = 0; i < n; i++) {
|
|
2527
2571
|
const ScrDynEntry *e = &v->v.obj.entries[i];
|
|
2528
|
-
/* Reserved '%'-prefixed members (the
|
|
2572
|
+
/* Reserved '%'-prefixed members (the checked-dynamic tree's error marker) never
|
|
2529
2573
|
* appear in user objects — '%' cannot start a JS identifier-ish
|
|
2530
2574
|
* JSON key from this runtime's own producers; skip defensively. */
|
|
2531
2575
|
is_index[i] = scr_dyn_key_is_index(e->key, e->key_len, &idx[i]);
|
|
@@ -2576,7 +2620,7 @@ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
|
2576
2620
|
return out;
|
|
2577
2621
|
}
|
|
2578
2622
|
if (v->kind == SCR_DYN_STR) {
|
|
2579
|
-
/* JS indexes strings by UTF-16 code units; the
|
|
2623
|
+
/* JS indexes strings by UTF-16 code units; the checked-dynamic tree stores UTF-8.
|
|
2580
2624
|
* Code points walk one at a time — an astral code point stays WHOLE
|
|
2581
2625
|
* (one entry where JS lists two lone surrogates; documented
|
|
2582
2626
|
* approximation, the keys stay dense). */
|
|
@@ -2620,7 +2664,7 @@ ScrDyn *scr_dyn_obj_keys(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWAL
|
|
|
2620
2664
|
* UTF-16 code unit, like Node's String exotic object); nullish sources
|
|
2621
2665
|
* copy nothing (Node skips them) and the scalar/function/handle kinds
|
|
2622
2666
|
* have no own enumerable string keys. Non-OBJ targets copy nothing (the
|
|
2623
|
-
* existing two-arg stance — a
|
|
2667
|
+
* existing two-arg stance — a dyn array target has no property table). */
|
|
2624
2668
|
static void scr_dyn_assign_from(ScrDyn *target, const ScrDyn *src) {
|
|
2625
2669
|
if (target->kind != SCR_DYN_OBJ) return;
|
|
2626
2670
|
if (src->kind == SCR_DYN_UNDEF || src->kind == SCR_DYN_NULL) return;
|
|
@@ -2651,7 +2695,7 @@ static void scr_dyn_assign_from(ScrDyn *target, const ScrDyn *src) {
|
|
|
2651
2695
|
scr_dyn_release(pairs);
|
|
2652
2696
|
}
|
|
2653
2697
|
|
|
2654
|
-
/* Object.assign over
|
|
2698
|
+
/* Object.assign over dyn values: copies `src`'s own members onto `target`
|
|
2655
2699
|
* (last write wins) and answers the target retained (+1). Nullish
|
|
2656
2700
|
* receivers throw Node's ToObject TypeError; nullish sources copy
|
|
2657
2701
|
* nothing; index-keyed sources (arrays/strings/bytes) copy their index
|
|
@@ -2666,13 +2710,13 @@ ScrDyn *scr_dyn_assign(ScrDyn *target, const ScrDyn *src) {
|
|
|
2666
2710
|
/* An ENGINE target: the copy runs in the engine (Object.assign's own
|
|
2667
2711
|
* semantics — setters fire, own-enumerable order); the source enters
|
|
2668
2712
|
* per the uniform conversion (a wrapped source spreads by reference,
|
|
2669
|
-
*
|
|
2713
|
+
* dyn data as the usual member deep copy). */
|
|
2670
2714
|
if (!scr_dyn_jsval_ops()->assign(target->v.jsval.cell, src)) return NULL;
|
|
2671
2715
|
return scr_dyn_retain(target);
|
|
2672
2716
|
}
|
|
2673
2717
|
if (src->kind == SCR_DYN_JSVAL) {
|
|
2674
|
-
/* A wrapped SOURCE onto a
|
|
2675
|
-
* [key, value] pairs (getters running) and each lands as a
|
|
2718
|
+
/* A wrapped SOURCE onto a dyn target: the engine lists its own
|
|
2719
|
+
* [key, value] pairs (getters running) and each lands as a dyn
|
|
2676
2720
|
* member — values wrap per element, scalars normalized. */
|
|
2677
2721
|
if (target->kind == SCR_DYN_OBJ) {
|
|
2678
2722
|
ScrDyn *entries = scr_dyn_jsval_ops()->obj_walk(src->v.jsval.cell, 2);
|
|
@@ -2692,7 +2736,7 @@ ScrDyn *scr_dyn_assign(ScrDyn *target, const ScrDyn *src) {
|
|
|
2692
2736
|
}
|
|
2693
2737
|
|
|
2694
2738
|
/* Variadic Object.assign's argument pack (the `Object.assign({},
|
|
2695
|
-
* ...arr.map(f), tail)` shape): the compiler builds one fresh
|
|
2739
|
+
* ...arr.map(f), tail)` shape): the compiler builds one fresh dyn array
|
|
2696
2740
|
* of sources — plain arguments push borrowed (+1 in), spread arguments
|
|
2697
2741
|
* flatten through the spread-call walk (scr_dyn_arr_push_spread's V8
|
|
2698
2742
|
* TypeError texts, `what` spelling the spread expression for the nullish
|
|
@@ -2721,6 +2765,18 @@ void scr_dyn_pack_push_spread_iter(ScrDyn *pack, const ScrDyn *src) {
|
|
|
2721
2765
|
scr_dyn_arr_push_spread(pack, src, ""); /* iterable kinds never throw */
|
|
2722
2766
|
return;
|
|
2723
2767
|
}
|
|
2768
|
+
if (src->kind == SCR_DYN_JSVAL) {
|
|
2769
|
+
/* A wrapped engine value on the ITERATED path: the engine's own
|
|
2770
|
+
* protocol drains (the kind wording on a non-iterable — the
|
|
2771
|
+
* iterated path's value-describing texts, engine-side). */
|
|
2772
|
+
ScrDyn *drained = scr_dyn_jsval_ops()->iter_drain(src->v.jsval.cell, false, NULL);
|
|
2773
|
+
if (!drained) return; /* pending */
|
|
2774
|
+
for (size_t i = 0; i < drained->v.arr.len; i++) {
|
|
2775
|
+
scr_dyn_arr_push(pack, scr_dyn_retain(drained->v.arr.items[i]));
|
|
2776
|
+
}
|
|
2777
|
+
scr_dyn_release(drained);
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2724
2780
|
ScrJsonBuf b;
|
|
2725
2781
|
scr_jb_init(&b);
|
|
2726
2782
|
switch (src->kind) {
|
|
@@ -2793,7 +2849,7 @@ ScrDyn *scr_dyn_obj_entries(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJ
|
|
|
2793
2849
|
|
|
2794
2850
|
/* ── DOMException's dyn-touching half ─────────────────────────────────
|
|
2795
2851
|
* Construction/cause/clone live HERE (not scr_error.c) so the error unit
|
|
2796
|
-
* stays linkable without the
|
|
2852
|
+
* stays linkable without the checked-dynamic tree (the runtime C-unit tests link
|
|
2797
2853
|
* subsets). The cause teardown installs through scr_error.c's hook
|
|
2798
2854
|
* before any cause can exist. */
|
|
2799
2855
|
|
|
@@ -2852,10 +2908,10 @@ ScrError *scr_domex_clone(ScrError *e, const ScrDyn *options) {
|
|
|
2852
2908
|
}
|
|
2853
2909
|
|
|
2854
2910
|
/* ── atob/btoa — the WHATWG base64 globals (Node globals since v16) ───
|
|
2855
|
-
* They live HERE (not scr_string.c) because the argument is a
|
|
2856
|
-
* WebIDL ToString runs over the
|
|
2911
|
+
* They live HERE (not scr_string.c) because the argument is a dyn value:
|
|
2912
|
+
* WebIDL ToString runs over the dyn kind (Node's atob(null) decodes the
|
|
2857
2913
|
* string "null"), and the string unit must stay linkable without the
|
|
2858
|
-
*
|
|
2914
|
+
* dyn. atob is forgiving-base64 exactly — ASCII whitespace stripped, a
|
|
2859
2915
|
* %4==0 input sheds up to two trailing '=', %4==1 refuses, leftover
|
|
2860
2916
|
* bits discard — decoding to the latin1 code points as a UTF-8 string;
|
|
2861
2917
|
* btoa refuses any code point over U+00FF. Malformed input throws the
|