@scriptc/runtime 0.0.20 → 0.0.22
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 +23 -0
- package/src/scr_bytes.c +7 -0
- package/src/scr_dyn_invoke.c +58 -6
- package/src/scr_fetch.c +5624 -123
- package/src/scr_fetch_curl.c +21 -9
- package/src/scr_http.c +13 -0
- package/src/scr_inspect.c +6 -0
- package/src/scr_island.c +19 -0
- package/src/scr_json.c +327 -7
- package/src/scr_runtime.h +124 -17
- package/src/scr_tls.c +15 -11
- package/src/scr_tls_ca.c +35 -8
- package/src/scr_url.c +103 -15
- package/src/scr_web.c +77 -1
- package/vendor/README.md +4 -4
package/src/scr_fetch_curl.c
CHANGED
|
@@ -360,14 +360,17 @@ static JSValue fx_host_start(JSContext *ctx, JSValueConst this_val, int argc,
|
|
|
360
360
|
curl_easy_setopt(e, CURLOPT_ACCEPT_ENCODING, ""); /* gzip in, transparent out */
|
|
361
361
|
curl_easy_setopt(e, CURLOPT_PROTOCOLS_STR, "http,https");
|
|
362
362
|
curl_easy_setopt(e, CURLOPT_SUPPRESS_CONNECT_HEADERS, 1L);
|
|
363
|
-
/* Node
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const char *
|
|
370
|
-
|
|
363
|
+
/* Node's global opt-in and Vercel's request-local EnvProxyDispatcher both
|
|
364
|
+
* activate libcurl's environment proxy lookup. Otherwise explicitly turn
|
|
365
|
+
* it off, because libcurl enables proxy variables by default. */
|
|
366
|
+
JSValue envproxyv = JS_GetPropertyStr(ctx, req, "useEnvProxy");
|
|
367
|
+
bool use_env_proxy = JS_ToBool(ctx, envproxyv) > 0;
|
|
368
|
+
JS_FreeValue(ctx, envproxyv);
|
|
369
|
+
const char *global_env_proxy = getenv("NODE_USE_ENV_PROXY");
|
|
370
|
+
use_env_proxy = use_env_proxy ||
|
|
371
|
+
(global_env_proxy != NULL &&
|
|
372
|
+
strcmp(global_env_proxy, "1") == 0);
|
|
373
|
+
if (!use_env_proxy) {
|
|
371
374
|
curl_easy_setopt(e, CURLOPT_PROXY, "");
|
|
372
375
|
}
|
|
373
376
|
|
|
@@ -486,6 +489,15 @@ static const char fx_glue[] =
|
|
|
486
489
|
" url = String(input);\n"
|
|
487
490
|
" }\n"
|
|
488
491
|
" init = init === undefined || init === null ? {} : init;\n"
|
|
492
|
+
" let useEnvProxy = g.process?.env?.NODE_USE_ENV_PROXY === '1';\n"
|
|
493
|
+
" const initDispatcher = init.dispatcher;\n"
|
|
494
|
+
" if (initDispatcher !== undefined) {\n"
|
|
495
|
+
" const dispatcher = initDispatcher;\n"
|
|
496
|
+
" const methods = ['dispatch', 'close', 'destroy', 'agents', 'getAgent', 'shouldProxy', 'parseNoProxy'];\n"
|
|
497
|
+
" const known = dispatcher !== null && typeof dispatcher === 'object' && dispatcher.constructor?.name === 'EnvProxyDispatcher' && methods.every((member) => typeof dispatcher[member] === 'function');\n"
|
|
498
|
+
" if (!known) throw new TypeError('unsupported RequestInit option: dispatcher');\n"
|
|
499
|
+
" useEnvProxy = true;\n"
|
|
500
|
+
" }\n"
|
|
489
501
|
" // An explicit init.signal overrides the Request's, null included.\n"
|
|
490
502
|
" if (init.signal !== undefined) {\n"
|
|
491
503
|
" if (init.signal !== null && !(init.signal instanceof g.AbortSignal)) {\n"
|
|
@@ -546,7 +558,7 @@ static const char fx_glue[] =
|
|
|
546
558
|
" const flat = [];\n"
|
|
547
559
|
" for (const pair of headers) { flat.push(pair[0], pair[1]); }\n"
|
|
548
560
|
" id = host.start(\n"
|
|
549
|
-
" { url, method, headers: flat, body: body === null ? undefined : body },\n"
|
|
561
|
+
" { url, method, headers: flat, body: body === null ? undefined : body, useEnvProxy },\n"
|
|
550
562
|
" {\n"
|
|
551
563
|
" onResponse(status, statusText, raw, finalUrl, redirected) {\n"
|
|
552
564
|
" resolved = true;\n"
|
package/src/scr_http.c
CHANGED
|
@@ -2608,6 +2608,19 @@ static bool scr_http_client_parse_head(ScrHttpConn *conn, size_t head_len) {
|
|
|
2608
2608
|
return false;
|
|
2609
2609
|
}
|
|
2610
2610
|
|
|
2611
|
+
/*
|
|
2612
|
+
* Informational responses do not settle the request. Node emits an
|
|
2613
|
+
* `information` event for these and keeps parsing until the final
|
|
2614
|
+
* response; this runtime has no information listener surface yet, so
|
|
2615
|
+
* consume the head and continue. 101 is the upgrade case below.
|
|
2616
|
+
*/
|
|
2617
|
+
if (status >= 100 && status < 200 && status != 101) {
|
|
2618
|
+
memmove(conn->buf, conn->buf + head_len, conn->len - head_len);
|
|
2619
|
+
conn->len -= head_len;
|
|
2620
|
+
scr_http_req_release(res);
|
|
2621
|
+
return true;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2611
2624
|
/* framing: HEAD requests and 204/304 responses have NO body; chunked
|
|
2612
2625
|
* wins over Content-Length; neither means EOF-delimited */
|
|
2613
2626
|
bool chunked = false;
|
package/src/scr_inspect.c
CHANGED
|
@@ -849,6 +849,12 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
|
|
849
849
|
scr_throw_error(SCR_ERR_ERROR, ib_take(&out));
|
|
850
850
|
return scr_str_new("", 0);
|
|
851
851
|
}
|
|
852
|
+
case SCR_DYN_TYPED_REF: {
|
|
853
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
854
|
+
ScrStr *out = scr_insp_dyn(materialized, recurse, depth);
|
|
855
|
+
scr_dyn_release(materialized);
|
|
856
|
+
return out;
|
|
857
|
+
}
|
|
852
858
|
case SCR_DYN_PROMISE: {
|
|
853
859
|
/* Node renders Promise { <pending> } / Promise { value } — the
|
|
854
860
|
* settled-value rendering pulls in the whole inspector; fence
|
package/src/scr_island.c
CHANGED
|
@@ -1635,6 +1635,25 @@ ScrJsval *scr_jsval_call(ScrJsval *f, int argc, ScrJsval **argv) {
|
|
|
1635
1635
|
return isl_cell_new(r);
|
|
1636
1636
|
}
|
|
1637
1637
|
|
|
1638
|
+
/* Call an already-resolved computed member with its original receiver. The
|
|
1639
|
+
* frontend performs GetValue before argument evaluation, then arrives here
|
|
1640
|
+
* after the arguments are ready; keeping callee and receiver separate avoids
|
|
1641
|
+
* a second getter read while preserving method `this`. */
|
|
1642
|
+
ScrJsval *scr_jsval_call_this(ScrJsval *f, ScrJsval *receiver, int argc,
|
|
1643
|
+
ScrJsval **argv) {
|
|
1644
|
+
isl_entry();
|
|
1645
|
+
JSValue stack_args[8];
|
|
1646
|
+
JSValue *args = argc <= 8 ? stack_args : malloc((size_t)argc * sizeof(JSValue));
|
|
1647
|
+
for (int i = 0; i < argc; i++) args[i] = argv[i]->v;
|
|
1648
|
+
JSValue r = JS_Call(isl_ctx, f->v, receiver->v, argc, args);
|
|
1649
|
+
if (args != stack_args) free(args);
|
|
1650
|
+
if (JS_IsException(r)) {
|
|
1651
|
+
isl_bridge_exception();
|
|
1652
|
+
return NULL;
|
|
1653
|
+
}
|
|
1654
|
+
return isl_cell_new(r);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1638
1657
|
/* Spread application on an island callee (jsOp callSpread) — the prelude
|
|
1639
1658
|
* helper's real `f(...pre, ...spread)`, so iterator protocols are the
|
|
1640
1659
|
* engine's own and the guards front-run V8's exact spread-call TypeError
|
package/src/scr_json.c
CHANGED
|
@@ -333,6 +333,8 @@ static ScrDyn *scr_dyn_alloc(ScrDynKind kind) {
|
|
|
333
333
|
d->v.arr.len = 0; /* cap/items preserved from the node's last life */
|
|
334
334
|
} else if (kind == SCR_DYN_OBJ) {
|
|
335
335
|
d->v.obj.len = 0; /* cap/entries preserved */
|
|
336
|
+
d->v.obj.source_identity = NULL;
|
|
337
|
+
d->v.obj.source_access = NULL;
|
|
336
338
|
} else {
|
|
337
339
|
memset(&d->v, 0, sizeof d->v);
|
|
338
340
|
}
|
|
@@ -369,6 +371,9 @@ void scr_dyn_release(ScrDyn *d) {
|
|
|
369
371
|
free(d->v.obj.entries[i].key);
|
|
370
372
|
scr_dyn_release(d->v.obj.entries[i].value);
|
|
371
373
|
}
|
|
374
|
+
if (d->v.obj.source_identity) {
|
|
375
|
+
d->v.obj.source_access(d->v.obj.source_identity, false);
|
|
376
|
+
}
|
|
372
377
|
break;
|
|
373
378
|
case SCR_DYN_FUNC:
|
|
374
379
|
scr_closure_release(d->v.fn.clo); /* sig/name are static literals */
|
|
@@ -387,6 +392,16 @@ void scr_dyn_release(ScrDyn *d) {
|
|
|
387
392
|
* only producer) — same story as the promise arm. */
|
|
388
393
|
scr_dyn_jsval_ops()->release(d->v.jsval.cell);
|
|
389
394
|
break;
|
|
395
|
+
case SCR_DYN_TYPED_REF:
|
|
396
|
+
scr_dyn_release(d->v.typed_ref.materialized);
|
|
397
|
+
while (d->v.typed_ref.casts) {
|
|
398
|
+
ScrDynTypedCast *cast = d->v.typed_ref.casts;
|
|
399
|
+
d->v.typed_ref.casts = cast->next;
|
|
400
|
+
cast->release(cast->ptr);
|
|
401
|
+
free(cast);
|
|
402
|
+
}
|
|
403
|
+
d->v.typed_ref.release(d->v.typed_ref.ptr);
|
|
404
|
+
break;
|
|
390
405
|
default:
|
|
391
406
|
break; /* null/bool/num have no children */
|
|
392
407
|
}
|
|
@@ -440,6 +455,12 @@ void scr_dyn_arr_push(ScrDyn *arr, ScrDyn *item) {
|
|
|
440
455
|
* the generic "Spread syntax requires ...iterable[Symbol.iterator] to be
|
|
441
456
|
* a function". Borrows src. */
|
|
442
457
|
void scr_dyn_arr_push_spread(ScrDyn *arr, const ScrDyn *src, const char *what) {
|
|
458
|
+
if (src->kind == SCR_DYN_TYPED_REF) {
|
|
459
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(src);
|
|
460
|
+
scr_dyn_arr_push_spread(arr, materialized, what);
|
|
461
|
+
scr_dyn_release(materialized);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
443
464
|
if (src->kind == SCR_DYN_ARR) {
|
|
444
465
|
for (size_t i = 0; i < src->v.arr.len; i++) {
|
|
445
466
|
scr_dyn_arr_push(arr, scr_dyn_retain(src->v.arr.items[i]));
|
|
@@ -502,6 +523,12 @@ void scr_dyn_arr_push_spread(ScrDyn *arr, const ScrDyn *src, const char *what) {
|
|
|
502
523
|
* undefined no kind prefix, null V8's "object null"). Borrows both; +1 or
|
|
503
524
|
* NULL with the TypeError pending. */
|
|
504
525
|
ScrDyn *scr_dyn_iter_pack(const ScrDyn *src, const ScrStr *msg) {
|
|
526
|
+
if (src->kind == SCR_DYN_TYPED_REF) {
|
|
527
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(src);
|
|
528
|
+
ScrDyn *out = scr_dyn_iter_pack(materialized, msg);
|
|
529
|
+
scr_dyn_release(materialized);
|
|
530
|
+
return out;
|
|
531
|
+
}
|
|
505
532
|
if (src->kind == SCR_DYN_ARR || src->kind == SCR_DYN_BYTES || src->kind == SCR_DYN_STR) {
|
|
506
533
|
ScrDyn *out = scr_dyn_new_arr();
|
|
507
534
|
scr_dyn_arr_push_spread(out, src, ""); /* iterable kinds never consult `what` */
|
|
@@ -611,6 +638,21 @@ ScrDyn *scr_dyn_new_str(ScrStr *s) {
|
|
|
611
638
|
|
|
612
639
|
ScrDyn *scr_dyn_new_arr(void) { return scr_dyn_alloc(SCR_DYN_ARR); }
|
|
613
640
|
ScrDyn *scr_dyn_new_obj(void) { return scr_dyn_alloc(SCR_DYN_OBJ); }
|
|
641
|
+
ScrDyn *scr_dyn_new_obj_with_identity(
|
|
642
|
+
void *source, void *(*source_retain)(void *),
|
|
643
|
+
ScrDyn *(*source_access)(void *, bool materialize)) {
|
|
644
|
+
ScrDyn *d = scr_dyn_alloc(SCR_DYN_OBJ);
|
|
645
|
+
d->v.obj.source_identity = source_retain(source);
|
|
646
|
+
d->v.obj.source_access = source_access;
|
|
647
|
+
return d;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
bool scr_dyn_obj_same_source(const ScrDyn *a, const ScrDyn *b) {
|
|
651
|
+
return a && b && a->kind == SCR_DYN_OBJ && b->kind == SCR_DYN_OBJ &&
|
|
652
|
+
a->v.obj.source_identity && b->v.obj.source_identity &&
|
|
653
|
+
a->v.obj.source_identity == b->v.obj.source_identity;
|
|
654
|
+
}
|
|
655
|
+
|
|
614
656
|
ScrDyn *scr_dyn_new_obj_null_proto(void) {
|
|
615
657
|
ScrDyn *d = scr_dyn_alloc(SCR_DYN_OBJ);
|
|
616
658
|
d->null_proto = true;
|
|
@@ -629,6 +671,136 @@ ScrDyn *scr_dyn_new_buffer_copy(const ScrBytes *b) {
|
|
|
629
671
|
return d;
|
|
630
672
|
}
|
|
631
673
|
|
|
674
|
+
ScrDyn *scr_dyn_new_typed_ref(
|
|
675
|
+
void *ptr, void *(*retain)(void *), void (*release)(void *),
|
|
676
|
+
const char *type_key, size_t type_key_len,
|
|
677
|
+
ScrDyn *(*materialize)(void *),
|
|
678
|
+
void (*commit)(void *, const ScrDyn *)) {
|
|
679
|
+
ScrDyn *d = scr_dyn_alloc(SCR_DYN_TYPED_REF);
|
|
680
|
+
d->v.typed_ref.ptr = retain(ptr);
|
|
681
|
+
d->v.typed_ref.retain = retain;
|
|
682
|
+
d->v.typed_ref.release = release;
|
|
683
|
+
d->v.typed_ref.type_key = type_key;
|
|
684
|
+
d->v.typed_ref.type_key_len = type_key_len;
|
|
685
|
+
d->v.typed_ref.materialize = materialize;
|
|
686
|
+
d->v.typed_ref.commit = commit;
|
|
687
|
+
d->v.typed_ref.materialized = NULL;
|
|
688
|
+
d->v.typed_ref.casts = NULL;
|
|
689
|
+
return d;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
bool scr_dyn_typed_ref_is(
|
|
693
|
+
const ScrDyn *d, const char *type_key, size_t type_key_len) {
|
|
694
|
+
return d && d->kind == SCR_DYN_TYPED_REF &&
|
|
695
|
+
d->v.typed_ref.type_key_len == type_key_len &&
|
|
696
|
+
memcmp(d->v.typed_ref.type_key, type_key, type_key_len) == 0;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
void *scr_dyn_typed_ref_unbox(const ScrDyn *d) {
|
|
700
|
+
return d->v.typed_ref.retain(d->v.typed_ref.ptr);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
static void scr_dyn_typed_ref_preserve_child(
|
|
704
|
+
ScrDyn **fresh_slot, ScrDyn *cached_child) {
|
|
705
|
+
ScrDyn *fresh_child = *fresh_slot;
|
|
706
|
+
if (!cached_child || !fresh_child ||
|
|
707
|
+
cached_child->kind != SCR_DYN_TYPED_REF ||
|
|
708
|
+
fresh_child->kind != SCR_DYN_TYPED_REF ||
|
|
709
|
+
!scr_dyn_strict_eq(cached_child, fresh_child)) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
*fresh_slot = scr_dyn_retain(cached_child);
|
|
713
|
+
scr_dyn_release(fresh_child);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/* A live record/array materializer creates typed capsules for mutable
|
|
717
|
+
* children. Preserve an existing child capsule when the fresh view points
|
|
718
|
+
* at the same static source, so retaining `value.child` across parent
|
|
719
|
+
* refreshes keeps JavaScript reference identity as well as liveness. */
|
|
720
|
+
static void scr_dyn_typed_ref_preserve_children(
|
|
721
|
+
ScrDyn *cached, ScrDyn *fresh) {
|
|
722
|
+
if (cached->kind != fresh->kind) return;
|
|
723
|
+
if (cached->kind == SCR_DYN_ARR) {
|
|
724
|
+
size_t n = cached->v.arr.len < fresh->v.arr.len
|
|
725
|
+
? cached->v.arr.len
|
|
726
|
+
: fresh->v.arr.len;
|
|
727
|
+
for (size_t i = 0; i < n; i++) {
|
|
728
|
+
scr_dyn_typed_ref_preserve_child(
|
|
729
|
+
&fresh->v.arr.items[i], cached->v.arr.items[i]);
|
|
730
|
+
}
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (cached->kind == SCR_DYN_OBJ) {
|
|
734
|
+
for (size_t i = 0; i < fresh->v.obj.len; i++) {
|
|
735
|
+
ScrDynEntry *entry = &fresh->v.obj.entries[i];
|
|
736
|
+
ScrDyn *cached_child = scr_dyn_obj_get(
|
|
737
|
+
cached, entry->key, entry->key_len);
|
|
738
|
+
scr_dyn_typed_ref_preserve_child(&entry->value, cached_child);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
ScrDyn *scr_dyn_typed_ref_materialize(const ScrDyn *d) {
|
|
744
|
+
ScrDyn *capsule = (ScrDyn *)d;
|
|
745
|
+
if (!capsule->v.typed_ref.materialized) {
|
|
746
|
+
capsule->v.typed_ref.materialized =
|
|
747
|
+
capsule->v.typed_ref.materialize(capsule->v.typed_ref.ptr);
|
|
748
|
+
} else {
|
|
749
|
+
/* Keep the stable dyn object identity while refreshing its contents
|
|
750
|
+
* from the live typed source. The fresh snapshot owns exactly one
|
|
751
|
+
* reference; swapping payloads lets its release dispose the old
|
|
752
|
+
* detached contents without changing the cached node's address. */
|
|
753
|
+
ScrDyn *fresh =
|
|
754
|
+
capsule->v.typed_ref.materialize(capsule->v.typed_ref.ptr);
|
|
755
|
+
scr_dyn_typed_ref_preserve_children(
|
|
756
|
+
capsule->v.typed_ref.materialized, fresh);
|
|
757
|
+
size_t cached_rc = capsule->v.typed_ref.materialized->rc;
|
|
758
|
+
size_t fresh_rc = fresh->rc;
|
|
759
|
+
ScrDyn old = *capsule->v.typed_ref.materialized;
|
|
760
|
+
*capsule->v.typed_ref.materialized = *fresh;
|
|
761
|
+
capsule->v.typed_ref.materialized->rc = cached_rc;
|
|
762
|
+
*fresh = old;
|
|
763
|
+
fresh->rc = fresh_rc;
|
|
764
|
+
scr_dyn_release(fresh);
|
|
765
|
+
}
|
|
766
|
+
return scr_dyn_retain(capsule->v.typed_ref.materialized);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
void scr_dyn_typed_ref_commit(ScrDyn *d) {
|
|
770
|
+
if (d && d->kind == SCR_DYN_TYPED_REF && d->v.typed_ref.commit &&
|
|
771
|
+
d->v.typed_ref.materialized) {
|
|
772
|
+
d->v.typed_ref.commit(d->v.typed_ref.ptr,
|
|
773
|
+
d->v.typed_ref.materialized);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
void *scr_dyn_typed_ref_cached_cast(
|
|
778
|
+
const ScrDyn *d, const char *type_key, size_t type_key_len) {
|
|
779
|
+
for (ScrDynTypedCast *cast = d->v.typed_ref.casts; cast;
|
|
780
|
+
cast = cast->next) {
|
|
781
|
+
if (cast->type_key_len == type_key_len &&
|
|
782
|
+
memcmp(cast->type_key, type_key, type_key_len) == 0) {
|
|
783
|
+
return cast->retain(cast->ptr);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return NULL;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
void scr_dyn_typed_ref_cache_cast(
|
|
790
|
+
ScrDyn *d, const char *type_key, size_t type_key_len, void *ptr,
|
|
791
|
+
void *(*retain)(void *), void (*release)(void *)) {
|
|
792
|
+
if (!ptr) return;
|
|
793
|
+
ScrDynTypedCast *cast = malloc(sizeof *cast);
|
|
794
|
+
if (!cast) scr_trap("scriptc: out of memory\n");
|
|
795
|
+
cast->type_key = type_key;
|
|
796
|
+
cast->type_key_len = type_key_len;
|
|
797
|
+
cast->ptr = retain(ptr);
|
|
798
|
+
cast->retain = retain;
|
|
799
|
+
cast->release = release;
|
|
800
|
+
cast->next = d->v.typed_ref.casts;
|
|
801
|
+
d->v.typed_ref.casts = cast;
|
|
802
|
+
}
|
|
803
|
+
|
|
632
804
|
ScrBytes *scr_dyn_bytes_copy_out(const ScrDyn *d) {
|
|
633
805
|
return scr_bytes_copy(d->v.bytes); /* extraction copies too (+1) */
|
|
634
806
|
}
|
|
@@ -760,6 +932,14 @@ const char *scr_dyn_specific_type(const ScrDyn *cb, char *detail, size_t cap) {
|
|
|
760
932
|
* normalization — pick by the engine's typeof. */
|
|
761
933
|
d = scr_dyn_isl_typeof_is(cb, "function") ? "function" : "an instance of Object";
|
|
762
934
|
break;
|
|
935
|
+
case SCR_DYN_TYPED_REF: {
|
|
936
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(cb);
|
|
937
|
+
const char *specific = scr_dyn_specific_type(materialized, detail, cap);
|
|
938
|
+
if (specific != detail) snprintf(detail, cap, "%s", specific);
|
|
939
|
+
scr_dyn_release(materialized);
|
|
940
|
+
d = detail;
|
|
941
|
+
break;
|
|
942
|
+
}
|
|
763
943
|
case SCR_DYN_BOOL:
|
|
764
944
|
snprintf(detail, cap, "type boolean (%s)", cb->v.b ? "true" : "false");
|
|
765
945
|
break;
|
|
@@ -931,6 +1111,24 @@ const ScrDynJsvalOps *scr_dyn_jsval_ops(void) {
|
|
|
931
1111
|
}
|
|
932
1112
|
|
|
933
1113
|
bool scr_dyn_isl_typeof_is(const ScrDyn *d, const char *name) {
|
|
1114
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1115
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1116
|
+
bool out = scr_dyn_isl_typeof_is(materialized, name);
|
|
1117
|
+
if (materialized->kind != SCR_DYN_JSVAL) {
|
|
1118
|
+
bool object =
|
|
1119
|
+
materialized->kind == SCR_DYN_NULL ||
|
|
1120
|
+
materialized->kind == SCR_DYN_OBJ ||
|
|
1121
|
+
materialized->kind == SCR_DYN_ARR ||
|
|
1122
|
+
materialized->kind == SCR_DYN_BYTES ||
|
|
1123
|
+
materialized->kind == SCR_DYN_HANDLE ||
|
|
1124
|
+
materialized->kind == SCR_DYN_PROMISE;
|
|
1125
|
+
out = (strcmp(name, "object") == 0 && object) ||
|
|
1126
|
+
(strcmp(name, "function") == 0 &&
|
|
1127
|
+
materialized->kind == SCR_DYN_FUNC);
|
|
1128
|
+
}
|
|
1129
|
+
scr_dyn_release(materialized);
|
|
1130
|
+
return out;
|
|
1131
|
+
}
|
|
934
1132
|
if (d->kind != SCR_DYN_JSVAL) return false;
|
|
935
1133
|
ScrStr *t = scr_dyn_jsval_ops()->type_of(d->v.jsval.cell);
|
|
936
1134
|
size_t n = strlen(name);
|
|
@@ -940,10 +1138,26 @@ bool scr_dyn_isl_typeof_is(const ScrDyn *d, const char *name) {
|
|
|
940
1138
|
}
|
|
941
1139
|
|
|
942
1140
|
bool scr_dyn_isl_is_array(const ScrDyn *d) {
|
|
1141
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1142
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1143
|
+
bool out = materialized->kind == SCR_DYN_ARR ||
|
|
1144
|
+
scr_dyn_isl_is_array(materialized);
|
|
1145
|
+
scr_dyn_release(materialized);
|
|
1146
|
+
return out;
|
|
1147
|
+
}
|
|
943
1148
|
return d->kind == SCR_DYN_JSVAL && scr_dyn_jsval_ops()->is_array(d->v.jsval.cell);
|
|
944
1149
|
}
|
|
945
1150
|
|
|
946
1151
|
bool scr_dyn_isl_is_error(const ScrDyn *d) {
|
|
1152
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1153
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1154
|
+
bool out =
|
|
1155
|
+
(materialized->kind == SCR_DYN_OBJ &&
|
|
1156
|
+
scr_dyn_obj_get(materialized, "%error", 6) != NULL) ||
|
|
1157
|
+
scr_dyn_isl_is_error(materialized);
|
|
1158
|
+
scr_dyn_release(materialized);
|
|
1159
|
+
return out;
|
|
1160
|
+
}
|
|
947
1161
|
return d->kind == SCR_DYN_JSVAL && scr_dyn_jsval_ops()->is_error(d->v.jsval.cell);
|
|
948
1162
|
}
|
|
949
1163
|
|
|
@@ -1103,7 +1317,8 @@ bool scr_dyn_truthy(const ScrDyn *d) {
|
|
|
1103
1317
|
case SCR_DYN_BYTES:
|
|
1104
1318
|
case SCR_DYN_FUNC:
|
|
1105
1319
|
case SCR_DYN_HANDLE:
|
|
1106
|
-
case SCR_DYN_PROMISE:
|
|
1320
|
+
case SCR_DYN_PROMISE:
|
|
1321
|
+
case SCR_DYN_TYPED_REF: return true;
|
|
1107
1322
|
case SCR_DYN_JSVAL:
|
|
1108
1323
|
/* Route to the engine's ToBoolean: objects/arrays/functions are
|
|
1109
1324
|
* true, but the symbol/bigint edge (0n is falsy) needs the engine. */
|
|
@@ -1116,6 +1331,12 @@ bool scr_dyn_truthy(const ScrDyn *d) {
|
|
|
1116
1331
|
* null answers "object" — JS's oldest wart, preserved. */
|
|
1117
1332
|
ScrStr *scr_dyn_typeof(const ScrDyn *d) {
|
|
1118
1333
|
const char *s;
|
|
1334
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1335
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1336
|
+
ScrStr *out = scr_dyn_typeof(materialized);
|
|
1337
|
+
scr_dyn_release(materialized);
|
|
1338
|
+
return out;
|
|
1339
|
+
}
|
|
1119
1340
|
/* An island value answers the ENGINE's typeof — "object" for the
|
|
1120
1341
|
* wrapped objects/arrays, "function" for engine functions (row 1 of
|
|
1121
1342
|
* the jsval→dyn op table; scalars normalized away at wrap time). */
|
|
@@ -1127,7 +1348,8 @@ ScrStr *scr_dyn_typeof(const ScrDyn *d) {
|
|
|
1127
1348
|
case SCR_DYN_ARR:
|
|
1128
1349
|
case SCR_DYN_BYTES:
|
|
1129
1350
|
case SCR_DYN_HANDLE:
|
|
1130
|
-
case SCR_DYN_PROMISE:
|
|
1351
|
+
case SCR_DYN_PROMISE:
|
|
1352
|
+
case SCR_DYN_TYPED_REF: s = "object"; break; /* handled above */
|
|
1131
1353
|
case SCR_DYN_BOOL: s = "boolean"; break;
|
|
1132
1354
|
case SCR_DYN_NUM: s = "number"; break;
|
|
1133
1355
|
case SCR_DYN_STR: s = "string"; break;
|
|
@@ -1222,6 +1444,12 @@ static bool scr_dyn_json_write(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1222
1444
|
scr_str_release(j);
|
|
1223
1445
|
return true;
|
|
1224
1446
|
}
|
|
1447
|
+
case SCR_DYN_TYPED_REF: {
|
|
1448
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1449
|
+
bool present = scr_dyn_json_write(b, materialized);
|
|
1450
|
+
scr_dyn_release(materialized);
|
|
1451
|
+
return present;
|
|
1452
|
+
}
|
|
1225
1453
|
case SCR_DYN_HANDLE:
|
|
1226
1454
|
default: {
|
|
1227
1455
|
const char *msg = "JSON.stringify of a runtime handle is not supported yet";
|
|
@@ -1447,6 +1675,12 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
|
|
|
1447
1675
|
ScrStr *s = scr_dyn_jsval_ops()->to_str(d->v.jsval.cell);
|
|
1448
1676
|
return s ? s : scr_str_new("", 0);
|
|
1449
1677
|
}
|
|
1678
|
+
case SCR_DYN_TYPED_REF: {
|
|
1679
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1680
|
+
ScrStr *s = scr_dyn_to_string(materialized, enc);
|
|
1681
|
+
scr_dyn_release(materialized);
|
|
1682
|
+
return s;
|
|
1683
|
+
}
|
|
1450
1684
|
case SCR_DYN_UNDEF:
|
|
1451
1685
|
case SCR_DYN_NULL:
|
|
1452
1686
|
default: {
|
|
@@ -1487,25 +1721,60 @@ ScrStr *scr_dyn_string_coerce(const ScrDyn *d) {
|
|
|
1487
1721
|
return scr_dyn_to_string(d, NULL);
|
|
1488
1722
|
}
|
|
1489
1723
|
|
|
1724
|
+
static bool scr_dyn_to_primitive_result_is_object(const ScrDyn *d) {
|
|
1725
|
+
switch (d->kind) {
|
|
1726
|
+
case SCR_DYN_OBJ:
|
|
1727
|
+
case SCR_DYN_ARR:
|
|
1728
|
+
case SCR_DYN_BYTES:
|
|
1729
|
+
case SCR_DYN_FUNC:
|
|
1730
|
+
case SCR_DYN_HANDLE:
|
|
1731
|
+
case SCR_DYN_PROMISE:
|
|
1732
|
+
case SCR_DYN_TYPED_REF:
|
|
1733
|
+
return true;
|
|
1734
|
+
case SCR_DYN_JSVAL:
|
|
1735
|
+
/* Engine scalars normally normalize into their native dyn kinds on
|
|
1736
|
+
* ingress, but preserve the correct answer for any producer that keeps
|
|
1737
|
+
* a wrapped object/function (or a defensively wrapped null). */
|
|
1738
|
+
return !scr_dyn_is_nullish(d) &&
|
|
1739
|
+
(scr_dyn_isl_typeof_is(d, "object") ||
|
|
1740
|
+
scr_dyn_isl_typeof_is(d, "function"));
|
|
1741
|
+
default:
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1490
1746
|
/* JS ToString over a dyn value WITH the object protocol (the WHATWG
|
|
1491
1747
|
* USVString conversions — URLSearchParams names/values): an OBJ whose
|
|
1492
1748
|
* own 'toString' member is callable is invoked with zero arguments (its
|
|
1493
|
-
* throw propagates, catchably);
|
|
1494
|
-
*
|
|
1749
|
+
* throw propagates, catchably); without an own member, an ordinary object
|
|
1750
|
+
* inherits Object.prototype.toString and answers "[object Object]". A
|
|
1751
|
+
* non-primitive answer falls through to 'valueOf' (ToPrimitive's string
|
|
1752
|
+
* hint); exhaustion is the spec's
|
|
1495
1753
|
* "Cannot convert object to primitive value" TypeError. Every other
|
|
1496
1754
|
* kind matches scr_dyn_string_coerce (units RENDER — ToString(null) is
|
|
1497
1755
|
* "null"). Borrows; +1, or NULL with the exception pending. */
|
|
1498
1756
|
ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) {
|
|
1757
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1758
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1759
|
+
ScrStr *out = scr_dyn_string_coerce_js(materialized);
|
|
1760
|
+
scr_dyn_release(materialized);
|
|
1761
|
+
return out;
|
|
1762
|
+
}
|
|
1499
1763
|
if (d->kind == SCR_DYN_OBJ) {
|
|
1500
1764
|
static const char *const hint[2] = { "toString", "valueOf" };
|
|
1501
1765
|
for (int i = 0; i < 2; i++) {
|
|
1502
1766
|
ScrDyn *m = scr_dyn_obj_get(d, hint[i], strlen(hint[i])); /* borrowed */
|
|
1767
|
+
if (!m && i == 0 && !d->null_proto) {
|
|
1768
|
+
return scr_str_new("[object Object]", 15);
|
|
1769
|
+
}
|
|
1503
1770
|
if (!m || m->kind != SCR_DYN_FUNC) continue;
|
|
1771
|
+
/* OrdinaryToPrimitive performs a method call, not a bare function
|
|
1772
|
+
* call: an own coercion hook observes the source object as `this`. */
|
|
1773
|
+
scr_dyn_this_push_dyn(d);
|
|
1504
1774
|
ScrDyn *r = scr_dyn_call(m, NULL, 0, hint[i]);
|
|
1775
|
+
scr_dyn_this_pop();
|
|
1505
1776
|
if (!r) return NULL; /* the method threw — pending */
|
|
1506
|
-
if (r
|
|
1507
|
-
r->kind == SCR_DYN_FUNC || r->kind == SCR_DYN_HANDLE ||
|
|
1508
|
-
r->kind == SCR_DYN_PROMISE) {
|
|
1777
|
+
if (scr_dyn_to_primitive_result_is_object(r)) {
|
|
1509
1778
|
scr_dyn_release(r); /* non-primitive answer: try the next method */
|
|
1510
1779
|
continue;
|
|
1511
1780
|
}
|
|
@@ -1532,6 +1801,12 @@ static const char *scr_dyn_kind_name(const ScrDyn *d);
|
|
|
1532
1801
|
* valid dense index, every other kind false (tsc admits `in` only on
|
|
1533
1802
|
* object-typed operands). Borrows both; never throws. */
|
|
1534
1803
|
bool scr_dyn_has_key(const ScrDyn *v, const ScrStr *key) {
|
|
1804
|
+
if (v->kind == SCR_DYN_TYPED_REF) {
|
|
1805
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(v);
|
|
1806
|
+
bool out = scr_dyn_has_key(materialized, key);
|
|
1807
|
+
scr_dyn_release(materialized);
|
|
1808
|
+
return out;
|
|
1809
|
+
}
|
|
1535
1810
|
if (v->kind == SCR_DYN_OBJ) return scr_dyn_obj_get(v, key->data, key->len) != NULL;
|
|
1536
1811
|
if (v->kind == SCR_DYN_ARR) {
|
|
1537
1812
|
if (key->len == 6 && memcmp(key->data, "length", 6) == 0) return true;
|
|
@@ -1549,6 +1824,13 @@ bool scr_dyn_has_key(const ScrDyn *v, const ScrStr *key) {
|
|
|
1549
1824
|
}
|
|
1550
1825
|
|
|
1551
1826
|
void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
1827
|
+
if (recv->kind == SCR_DYN_TYPED_REF) {
|
|
1828
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(recv);
|
|
1829
|
+
scr_dyn_key_set(materialized, key, value);
|
|
1830
|
+
if (!scr_exc_pending()) scr_dyn_typed_ref_commit(recv);
|
|
1831
|
+
scr_dyn_release(materialized);
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1552
1834
|
if (recv->kind == SCR_DYN_OBJ) {
|
|
1553
1835
|
scr_dyn_obj_set(recv, key->data, key->len, scr_dyn_retain(value));
|
|
1554
1836
|
return;
|
|
@@ -1689,6 +1971,12 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1689
1971
|
scr_str_release(j);
|
|
1690
1972
|
return;
|
|
1691
1973
|
}
|
|
1974
|
+
case SCR_DYN_TYPED_REF: {
|
|
1975
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1976
|
+
scr_jb_put_dyn(b, materialized);
|
|
1977
|
+
scr_dyn_release(materialized);
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1692
1980
|
case SCR_DYN_OBJ: {
|
|
1693
1981
|
scr_jb_putc(b, '{');
|
|
1694
1982
|
bool first = true;
|
|
@@ -1747,6 +2035,7 @@ static const char *scr_dyn_kind_name(const ScrDyn *d) {
|
|
|
1747
2035
|
case SCR_DYN_JSVAL: return "an island value"; /* "got an island value" — validated
|
|
1748
2036
|
* extraction of engine-held values has no armed route yet (lane
|
|
1749
2037
|
* dom-jsval-long-tail); the failure names the world honestly. */
|
|
2038
|
+
case SCR_DYN_TYPED_REF: return "object";
|
|
1750
2039
|
}
|
|
1751
2040
|
return "unknown";
|
|
1752
2041
|
}
|
|
@@ -2307,6 +2596,11 @@ bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
|
2307
2596
|
* strict equality answers). Mixed kinds already answered false above
|
|
2308
2597
|
* — a dyn copy is a different object, which is Node's answer too. */
|
|
2309
2598
|
return a == b || scr_dyn_jsval_ops()->strict_eq(a->v.jsval.cell, b->v.jsval.cell);
|
|
2599
|
+
case SCR_DYN_TYPED_REF:
|
|
2600
|
+
return a->v.typed_ref.ptr == b->v.typed_ref.ptr &&
|
|
2601
|
+
a->v.typed_ref.type_key_len == b->v.typed_ref.type_key_len &&
|
|
2602
|
+
memcmp(a->v.typed_ref.type_key, b->v.typed_ref.type_key,
|
|
2603
|
+
a->v.typed_ref.type_key_len) == 0;
|
|
2310
2604
|
default: return a == b;
|
|
2311
2605
|
}
|
|
2312
2606
|
}
|
|
@@ -2433,6 +2727,12 @@ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
|
|
|
2433
2727
|
* silent wrong answer. Loud fence (lane dom-jsval-long-tail). */
|
|
2434
2728
|
scr_dyn_isl_fence(v, "structuredClone");
|
|
2435
2729
|
return NULL;
|
|
2730
|
+
case SCR_DYN_TYPED_REF: {
|
|
2731
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(v);
|
|
2732
|
+
ScrDyn *out = scr_sc_clone(materialized, up);
|
|
2733
|
+
scr_dyn_release(materialized);
|
|
2734
|
+
return out;
|
|
2735
|
+
}
|
|
2436
2736
|
case SCR_DYN_FUNC:
|
|
2437
2737
|
case SCR_DYN_HANDLE:
|
|
2438
2738
|
default: {
|
|
@@ -2544,6 +2844,12 @@ static void scr_dyn_objwalk_push(ScrDyn *out, ScrObjWalk mode, const char *key,
|
|
|
2544
2844
|
}
|
|
2545
2845
|
|
|
2546
2846
|
static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
2847
|
+
if (v->kind == SCR_DYN_TYPED_REF) {
|
|
2848
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(v);
|
|
2849
|
+
ScrDyn *out = scr_dyn_objwalk(materialized, mode);
|
|
2850
|
+
scr_dyn_release(materialized);
|
|
2851
|
+
return out;
|
|
2852
|
+
}
|
|
2547
2853
|
if (v->kind == SCR_DYN_UNDEF || v->kind == SCR_DYN_NULL) {
|
|
2548
2854
|
static const char msg[] = "Cannot convert undefined or null to object";
|
|
2549
2855
|
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
@@ -2667,6 +2973,12 @@ ScrDyn *scr_dyn_obj_keys(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWAL
|
|
|
2667
2973
|
* existing two-arg stance — a dyn array target has no property table). */
|
|
2668
2974
|
static void scr_dyn_assign_from(ScrDyn *target, const ScrDyn *src) {
|
|
2669
2975
|
if (target->kind != SCR_DYN_OBJ) return;
|
|
2976
|
+
if (src->kind == SCR_DYN_TYPED_REF) {
|
|
2977
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(src);
|
|
2978
|
+
scr_dyn_assign_from(target, materialized);
|
|
2979
|
+
scr_dyn_release(materialized);
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2670
2982
|
if (src->kind == SCR_DYN_UNDEF || src->kind == SCR_DYN_NULL) return;
|
|
2671
2983
|
if (src->kind == SCR_DYN_OBJ) {
|
|
2672
2984
|
for (size_t i = 0; i < src->v.obj.len; i++) {
|
|
@@ -2714,6 +3026,14 @@ ScrDyn *scr_dyn_assign(ScrDyn *target, const ScrDyn *src) {
|
|
|
2714
3026
|
if (!scr_dyn_jsval_ops()->assign(target->v.jsval.cell, src)) return NULL;
|
|
2715
3027
|
return scr_dyn_retain(target);
|
|
2716
3028
|
}
|
|
3029
|
+
if (target->kind == SCR_DYN_TYPED_REF) {
|
|
3030
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(target);
|
|
3031
|
+
ScrDyn *assigned = scr_dyn_assign(materialized, src);
|
|
3032
|
+
if (!scr_exc_pending()) scr_dyn_typed_ref_commit(target);
|
|
3033
|
+
scr_dyn_release(assigned);
|
|
3034
|
+
scr_dyn_release(materialized);
|
|
3035
|
+
return scr_exc_pending() ? NULL : scr_dyn_retain(target);
|
|
3036
|
+
}
|
|
2717
3037
|
if (src->kind == SCR_DYN_JSVAL) {
|
|
2718
3038
|
/* A wrapped SOURCE onto a dyn target: the engine lists its own
|
|
2719
3039
|
* [key, value] pairs (getters running) and each lands as a dyn
|