mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-29 07:04:09 +08:00
structuredClone of checked-dynamic values builds a fresh tree (shared inputs are copied per path, cycles throw), so its containers join one ScrDynRegion as they complete, exactly like JSON.parse; a BigInt or bytes leaf clears the region, because intact containers promise JSON.stringify inert leaves only. scr_message_decode (worker, MessagePort and BroadcastChannel payloads) tags its containers before dropping the decoder's references when every node is a JSON primitive, undefined, array or object, arrays carry no named properties, and every container edge leads to a higher encoder id. Ids are assigned parents first, so that proves the graph acyclic; shared subtrees reached again from a later node and cycles stay untagged. Region API for other whole-document producers: scr_dyn_region_open, scr_dyn_region_tag (on each completed container) and scr_dyn_region_close. Node layout is unchanged: arrays and objects keep the region pointer added for parse regions (arr.region / obj.region in the capsule payload space).
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
// structuredClone of plain data builds a fresh document; serializing it,
|
|
// writing into it and closing cycles through it behave as for any object.
|
|
const source: any = JSON.parse('{"a":[1,2,{"b":"x"}],"n":null,"s":"text","f":1.5}');
|
|
source.missing = undefined;
|
|
const clone: any = structuredClone(source);
|
|
console.log(JSON.stringify(clone));
|
|
console.log(JSON.stringify(clone, null, 2));
|
|
console.log(clone !== source, clone.a !== source.a, JSON.stringify(source) === JSON.stringify(clone));
|
|
|
|
// A cycle closed after cloning is still collected and observable.
|
|
const tree: any = structuredClone(JSON.parse('{"items":[{"id":1},{"id":2}]}'));
|
|
tree.items[0].owner = tree;
|
|
console.log(tree.items[0].owner === tree, Object.keys(tree.items[0]).join(","));
|
|
try {
|
|
JSON.stringify(tree);
|
|
} catch (error) {
|
|
console.log((error as Error).name);
|
|
}
|
|
tree.items[0].owner = null;
|
|
console.log(JSON.stringify(tree));
|
|
|
|
// Members JSON cannot spell as plain data keep their serialization.
|
|
const bytesHolder: any = { n: 3 };
|
|
bytesHolder.bytes = new Uint8Array([1, 2]);
|
|
console.log(JSON.stringify(structuredClone(bytesHolder)));
|
|
const bigHolder: any = { n: 3 };
|
|
bigHolder.big = 10n;
|
|
try {
|
|
JSON.stringify(structuredClone(bigHolder));
|
|
} catch (error) {
|
|
console.log((error as Error).name);
|
|
}
|
|
|
|
// Many short-lived clones, some closed into cycles.
|
|
let total = 0;
|
|
for (let i = 0; i < 3000; i++) {
|
|
const input: any = JSON.parse('{"list":[0,{"k":0}]}');
|
|
input.list[1].k = i;
|
|
const copy: any = structuredClone(input);
|
|
if (i % 3 === 0) copy.list[1].back = copy;
|
|
if (i % 5 === 0) copy.list.push(copy.list);
|
|
total = total + copy.list[1].k;
|
|
}
|
|
console.log(total);
|