@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_http2.c
CHANGED
|
@@ -2192,6 +2192,7 @@ static void scr_h2_compat_dispatch(ScrH2Session *s, ScrH2Stream *st, const ScrH2
|
|
|
2192
2192
|
scr_http_h2_req_header(req, name, value);
|
|
2193
2193
|
}
|
|
2194
2194
|
ScrHttpRes *res = scr_http_h2_res_new(s->sock, st);
|
|
2195
|
+
scr_http_res_set_req(res, req); /* res.req — the compat pair's backref */
|
|
2195
2196
|
st->compat_req = scr_http_req_retain(req);
|
|
2196
2197
|
st->compat_res = scr_http_res_retain(res);
|
|
2197
2198
|
/* the req owns the READ side: the stream's own 'end' bookkeeping is
|
|
@@ -2302,6 +2303,55 @@ static ScrH2SrvCtx *scr_h2_server_ctx(ScrNetServer *s) {
|
|
|
2302
2303
|
return ctx;
|
|
2303
2304
|
}
|
|
2304
2305
|
|
|
2306
|
+
/* createSecureServer(options, handler): the eager COMPAT handler is the
|
|
2307
|
+
* first 'request' listener over the ALPN=h2 server — the
|
|
2308
|
+
* create_server_req route, secured. */
|
|
2309
|
+
ScrNetServer *scr_http2_create_secure_server_req(const char *cert, size_t cert_len,
|
|
2310
|
+
const char *key, size_t key_len,
|
|
2311
|
+
ScrClosure *handler /*moves, nullable*/,
|
|
2312
|
+
ScrHttpReqFn fn) {
|
|
2313
|
+
ScrNetServer *s = scr_http2_create_secure_server(cert, cert_len, key, key_len);
|
|
2314
|
+
if (handler != NULL) {
|
|
2315
|
+
ScrH2SrvCtx *ctx = scr_h2_server_ctx(s);
|
|
2316
|
+
if (ctx != NULL) scr_net_ls_add(&ctx->request_ls, handler, (void *)fn, false);
|
|
2317
|
+
else scr_closure_release(handler);
|
|
2318
|
+
}
|
|
2319
|
+
return s;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
/* createSecureServer with a RUNTIME options record (the divergence-66
|
|
2323
|
+
* stance): allowHTTP1 picks the flavor — truthy is the https/HTTP-1.1
|
|
2324
|
+
* compatibility server, absent or falsy the ALPN=h2 server — cert/key
|
|
2325
|
+
* ride the shared TLS server walk (scr_tls.c: out-of-bounds members
|
|
2326
|
+
* throw the catchable runtime fence), and the h2 session-tuning keys
|
|
2327
|
+
* drop exactly like the literal walk ignores them. NULL + pending
|
|
2328
|
+
* exception when the walk fences. */
|
|
2329
|
+
ScrNetServer *scr_http2_create_secure_server_dyn(const ScrDyn *opts /*borrowed*/,
|
|
2330
|
+
ScrClosure *handler /*moves, nullable*/,
|
|
2331
|
+
ScrHttpReqFn fn) {
|
|
2332
|
+
bool allow_http1 = false;
|
|
2333
|
+
if (opts != NULL && opts->kind == SCR_DYN_OBJ) {
|
|
2334
|
+
for (size_t i = 0; i < opts->v.obj.len; i++) {
|
|
2335
|
+
if (strcmp(opts->v.obj.entries[i].key, "allowHTTP1") == 0) {
|
|
2336
|
+
allow_http1 = scr_dyn_truthy(opts->v.obj.entries[i].value);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
ScrBytes *cert, *key;
|
|
2341
|
+
if (!scr_tls_srv_opts_walk(opts, "http2.createSecureServer", &cert, &key)) {
|
|
2342
|
+
scr_closure_release(handler);
|
|
2343
|
+
return NULL;
|
|
2344
|
+
}
|
|
2345
|
+
ScrNetServer *s = allow_http1
|
|
2346
|
+
? scr_https_create_server((const char *)cert->data, cert->len,
|
|
2347
|
+
(const char *)key->data, key->len, handler, fn)
|
|
2348
|
+
: scr_http2_create_secure_server_req((const char *)cert->data, cert->len,
|
|
2349
|
+
(const char *)key->data, key->len, handler, fn);
|
|
2350
|
+
scr_bytes_release(cert);
|
|
2351
|
+
scr_bytes_release(key);
|
|
2352
|
+
return s;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2305
2355
|
void scr_http2_server_on_stream(ScrNetServer *s, ScrClosure *cb /*moves*/, ScrH2StreamFn fn,
|
|
2306
2356
|
bool once) {
|
|
2307
2357
|
ScrH2SrvCtx *ctx = scr_h2_server_ctx(s);
|
|
@@ -2589,7 +2639,7 @@ void scr_http2_settings_thunk0(ScrClosure *cb, ScrDyn *settings) {
|
|
|
2589
2639
|
}
|
|
2590
2640
|
|
|
2591
2641
|
/* The typed lane's DYN spellings (session receivers with mustCall-style
|
|
2592
|
-
* callbacks): the raw
|
|
2642
|
+
* callbacks): the raw dyn function crosses the lib boundary and fires
|
|
2593
2643
|
* through the same runtime-built listener closures the dyn handle ops
|
|
2594
2644
|
* use — Node's exact payload shapes. */
|
|
2595
2645
|
static void scr_h2_dyn_fire_settings(ScrClosure *cb, ScrDyn *settings /*+1*/);
|
|
@@ -2689,7 +2739,7 @@ void scr_http2_goaway_thunk0(ScrClosure *cb, double code, double last) {
|
|
|
2689
2739
|
* parameter, or a handle stored in an untyped binding) dispatch their
|
|
2690
2740
|
* members here, the scr_http.c req/res ops precedent: `.on(...)` wires a
|
|
2691
2741
|
* runtime-built listener closure over the dyn callback, header events
|
|
2692
|
-
* build a
|
|
2742
|
+
* build a dyn headers object from the pairs, method calls decode dyn
|
|
2693
2743
|
* args onto the static entry points, and property reads box the scalar
|
|
2694
2744
|
* answers. Real-but-unmodeled members throw the loud ladder; names the
|
|
2695
2745
|
* surface never had throw Node's "<x> is not a function". */
|
|
@@ -2791,7 +2841,7 @@ static void scr_h2_dyn_fire_goaway(ScrClosure *cb, double code, double last) {
|
|
|
2791
2841
|
scr_dyn_release(fn);
|
|
2792
2842
|
}
|
|
2793
2843
|
|
|
2794
|
-
/* Flatten a
|
|
2844
|
+
/* Flatten a dyn headers object into the flat pairs array (numbers via
|
|
2795
2845
|
* String(n); the :status/:method/... pseudo-headers pass through). */
|
|
2796
2846
|
static ScrArr *scr_h2_dom_to_pairs(const ScrDyn *headers) {
|
|
2797
2847
|
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
|