@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_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
|
|
@@ -1510,10 +1533,10 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
|
1510
1533
|
return;
|
|
1511
1534
|
}
|
|
1512
1535
|
if (recv->kind == SCR_DYN_ARR) {
|
|
1513
|
-
/* An INDEX write on a
|
|
1536
|
+
/* An INDEX write on a dyn array (`args[i] = v` — the variadic-rest
|
|
1514
1537
|
* rebuild): a canonical numeric key sets/extends the element, holes
|
|
1515
1538
|
* padding with undefined exactly like JS length growth. Non-index
|
|
1516
|
-
* keys keep the throw below (
|
|
1539
|
+
* keys keep the throw below (dyn arrays carry no expando table). */
|
|
1517
1540
|
size_t idx = 0;
|
|
1518
1541
|
int is_index = key->len > 0 && !(key->len > 1 && key->data[0] == '0');
|
|
1519
1542
|
for (size_t i = 0; is_index && i < key->len; i++) {
|
|
@@ -1573,7 +1596,7 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
|
1573
1596
|
scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
|
|
1574
1597
|
}
|
|
1575
1598
|
|
|
1576
|
-
/* Node's JSON.stringify over a
|
|
1599
|
+
/* Node's JSON.stringify over a dyn: object members holding undefined DROP,
|
|
1577
1600
|
* array slots holding undefined print null. A bare undefined never arrives
|
|
1578
1601
|
* (the record serializer drops the entry first); print null defensively. */
|
|
1579
1602
|
void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
@@ -1594,7 +1617,7 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1594
1617
|
return;
|
|
1595
1618
|
case SCR_DYN_BYTES: {
|
|
1596
1619
|
/* Node's JSON.stringify over a typed array: the index-keyed object
|
|
1597
|
-
* form — {"0":1,"1":2}. u8 payloads only reach the
|
|
1620
|
+
* form — {"0":1,"1":2}. u8 payloads only reach the checked-dynamic tree today. */
|
|
1598
1621
|
scr_jb_putc(b, '{');
|
|
1599
1622
|
for (size_t i = 0; i < d->v.bytes->len; i++) {
|
|
1600
1623
|
if (i > 0) scr_jb_putc(b, ',');
|
|
@@ -1633,7 +1656,7 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1633
1656
|
case SCR_DYN_JSVAL: {
|
|
1634
1657
|
/* The ENGINE's own JSON.stringify text splices in (toJSON protocols,
|
|
1635
1658
|
* cycle TypeErrors — all the engine's, bridged catchably). An engine
|
|
1636
|
-
* FUNCTION serializes like the
|
|
1659
|
+
* FUNCTION serializes like the checked-dynamic tree's FUNC kind (dropped from objects
|
|
1637
1660
|
* by the member loop below; null defensively elsewhere). */
|
|
1638
1661
|
if (scr_dyn_isl_typeof_is(d, "function")) {
|
|
1639
1662
|
scr_jb_puts(b, "null");
|
|
@@ -2232,9 +2255,9 @@ ScrDyn *scr_json_parse(ScrStr *text) {
|
|
|
2232
2255
|
void *scr_dyn_retain_v(void *d) { return scr_dyn_retain((ScrDyn *)d); }
|
|
2233
2256
|
void scr_dyn_release_v(void *d) { scr_dyn_release((ScrDyn *)d); }
|
|
2234
2257
|
|
|
2235
|
-
/* JS === over two
|
|
2258
|
+
/* JS === over two dyn values: scalars by value (NaN false, ±0 equal via
|
|
2236
2259
|
* C ==; strings bytewise), units by kind, everything reference-shaped by
|
|
2237
|
-
* node IDENTITY (the
|
|
2260
|
+
* node IDENTITY (the checked-dynamic tree's object identity). Never throws. */
|
|
2238
2261
|
bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
2239
2262
|
if (a->kind != b->kind) return false;
|
|
2240
2263
|
switch (a->kind) {
|
|
@@ -2261,7 +2284,7 @@ bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
|
2261
2284
|
/* Identity is the ENGINE VALUE, not the box or even the cell: two
|
|
2262
2285
|
* wraps of one engine value compare ===-equal (the engine's own
|
|
2263
2286
|
* strict equality answers). Mixed kinds already answered false above
|
|
2264
|
-
* — a
|
|
2287
|
+
* — a dyn copy is a different object, which is Node's answer too. */
|
|
2265
2288
|
return a == b || scr_dyn_jsval_ops()->strict_eq(a->v.jsval.cell, b->v.jsval.cell);
|
|
2266
2289
|
default: return a == b;
|
|
2267
2290
|
}
|
|
@@ -2290,10 +2313,10 @@ ScrDyn *scr_dyn_fn_get(const ScrDyn *d, const char *key, size_t key_len) {
|
|
|
2290
2313
|
return NULL;
|
|
2291
2314
|
}
|
|
2292
2315
|
|
|
2293
|
-
/* ── structuredClone over the
|
|
2316
|
+
/* ── structuredClone over the checked-dynamic tree ─────────────────────────────────────
|
|
2294
2317
|
* The JSON-safe subset plus bytes, deep. Functions and handle kinds
|
|
2295
2318
|
* throw the spec's catchable DataCloneError; cycles throw the scriptc
|
|
2296
|
-
* fence (the
|
|
2319
|
+
* fence (the checked-dynamic tree cannot represent them — Node clones cycles; documented
|
|
2297
2320
|
* divergence). Option validation throws Node's exact TypeErrors and is
|
|
2298
2321
|
* shared with scr_domex_clone. */
|
|
2299
2322
|
|
|
@@ -2353,7 +2376,7 @@ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
|
|
|
2353
2376
|
for (const ScrScParent *p = up; p != NULL; p = p->up) {
|
|
2354
2377
|
if (p->node == v) {
|
|
2355
2378
|
static const char msg[] =
|
|
2356
|
-
"structuredClone of cyclic values (the
|
|
2379
|
+
"structuredClone of cyclic values (the checked-dynamic tree cannot represent cycles) is not supported yet";
|
|
2357
2380
|
scr_throw_error_msg(SCR_ERR_ERROR, msg, sizeof msg - 1);
|
|
2358
2381
|
return NULL;
|
|
2359
2382
|
}
|
|
@@ -2392,7 +2415,7 @@ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
|
|
|
2392
2415
|
case SCR_DYN_FUNC:
|
|
2393
2416
|
case SCR_DYN_HANDLE:
|
|
2394
2417
|
default: {
|
|
2395
|
-
/* Node renders the value's source text; the
|
|
2418
|
+
/* Node renders the value's source text; the checked-dynamic tree has none — the
|
|
2396
2419
|
* String() rendering stands in ("function () { [native code] } could
|
|
2397
2420
|
* not be cloned."). */
|
|
2398
2421
|
ScrStr *what = scr_dyn_string_coerce(v);
|
|
@@ -2434,7 +2457,7 @@ ScrDyn *scr_structured_clone_missing(void) {
|
|
|
2434
2457
|
|
|
2435
2458
|
bool scr_dyn_err_instanceof(const ScrDyn *d, double kind) {
|
|
2436
2459
|
/* A JSVAL node never came from a runtime ScrError, so the cache miss
|
|
2437
|
-
* below answers false — the documented contract ("a
|
|
2460
|
+
* below answers false — the documented contract ("a dyn value that
|
|
2438
2461
|
* never came from an error answers false"). An ENGINE TypeError held
|
|
2439
2462
|
* in 'unknown' thus answers false where Node answers true: covered by
|
|
2440
2463
|
* lane dom-jsval-long-tail (needs the engine's class instanceof). */
|
|
@@ -2448,7 +2471,7 @@ bool scr_dyn_err_instanceof(const ScrDyn *d, double kind) {
|
|
|
2448
2471
|
return false;
|
|
2449
2472
|
}
|
|
2450
2473
|
|
|
2451
|
-
/* ── Object.keys/values/entries over the
|
|
2474
|
+
/* ── Object.keys/values/entries over the checked-dynamic tree ──────────────────────────
|
|
2452
2475
|
* JS own-key order: array-index keys ascending first, then the rest in
|
|
2453
2476
|
* insertion order. entries answers [key, value] pairs; values RETAIN
|
|
2454
2477
|
* the member nodes (reference semantics, like JS). Strings/arrays/bytes
|
|
@@ -2471,7 +2494,7 @@ static bool scr_dyn_key_is_index(const char *key, size_t len, double *out) {
|
|
|
2471
2494
|
|
|
2472
2495
|
typedef enum { SCR_OBJWALK_KEYS, SCR_OBJWALK_VALUES, SCR_OBJWALK_ENTRIES } ScrObjWalk;
|
|
2473
2496
|
|
|
2474
|
-
/* A fresh key string boxed into the
|
|
2497
|
+
/* A fresh key string boxed into the checked-dynamic tree: scr_dyn_new_str RETAINS its
|
|
2475
2498
|
* argument, so the local +1 drops right after. */
|
|
2476
2499
|
static ScrDyn *scr_dyn_objwalk_key(const char *key, size_t key_len) {
|
|
2477
2500
|
ScrStr *k = scr_str_new(key, key_len);
|
|
@@ -2507,15 +2530,15 @@ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
|
2507
2530
|
}
|
|
2508
2531
|
if (v->kind == SCR_DYN_JSVAL) {
|
|
2509
2532
|
/* 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
|
|
2533
|
+
* Object.entries' pairs) and the results come back as a NATIVE dyn
|
|
2534
|
+
* array — keys are dyn strings, values wrap per element. */
|
|
2512
2535
|
return scr_dyn_jsval_ops()->obj_walk(v->v.jsval.cell, (int)mode);
|
|
2513
2536
|
}
|
|
2514
2537
|
ScrDyn *out = scr_dyn_new_arr();
|
|
2515
2538
|
if (v->kind == SCR_DYN_OBJ) {
|
|
2516
2539
|
size_t n = v->v.obj.len;
|
|
2517
2540
|
/* Two passes: array-index keys ascending, then the rest in insertion
|
|
2518
|
-
* order (JS's own-key order). Index keys are rare in
|
|
2541
|
+
* order (JS's own-key order). Index keys are rare in dyn objects —
|
|
2519
2542
|
* the ascending pass is a simple selection scan. */
|
|
2520
2543
|
bool *is_index = malloc(n ? n * sizeof *is_index : 1);
|
|
2521
2544
|
double *idx = malloc(n ? n * sizeof *idx : 1);
|
|
@@ -2525,7 +2548,7 @@ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
|
2525
2548
|
size_t index_count = 0;
|
|
2526
2549
|
for (size_t i = 0; i < n; i++) {
|
|
2527
2550
|
const ScrDynEntry *e = &v->v.obj.entries[i];
|
|
2528
|
-
/* Reserved '%'-prefixed members (the
|
|
2551
|
+
/* Reserved '%'-prefixed members (the checked-dynamic tree's error marker) never
|
|
2529
2552
|
* appear in user objects — '%' cannot start a JS identifier-ish
|
|
2530
2553
|
* JSON key from this runtime's own producers; skip defensively. */
|
|
2531
2554
|
is_index[i] = scr_dyn_key_is_index(e->key, e->key_len, &idx[i]);
|
|
@@ -2576,7 +2599,7 @@ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
|
2576
2599
|
return out;
|
|
2577
2600
|
}
|
|
2578
2601
|
if (v->kind == SCR_DYN_STR) {
|
|
2579
|
-
/* JS indexes strings by UTF-16 code units; the
|
|
2602
|
+
/* JS indexes strings by UTF-16 code units; the checked-dynamic tree stores UTF-8.
|
|
2580
2603
|
* Code points walk one at a time — an astral code point stays WHOLE
|
|
2581
2604
|
* (one entry where JS lists two lone surrogates; documented
|
|
2582
2605
|
* approximation, the keys stay dense). */
|
|
@@ -2620,7 +2643,7 @@ ScrDyn *scr_dyn_obj_keys(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWAL
|
|
|
2620
2643
|
* UTF-16 code unit, like Node's String exotic object); nullish sources
|
|
2621
2644
|
* copy nothing (Node skips them) and the scalar/function/handle kinds
|
|
2622
2645
|
* have no own enumerable string keys. Non-OBJ targets copy nothing (the
|
|
2623
|
-
* existing two-arg stance — a
|
|
2646
|
+
* existing two-arg stance — a dyn array target has no property table). */
|
|
2624
2647
|
static void scr_dyn_assign_from(ScrDyn *target, const ScrDyn *src) {
|
|
2625
2648
|
if (target->kind != SCR_DYN_OBJ) return;
|
|
2626
2649
|
if (src->kind == SCR_DYN_UNDEF || src->kind == SCR_DYN_NULL) return;
|
|
@@ -2651,7 +2674,7 @@ static void scr_dyn_assign_from(ScrDyn *target, const ScrDyn *src) {
|
|
|
2651
2674
|
scr_dyn_release(pairs);
|
|
2652
2675
|
}
|
|
2653
2676
|
|
|
2654
|
-
/* Object.assign over
|
|
2677
|
+
/* Object.assign over dyn values: copies `src`'s own members onto `target`
|
|
2655
2678
|
* (last write wins) and answers the target retained (+1). Nullish
|
|
2656
2679
|
* receivers throw Node's ToObject TypeError; nullish sources copy
|
|
2657
2680
|
* nothing; index-keyed sources (arrays/strings/bytes) copy their index
|
|
@@ -2666,13 +2689,13 @@ ScrDyn *scr_dyn_assign(ScrDyn *target, const ScrDyn *src) {
|
|
|
2666
2689
|
/* An ENGINE target: the copy runs in the engine (Object.assign's own
|
|
2667
2690
|
* semantics — setters fire, own-enumerable order); the source enters
|
|
2668
2691
|
* per the uniform conversion (a wrapped source spreads by reference,
|
|
2669
|
-
*
|
|
2692
|
+
* dyn data as the usual member deep copy). */
|
|
2670
2693
|
if (!scr_dyn_jsval_ops()->assign(target->v.jsval.cell, src)) return NULL;
|
|
2671
2694
|
return scr_dyn_retain(target);
|
|
2672
2695
|
}
|
|
2673
2696
|
if (src->kind == SCR_DYN_JSVAL) {
|
|
2674
|
-
/* A wrapped SOURCE onto a
|
|
2675
|
-
* [key, value] pairs (getters running) and each lands as a
|
|
2697
|
+
/* A wrapped SOURCE onto a dyn target: the engine lists its own
|
|
2698
|
+
* [key, value] pairs (getters running) and each lands as a dyn
|
|
2676
2699
|
* member — values wrap per element, scalars normalized. */
|
|
2677
2700
|
if (target->kind == SCR_DYN_OBJ) {
|
|
2678
2701
|
ScrDyn *entries = scr_dyn_jsval_ops()->obj_walk(src->v.jsval.cell, 2);
|
|
@@ -2692,7 +2715,7 @@ ScrDyn *scr_dyn_assign(ScrDyn *target, const ScrDyn *src) {
|
|
|
2692
2715
|
}
|
|
2693
2716
|
|
|
2694
2717
|
/* Variadic Object.assign's argument pack (the `Object.assign({},
|
|
2695
|
-
* ...arr.map(f), tail)` shape): the compiler builds one fresh
|
|
2718
|
+
* ...arr.map(f), tail)` shape): the compiler builds one fresh dyn array
|
|
2696
2719
|
* of sources — plain arguments push borrowed (+1 in), spread arguments
|
|
2697
2720
|
* flatten through the spread-call walk (scr_dyn_arr_push_spread's V8
|
|
2698
2721
|
* TypeError texts, `what` spelling the spread expression for the nullish
|
|
@@ -2721,6 +2744,18 @@ void scr_dyn_pack_push_spread_iter(ScrDyn *pack, const ScrDyn *src) {
|
|
|
2721
2744
|
scr_dyn_arr_push_spread(pack, src, ""); /* iterable kinds never throw */
|
|
2722
2745
|
return;
|
|
2723
2746
|
}
|
|
2747
|
+
if (src->kind == SCR_DYN_JSVAL) {
|
|
2748
|
+
/* A wrapped engine value on the ITERATED path: the engine's own
|
|
2749
|
+
* protocol drains (the kind wording on a non-iterable — the
|
|
2750
|
+
* iterated path's value-describing texts, engine-side). */
|
|
2751
|
+
ScrDyn *drained = scr_dyn_jsval_ops()->iter_drain(src->v.jsval.cell, false, NULL);
|
|
2752
|
+
if (!drained) return; /* pending */
|
|
2753
|
+
for (size_t i = 0; i < drained->v.arr.len; i++) {
|
|
2754
|
+
scr_dyn_arr_push(pack, scr_dyn_retain(drained->v.arr.items[i]));
|
|
2755
|
+
}
|
|
2756
|
+
scr_dyn_release(drained);
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2724
2759
|
ScrJsonBuf b;
|
|
2725
2760
|
scr_jb_init(&b);
|
|
2726
2761
|
switch (src->kind) {
|
|
@@ -2793,7 +2828,7 @@ ScrDyn *scr_dyn_obj_entries(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJ
|
|
|
2793
2828
|
|
|
2794
2829
|
/* ── DOMException's dyn-touching half ─────────────────────────────────
|
|
2795
2830
|
* Construction/cause/clone live HERE (not scr_error.c) so the error unit
|
|
2796
|
-
* stays linkable without the
|
|
2831
|
+
* stays linkable without the checked-dynamic tree (the runtime C-unit tests link
|
|
2797
2832
|
* subsets). The cause teardown installs through scr_error.c's hook
|
|
2798
2833
|
* before any cause can exist. */
|
|
2799
2834
|
|
|
@@ -2852,10 +2887,10 @@ ScrError *scr_domex_clone(ScrError *e, const ScrDyn *options) {
|
|
|
2852
2887
|
}
|
|
2853
2888
|
|
|
2854
2889
|
/* ── 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
|
|
2890
|
+
* They live HERE (not scr_string.c) because the argument is a dyn value:
|
|
2891
|
+
* WebIDL ToString runs over the dyn kind (Node's atob(null) decodes the
|
|
2857
2892
|
* string "null"), and the string unit must stay linkable without the
|
|
2858
|
-
*
|
|
2893
|
+
* dyn. atob is forgiving-base64 exactly — ASCII whitespace stripped, a
|
|
2859
2894
|
* %4==0 input sheds up to two trailing '=', %4==1 refuses, leftover
|
|
2860
2895
|
* bits discard — decoding to the latin1 code points as a UTF-8 string;
|
|
2861
2896
|
* btoa refuses any code point over U+00FF. Malformed input throws the
|
package/src/scr_library.c
CHANGED
|
@@ -251,6 +251,21 @@ ScrBytes *scr_library_bytes_in(const uint8_t *p, size_t len, const char *trap_ms
|
|
|
251
251
|
return b;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
double scr_library_i64_in(int64_t v, const char *trap_msg) {
|
|
255
|
+
/* The inbound declared-integer edge (ask 4): only |v| <= 2^53-1 rides
|
|
256
|
+
* f64 exactly; past it, (double)v silently rounds — a coercion the
|
|
257
|
+
* author never wrote, so the wrapper funnels the host-contract
|
|
258
|
+
* violation instead (same story as an impossible bytes length; the
|
|
259
|
+
* message is the compiler-assembled structured trap-teaching form). */
|
|
260
|
+
if (v > 9007199254740991LL || v < -9007199254740991LL) scr_trap(trap_msg);
|
|
261
|
+
return (double)v;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
double scr_library_u64_in(uint64_t v, const char *trap_msg) {
|
|
265
|
+
if (v > 9007199254740991ULL) scr_trap(trap_msg);
|
|
266
|
+
return (double)v;
|
|
267
|
+
}
|
|
268
|
+
|
|
254
269
|
void scr_library_str_out(ScrStr *s, const uint8_t **out, size_t *out_len) {
|
|
255
270
|
scr_library_arena_keep(s, true);
|
|
256
271
|
*out = (const uint8_t *)s->data; /* NUL-terminated after len (ScrStr layout) */
|
package/src/scr_net.c
CHANGED
|
@@ -1603,7 +1603,7 @@ ScrNetSocket *scr_net_connect(double port, ScrStr *host /*borrowed, nullable*/,
|
|
|
1603
1603
|
}
|
|
1604
1604
|
|
|
1605
1605
|
/* ── the connect option-bag validation ladders (checked-dynamic lane) ──
|
|
1606
|
-
* Node-order validation over
|
|
1606
|
+
* Node-order validation over dyn option values with Node's exact typed
|
|
1607
1607
|
* errors; the honest tail (connect for the validated forms, the
|
|
1608
1608
|
* compiler-rendered fence for bags with unmodeled keys) runs only after
|
|
1609
1609
|
* every validation passes. */
|
package/src/scr_qs.c
CHANGED
|
@@ -429,7 +429,7 @@ void scr_qs_parse_into(ScrMap *out, const ScrStr *qs, const ScrStr *sep,
|
|
|
429
429
|
|
|
430
430
|
/* ── stringify ───────────────────────────────────────────────────────── */
|
|
431
431
|
|
|
432
|
-
/* Node's encodeStringified over one
|
|
432
|
+
/* Node's encodeStringified over one dyn value: strings escape, finite
|
|
433
433
|
* numbers render then escape, booleans are bare, everything else is the
|
|
434
434
|
* empty value. Appends to b. */
|
|
435
435
|
static void qs_stringify_value(QsBuf *b, const ScrDyn *v) {
|