@scriptc/runtime 0.0.21 → 0.0.23
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_async.c +5 -2
- package/src/scr_bytes.c +7 -0
- package/src/scr_cycle.c +403 -62
- package/src/scr_dyn_invoke.c +58 -6
- package/src/scr_fetch.c +6200 -124
- 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 +413 -9
- package/src/scr_lib.c +277 -18
- package/src/scr_runtime.h +211 -27
- 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_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";
|
|
@@ -1409,8 +1637,17 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
|
|
|
1409
1637
|
case SCR_DYN_OBJ:
|
|
1410
1638
|
return scr_str_new("[object Object]", 15);
|
|
1411
1639
|
case SCR_DYN_HANDLE:
|
|
1412
|
-
|
|
1413
|
-
|
|
1640
|
+
if (d->v.handle.tag >= SCR_DYNH_ABORT_SIGNAL &&
|
|
1641
|
+
d->v.handle.tag <= SCR_DYNH_ABORT_CONTROLLER) {
|
|
1642
|
+
ScrJsonBuf b;
|
|
1643
|
+
scr_jb_init(&b);
|
|
1644
|
+
scr_jb_puts(&b, "[object ");
|
|
1645
|
+
scr_jb_puts(&b, scr_dyn_handle_cls(d));
|
|
1646
|
+
scr_jb_putc(&b, ']');
|
|
1647
|
+
return scr_jb_finish(&b);
|
|
1648
|
+
}
|
|
1649
|
+
/* IncomingMessage/ServerResponse/Socket and the other Node handles
|
|
1650
|
+
* inherit Object.prototype.toString without a @@toStringTag. */
|
|
1414
1651
|
return scr_str_new("[object Object]", 15);
|
|
1415
1652
|
case SCR_DYN_PROMISE:
|
|
1416
1653
|
/* Object.prototype.toString with the Promise @@toStringTag. */
|
|
@@ -1447,6 +1684,12 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
|
|
|
1447
1684
|
ScrStr *s = scr_dyn_jsval_ops()->to_str(d->v.jsval.cell);
|
|
1448
1685
|
return s ? s : scr_str_new("", 0);
|
|
1449
1686
|
}
|
|
1687
|
+
case SCR_DYN_TYPED_REF: {
|
|
1688
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1689
|
+
ScrStr *s = scr_dyn_to_string(materialized, enc);
|
|
1690
|
+
scr_dyn_release(materialized);
|
|
1691
|
+
return s;
|
|
1692
|
+
}
|
|
1450
1693
|
case SCR_DYN_UNDEF:
|
|
1451
1694
|
case SCR_DYN_NULL:
|
|
1452
1695
|
default: {
|
|
@@ -1487,25 +1730,60 @@ ScrStr *scr_dyn_string_coerce(const ScrDyn *d) {
|
|
|
1487
1730
|
return scr_dyn_to_string(d, NULL);
|
|
1488
1731
|
}
|
|
1489
1732
|
|
|
1733
|
+
static bool scr_dyn_to_primitive_result_is_object(const ScrDyn *d) {
|
|
1734
|
+
switch (d->kind) {
|
|
1735
|
+
case SCR_DYN_OBJ:
|
|
1736
|
+
case SCR_DYN_ARR:
|
|
1737
|
+
case SCR_DYN_BYTES:
|
|
1738
|
+
case SCR_DYN_FUNC:
|
|
1739
|
+
case SCR_DYN_HANDLE:
|
|
1740
|
+
case SCR_DYN_PROMISE:
|
|
1741
|
+
case SCR_DYN_TYPED_REF:
|
|
1742
|
+
return true;
|
|
1743
|
+
case SCR_DYN_JSVAL:
|
|
1744
|
+
/* Engine scalars normally normalize into their native dyn kinds on
|
|
1745
|
+
* ingress, but preserve the correct answer for any producer that keeps
|
|
1746
|
+
* a wrapped object/function (or a defensively wrapped null). */
|
|
1747
|
+
return !scr_dyn_is_nullish(d) &&
|
|
1748
|
+
(scr_dyn_isl_typeof_is(d, "object") ||
|
|
1749
|
+
scr_dyn_isl_typeof_is(d, "function"));
|
|
1750
|
+
default:
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1490
1755
|
/* JS ToString over a dyn value WITH the object protocol (the WHATWG
|
|
1491
1756
|
* USVString conversions — URLSearchParams names/values): an OBJ whose
|
|
1492
1757
|
* own 'toString' member is callable is invoked with zero arguments (its
|
|
1493
|
-
* throw propagates, catchably);
|
|
1494
|
-
*
|
|
1758
|
+
* throw propagates, catchably); without an own member, an ordinary object
|
|
1759
|
+
* inherits Object.prototype.toString and answers "[object Object]". A
|
|
1760
|
+
* non-primitive answer falls through to 'valueOf' (ToPrimitive's string
|
|
1761
|
+
* hint); exhaustion is the spec's
|
|
1495
1762
|
* "Cannot convert object to primitive value" TypeError. Every other
|
|
1496
1763
|
* kind matches scr_dyn_string_coerce (units RENDER — ToString(null) is
|
|
1497
1764
|
* "null"). Borrows; +1, or NULL with the exception pending. */
|
|
1498
1765
|
ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) {
|
|
1766
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1767
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1768
|
+
ScrStr *out = scr_dyn_string_coerce_js(materialized);
|
|
1769
|
+
scr_dyn_release(materialized);
|
|
1770
|
+
return out;
|
|
1771
|
+
}
|
|
1499
1772
|
if (d->kind == SCR_DYN_OBJ) {
|
|
1500
1773
|
static const char *const hint[2] = { "toString", "valueOf" };
|
|
1501
1774
|
for (int i = 0; i < 2; i++) {
|
|
1502
1775
|
ScrDyn *m = scr_dyn_obj_get(d, hint[i], strlen(hint[i])); /* borrowed */
|
|
1776
|
+
if (!m && i == 0 && !d->null_proto) {
|
|
1777
|
+
return scr_str_new("[object Object]", 15);
|
|
1778
|
+
}
|
|
1503
1779
|
if (!m || m->kind != SCR_DYN_FUNC) continue;
|
|
1780
|
+
/* OrdinaryToPrimitive performs a method call, not a bare function
|
|
1781
|
+
* call: an own coercion hook observes the source object as `this`. */
|
|
1782
|
+
scr_dyn_this_push_dyn(d);
|
|
1504
1783
|
ScrDyn *r = scr_dyn_call(m, NULL, 0, hint[i]);
|
|
1784
|
+
scr_dyn_this_pop();
|
|
1505
1785
|
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) {
|
|
1786
|
+
if (scr_dyn_to_primitive_result_is_object(r)) {
|
|
1509
1787
|
scr_dyn_release(r); /* non-primitive answer: try the next method */
|
|
1510
1788
|
continue;
|
|
1511
1789
|
}
|
|
@@ -1520,6 +1798,81 @@ ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) {
|
|
|
1520
1798
|
return scr_dyn_string_coerce(d);
|
|
1521
1799
|
}
|
|
1522
1800
|
|
|
1801
|
+
/* JS ToNumber over a checked-dynamic value, including OrdinaryToPrimitive's
|
|
1802
|
+
* NUMBER hint for object snapshots. This is the numeric twin of
|
|
1803
|
+
* scr_dyn_string_coerce_js above: valueOf precedes toString, inherited
|
|
1804
|
+
* Object.prototype.valueOf returns the receiver (so conversion continues),
|
|
1805
|
+
* and the inherited toString fallback supplies "[object Object]". Borrows d;
|
|
1806
|
+
* false means an object hook threw or no primitive value could be produced. */
|
|
1807
|
+
bool scr_dyn_number_coerce_js(const ScrDyn *d, double *out) {
|
|
1808
|
+
if (d->kind == SCR_DYN_TYPED_REF) {
|
|
1809
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
1810
|
+
bool ok = scr_dyn_number_coerce_js(materialized, out);
|
|
1811
|
+
scr_dyn_release(materialized);
|
|
1812
|
+
return ok;
|
|
1813
|
+
}
|
|
1814
|
+
switch (d->kind) {
|
|
1815
|
+
case SCR_DYN_NULL:
|
|
1816
|
+
*out = 0.0;
|
|
1817
|
+
return true;
|
|
1818
|
+
case SCR_DYN_BOOL:
|
|
1819
|
+
*out = d->v.b ? 1.0 : 0.0;
|
|
1820
|
+
return true;
|
|
1821
|
+
case SCR_DYN_NUM:
|
|
1822
|
+
*out = d->v.num;
|
|
1823
|
+
return true;
|
|
1824
|
+
case SCR_DYN_STR:
|
|
1825
|
+
*out = scr_string_to_number(d->v.str);
|
|
1826
|
+
return true;
|
|
1827
|
+
case SCR_DYN_UNDEF:
|
|
1828
|
+
*out = NAN;
|
|
1829
|
+
return true;
|
|
1830
|
+
case SCR_DYN_OBJ: {
|
|
1831
|
+
static const char *const hint[2] = { "valueOf", "toString" };
|
|
1832
|
+
for (int i = 0; i < 2; i++) {
|
|
1833
|
+
ScrDyn *m = scr_dyn_obj_get(d, hint[i], strlen(hint[i])); /* borrowed */
|
|
1834
|
+
if (!m) {
|
|
1835
|
+
if (!d->null_proto && i == 0) {
|
|
1836
|
+
/* Inherited Object.prototype.valueOf returns the object, so the
|
|
1837
|
+
* number-hint protocol advances to toString. */
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
if (!d->null_proto && i == 1) {
|
|
1841
|
+
*out = NAN; /* Number("[object Object]") */
|
|
1842
|
+
return true;
|
|
1843
|
+
}
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
if (m->kind != SCR_DYN_FUNC) continue;
|
|
1847
|
+
scr_dyn_this_push_dyn(d);
|
|
1848
|
+
ScrDyn *r = scr_dyn_call(m, NULL, 0, hint[i]);
|
|
1849
|
+
scr_dyn_this_pop();
|
|
1850
|
+
if (!r) return false;
|
|
1851
|
+
if (scr_dyn_to_primitive_result_is_object(r)) {
|
|
1852
|
+
scr_dyn_release(r);
|
|
1853
|
+
continue;
|
|
1854
|
+
}
|
|
1855
|
+
bool ok = scr_dyn_number_coerce_js(r, out);
|
|
1856
|
+
scr_dyn_release(r);
|
|
1857
|
+
return ok;
|
|
1858
|
+
}
|
|
1859
|
+
static const char msg[] = "Cannot convert object to primitive value";
|
|
1860
|
+
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
1861
|
+
return false;
|
|
1862
|
+
}
|
|
1863
|
+
default: {
|
|
1864
|
+
/* Arrays, byte views, functions, promises, and native handles inherit a
|
|
1865
|
+
* valueOf that returns the receiver, then use their existing JS-exact
|
|
1866
|
+
* string rendering for the numeric conversion. */
|
|
1867
|
+
ScrStr *text = scr_dyn_string_coerce(d);
|
|
1868
|
+
if (!text) return false;
|
|
1869
|
+
*out = scr_string_to_number(text);
|
|
1870
|
+
scr_str_release(text);
|
|
1871
|
+
return true;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1523
1876
|
/* The checked-dynamic keyed WRITE (`h.k = v` on a dyn receiver): OBJ sets
|
|
1524
1877
|
* the member (later writes win, insertion order — JS); undefined/null
|
|
1525
1878
|
* throws Node's "Cannot set properties of ..."; every other kind throws
|
|
@@ -1532,6 +1885,12 @@ static const char *scr_dyn_kind_name(const ScrDyn *d);
|
|
|
1532
1885
|
* valid dense index, every other kind false (tsc admits `in` only on
|
|
1533
1886
|
* object-typed operands). Borrows both; never throws. */
|
|
1534
1887
|
bool scr_dyn_has_key(const ScrDyn *v, const ScrStr *key) {
|
|
1888
|
+
if (v->kind == SCR_DYN_TYPED_REF) {
|
|
1889
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(v);
|
|
1890
|
+
bool out = scr_dyn_has_key(materialized, key);
|
|
1891
|
+
scr_dyn_release(materialized);
|
|
1892
|
+
return out;
|
|
1893
|
+
}
|
|
1535
1894
|
if (v->kind == SCR_DYN_OBJ) return scr_dyn_obj_get(v, key->data, key->len) != NULL;
|
|
1536
1895
|
if (v->kind == SCR_DYN_ARR) {
|
|
1537
1896
|
if (key->len == 6 && memcmp(key->data, "length", 6) == 0) return true;
|
|
@@ -1549,6 +1908,13 @@ bool scr_dyn_has_key(const ScrDyn *v, const ScrStr *key) {
|
|
|
1549
1908
|
}
|
|
1550
1909
|
|
|
1551
1910
|
void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
1911
|
+
if (recv->kind == SCR_DYN_TYPED_REF) {
|
|
1912
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(recv);
|
|
1913
|
+
scr_dyn_key_set(materialized, key, value);
|
|
1914
|
+
if (!scr_exc_pending()) scr_dyn_typed_ref_commit(recv);
|
|
1915
|
+
scr_dyn_release(materialized);
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1552
1918
|
if (recv->kind == SCR_DYN_OBJ) {
|
|
1553
1919
|
scr_dyn_obj_set(recv, key->data, key->len, scr_dyn_retain(value));
|
|
1554
1920
|
return;
|
|
@@ -1689,6 +2055,12 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1689
2055
|
scr_str_release(j);
|
|
1690
2056
|
return;
|
|
1691
2057
|
}
|
|
2058
|
+
case SCR_DYN_TYPED_REF: {
|
|
2059
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
|
2060
|
+
scr_jb_put_dyn(b, materialized);
|
|
2061
|
+
scr_dyn_release(materialized);
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
1692
2064
|
case SCR_DYN_OBJ: {
|
|
1693
2065
|
scr_jb_putc(b, '{');
|
|
1694
2066
|
bool first = true;
|
|
@@ -1747,6 +2119,7 @@ static const char *scr_dyn_kind_name(const ScrDyn *d) {
|
|
|
1747
2119
|
case SCR_DYN_JSVAL: return "an island value"; /* "got an island value" — validated
|
|
1748
2120
|
* extraction of engine-held values has no armed route yet (lane
|
|
1749
2121
|
* dom-jsval-long-tail); the failure names the world honestly. */
|
|
2122
|
+
case SCR_DYN_TYPED_REF: return "object";
|
|
1750
2123
|
}
|
|
1751
2124
|
return "unknown";
|
|
1752
2125
|
}
|
|
@@ -2307,6 +2680,11 @@ bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
|
|
|
2307
2680
|
* strict equality answers). Mixed kinds already answered false above
|
|
2308
2681
|
* — a dyn copy is a different object, which is Node's answer too. */
|
|
2309
2682
|
return a == b || scr_dyn_jsval_ops()->strict_eq(a->v.jsval.cell, b->v.jsval.cell);
|
|
2683
|
+
case SCR_DYN_TYPED_REF:
|
|
2684
|
+
return a->v.typed_ref.ptr == b->v.typed_ref.ptr &&
|
|
2685
|
+
a->v.typed_ref.type_key_len == b->v.typed_ref.type_key_len &&
|
|
2686
|
+
memcmp(a->v.typed_ref.type_key, b->v.typed_ref.type_key,
|
|
2687
|
+
a->v.typed_ref.type_key_len) == 0;
|
|
2310
2688
|
default: return a == b;
|
|
2311
2689
|
}
|
|
2312
2690
|
}
|
|
@@ -2433,6 +2811,12 @@ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
|
|
|
2433
2811
|
* silent wrong answer. Loud fence (lane dom-jsval-long-tail). */
|
|
2434
2812
|
scr_dyn_isl_fence(v, "structuredClone");
|
|
2435
2813
|
return NULL;
|
|
2814
|
+
case SCR_DYN_TYPED_REF: {
|
|
2815
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(v);
|
|
2816
|
+
ScrDyn *out = scr_sc_clone(materialized, up);
|
|
2817
|
+
scr_dyn_release(materialized);
|
|
2818
|
+
return out;
|
|
2819
|
+
}
|
|
2436
2820
|
case SCR_DYN_FUNC:
|
|
2437
2821
|
case SCR_DYN_HANDLE:
|
|
2438
2822
|
default: {
|
|
@@ -2544,6 +2928,12 @@ static void scr_dyn_objwalk_push(ScrDyn *out, ScrObjWalk mode, const char *key,
|
|
|
2544
2928
|
}
|
|
2545
2929
|
|
|
2546
2930
|
static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
|
|
2931
|
+
if (v->kind == SCR_DYN_TYPED_REF) {
|
|
2932
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(v);
|
|
2933
|
+
ScrDyn *out = scr_dyn_objwalk(materialized, mode);
|
|
2934
|
+
scr_dyn_release(materialized);
|
|
2935
|
+
return out;
|
|
2936
|
+
}
|
|
2547
2937
|
if (v->kind == SCR_DYN_UNDEF || v->kind == SCR_DYN_NULL) {
|
|
2548
2938
|
static const char msg[] = "Cannot convert undefined or null to object";
|
|
2549
2939
|
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
@@ -2667,6 +3057,12 @@ ScrDyn *scr_dyn_obj_keys(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWAL
|
|
|
2667
3057
|
* existing two-arg stance — a dyn array target has no property table). */
|
|
2668
3058
|
static void scr_dyn_assign_from(ScrDyn *target, const ScrDyn *src) {
|
|
2669
3059
|
if (target->kind != SCR_DYN_OBJ) return;
|
|
3060
|
+
if (src->kind == SCR_DYN_TYPED_REF) {
|
|
3061
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(src);
|
|
3062
|
+
scr_dyn_assign_from(target, materialized);
|
|
3063
|
+
scr_dyn_release(materialized);
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
2670
3066
|
if (src->kind == SCR_DYN_UNDEF || src->kind == SCR_DYN_NULL) return;
|
|
2671
3067
|
if (src->kind == SCR_DYN_OBJ) {
|
|
2672
3068
|
for (size_t i = 0; i < src->v.obj.len; i++) {
|
|
@@ -2714,6 +3110,14 @@ ScrDyn *scr_dyn_assign(ScrDyn *target, const ScrDyn *src) {
|
|
|
2714
3110
|
if (!scr_dyn_jsval_ops()->assign(target->v.jsval.cell, src)) return NULL;
|
|
2715
3111
|
return scr_dyn_retain(target);
|
|
2716
3112
|
}
|
|
3113
|
+
if (target->kind == SCR_DYN_TYPED_REF) {
|
|
3114
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(target);
|
|
3115
|
+
ScrDyn *assigned = scr_dyn_assign(materialized, src);
|
|
3116
|
+
if (!scr_exc_pending()) scr_dyn_typed_ref_commit(target);
|
|
3117
|
+
scr_dyn_release(assigned);
|
|
3118
|
+
scr_dyn_release(materialized);
|
|
3119
|
+
return scr_exc_pending() ? NULL : scr_dyn_retain(target);
|
|
3120
|
+
}
|
|
2717
3121
|
if (src->kind == SCR_DYN_JSVAL) {
|
|
2718
3122
|
/* A wrapped SOURCE onto a dyn target: the engine lists its own
|
|
2719
3123
|
* [key, value] pairs (getters running) and each lands as a dyn
|