@scriptc/compiler 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.
Files changed (92) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +117 -37
  2. package/dist/backend/cc-cache.test.d.ts +1 -0
  3. package/dist/backend/cc-cache.test.js +2507 -0
  4. package/dist/backend/cc-cache.test.js.map +1 -0
  5. package/dist/backend/cc.d.ts +69 -11
  6. package/dist/backend/cc.js +1937 -222
  7. package/dist/backend/cc.js.map +1 -1
  8. package/dist/backend/emission/emit-async.js +3 -1
  9. package/dist/backend/emission/emit-async.js.map +1 -1
  10. package/dist/backend/emission/emit-exprs.d.ts +10 -0
  11. package/dist/backend/emission/emit-exprs.js +482 -18
  12. package/dist/backend/emission/emit-exprs.js.map +1 -1
  13. package/dist/backend/emission/emit-stmts.js +13 -5
  14. package/dist/backend/emission/emit-stmts.js.map +1 -1
  15. package/dist/backend/emission/emit-types.js +4 -1
  16. package/dist/backend/emission/emit-types.js.map +1 -1
  17. package/dist/backend/emission/emit-walkers.js +108 -7
  18. package/dist/backend/emission/emit-walkers.js.map +1 -1
  19. package/dist/backend/emission/emitter.d.ts +27 -1
  20. package/dist/backend/emission/emitter.js +84 -3
  21. package/dist/backend/emission/emitter.js.map +1 -1
  22. package/dist/backend/llvm/dyn.d.ts +1 -0
  23. package/dist/backend/llvm/dyn.js +305 -8
  24. package/dist/backend/llvm/dyn.js.map +1 -1
  25. package/dist/backend/llvm/emitter.js +831 -23
  26. package/dist/backend/llvm/emitter.js.map +1 -1
  27. package/dist/backend/llvm/shapes.js +3 -2
  28. package/dist/backend/llvm/shapes.js.map +1 -1
  29. package/dist/compat/fetch-profile.d.ts +118 -0
  30. package/dist/compat/fetch-profile.js +531 -0
  31. package/dist/compat/fetch-profile.js.map +1 -0
  32. package/dist/coverage/surface-manifest.js +69 -0
  33. package/dist/coverage/surface-manifest.js.map +1 -1
  34. package/dist/diagnostics/diagnostic.d.ts +10 -0
  35. package/dist/diagnostics/diagnostic.js +27 -1
  36. package/dist/diagnostics/diagnostic.js.map +1 -1
  37. package/dist/frontend/lowering/lower-builtins.d.ts +24 -15
  38. package/dist/frontend/lowering/lower-builtins.js +217 -69
  39. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  40. package/dist/frontend/lowering/lower-calls.js +41 -0
  41. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  42. package/dist/frontend/lowering/lower-classes.js +32 -11
  43. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  44. package/dist/frontend/lowering/lower-exprs.d.ts +3 -3
  45. package/dist/frontend/lowering/lower-exprs.js +87 -15
  46. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  47. package/dist/frontend/lowering/lower-generators.js +7 -1
  48. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  49. package/dist/frontend/lowering/lower-island.d.ts +123 -7
  50. package/dist/frontend/lowering/lower-island.js +1976 -26
  51. package/dist/frontend/lowering/lower-island.js.map +1 -1
  52. package/dist/frontend/lowering/lower-modules.js +17 -1
  53. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  54. package/dist/frontend/lowering/lower-stmts.js +33 -9
  55. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  56. package/dist/frontend/lowering/lowerer.d.ts +40 -0
  57. package/dist/frontend/lowering/lowerer.js +290 -18
  58. package/dist/frontend/lowering/lowerer.js.map +1 -1
  59. package/dist/frontend/lowering/surfaces.js +55 -12
  60. package/dist/frontend/lowering/surfaces.js.map +1 -1
  61. package/dist/frontend/program.d.ts +20 -4
  62. package/dist/frontend/program.js +391 -49
  63. package/dist/frontend/program.js.map +1 -1
  64. package/dist/frontend/resolve.d.ts +10 -0
  65. package/dist/frontend/resolve.js +37 -0
  66. package/dist/frontend/resolve.js.map +1 -1
  67. package/dist/frontend/ts7/enums.d.ts +9 -0
  68. package/dist/frontend/ts7/enums.js +8 -6
  69. package/dist/frontend/ts7/enums.js.map +1 -1
  70. package/dist/frontend/ts7/program.js +4 -1
  71. package/dist/frontend/ts7/program.js.map +1 -1
  72. package/dist/frontend/types.d.ts +16 -9
  73. package/dist/frontend/types.js +119 -38
  74. package/dist/frontend/types.js.map +1 -1
  75. package/dist/index.d.ts +7 -1
  76. package/dist/index.js +70 -14
  77. package/dist/index.js.map +1 -1
  78. package/dist/ir/nodes.d.ts +55 -26
  79. package/dist/ir/nodes.js +42 -29
  80. package/dist/ir/nodes.js.map +1 -1
  81. package/dist/ir/validate.js +106 -9
  82. package/dist/ir/validate.js.map +1 -1
  83. package/dist/library/int-infer.js +7 -1
  84. package/dist/library/int-infer.js.map +1 -1
  85. package/dist/library/int-infer.test.js +80 -0
  86. package/dist/library/int-infer.test.js.map +1 -1
  87. package/dist/library/sidecar-validate.js +5 -0
  88. package/dist/library/sidecar-validate.js.map +1 -1
  89. package/dist/library/sidecar.d.ts +3 -0
  90. package/dist/library/sidecar.js.map +1 -1
  91. package/package.json +2 -2
  92. package/surface-manifest.json +1097 -130
@@ -1,9 +1,345 @@
1
- import { arrayOf, BOOL, BYTES_U8, bytesOf, canMarshalFuncIntoIsland, CHILDSTREAM_T, DYN, F64, islandPromisePayloadTag, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, RUNTIME_ERROR_CLASSES, STRING, typeEquals } from "../../ir/nodes.js";
2
- import { boxAccess, BYTES_NUM_KIND_C, BYTES_NUM_VAR_C, bytesElemKindC, cDecl, cFnPtrCast, cNumberLiteral, cStringLiteral, cType, DV_GET_KIND_C, DV_SET_KIND_C, elemAccess, mapKeyAccess, mapKeyKindC, mapValKindC, retainCallC, vAdapters } from "./emit-types.js";
1
+ import { arrayOf, BOOL, BYTES_U8, bytesOf, canMarshalFuncIntoIsland, CHILDSTREAM_T, DYN, F64, islandPromisePayloadTag, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, RUNTIME_ERROR_CLASSES, STRING, typeEquals, typeKey } from "../../ir/nodes.js";
2
+ import { boxAccess, BYTES_NUM_KIND_C, BYTES_NUM_VAR_C, bytesElemKindC, cDecl, cFnPtrCast, cNumberLiteral, cStringLiteral, cType, DV_GET_KIND_C, DV_SET_KIND_C, elemAccess, mapKeyAccess, mapKeyKindC, mapValKindC, releaseCallC, retainCallC, vAdapters } from "./emit-types.js";
3
3
  import { mangleClassNew, mangleClassRetain, mangleClassStruct, mangleField, mangleFnClosure, mangleFunction, mangleGlobal, mangleLocal, mangleRecordNew, mangleRecordStruct, mangleVtStruct } from "../mangle.js";
4
4
  import { OVERFLOW_MEMBER } from "./emit-shapes.js";
5
5
  import { dynDestrCheckHelper, dynIterNHelper, dynKeyGetHelper } from "./emit-walkers.js";
6
6
  import { genResultThunkFor } from "./emit-async.js";
7
+ function streamTypedRefCommitAdapter(E, t, snapshot, defs) {
8
+ if (t.kind === "bytes") {
9
+ const commit = `${snapshot}_commit`;
10
+ E.walkerProtos.push(`static void ${commit}(void *sc_p, const ScrDyn *sc_d); /* commit live stream element ${typeKey(t)} */`);
11
+ defs.push(`static void ${commit}(void *sc_p, const ScrDyn *sc_d) {`, ` ScrBytes *sc_target = (ScrBytes *)sc_p;`, ` ScrBytes *sc_next = ${E.dynCheckHelper(t)}(sc_d, NULL);`, ` if (!sc_next) return;`, ` scr_bytes_copy_contents(sc_target, sc_next);`, ` scr_bytes_release(sc_next);`, `}`, ``);
12
+ return `&${commit}`;
13
+ }
14
+ if (t.kind === "array") {
15
+ const commit = `${snapshot}_commit`;
16
+ E.walkerProtos.push(`static void ${commit}(void *sc_p, const ScrDyn *sc_d); /* commit live stream element ${typeKey(t)} */`);
17
+ defs.push(`static void ${commit}(void *sc_p, const ScrDyn *sc_d) {`, ` ScrArr *sc_target = (ScrArr *)sc_p;`, ` ScrArr *sc_next = ${E.dynCheckHelper(t)}(sc_d, NULL);`, ` if (!sc_next) return;`, ` size_t sc_target_len = sc_target->len;`, ` size_t sc_target_cap = sc_target->cap;`, ` uint64_t *sc_target_data = sc_target->data;`, ` sc_target->len = sc_next->len;`, ` sc_target->cap = sc_next->cap;`, ` sc_target->data = sc_next->data;`, ` sc_next->len = sc_target_len;`, ` sc_next->cap = sc_target_cap;`, ` sc_next->data = sc_target_data;`, ` scr_arr_release(sc_next);`, `}`, ``);
18
+ return `&${commit}`;
19
+ }
20
+ if (t.kind !== "record")
21
+ return "NULL";
22
+ const shape = E.recordsById.get(t.shapeId);
23
+ if (!shape) {
24
+ throw new Error(`emitter bug: stream typed-ref commit of unknown shape ${t.shapeId}`);
25
+ }
26
+ const commit = `${snapshot}_commit`;
27
+ E.walkerProtos.push(`static void ${commit}(void *sc_p, const ScrDyn *sc_d); /* commit live stream element ${typeKey(t)} */`);
28
+ defs.push(`static void ${commit}(void *sc_p, const ScrDyn *sc_d) {`, ` ${cDecl(t, "sc_target")} = (${cType(t).trim()})sc_p;`, ` ${cDecl(t, "sc_next")} = ${E.dynCheckHelper(t)}(sc_d, NULL);`, ` if (!sc_next) return;`);
29
+ for (const field of shape.fields) {
30
+ const member = mangleField(field.name);
31
+ defs.push(` {`, ` ${cDecl(field.type, "sc_old")} = sc_target->${member};`, ` sc_target->${member} = sc_next->${member};`, ` sc_next->${member} = sc_old;`, ` }`);
32
+ }
33
+ if (shape.indexValue) {
34
+ defs.push(` {`, ` ScrMap *sc_old = sc_target->${OVERFLOW_MEMBER};`, ` sc_target->${OVERFLOW_MEMBER} = sc_next->${OVERFLOW_MEMBER};`, ` sc_next->${OVERFLOW_MEMBER} = sc_old;`, ` }`);
35
+ }
36
+ defs.push(` ${releaseCallC(t, "sc_next")};`, `}`, ``);
37
+ return `&${commit}`;
38
+ }
39
+ function streamTypedRefEligible(t) {
40
+ return t.kind === "record" || t.kind === "array" || t.kind === "bytes";
41
+ }
42
+ /** Build the live dyn view of one typed stream value. Mutable reference
43
+ * children are capsules of their own, so a write through `v.child.x` or
44
+ * `v.items[0]` commits directly to that child's static source instead of
45
+ * disappearing into the parent's detached snapshot. */
46
+ function streamTypedRefAdapter(E, t, ctx, preferredSnapshot) {
47
+ const key = typeKey(t);
48
+ const existing = ctx.adapters.get(key);
49
+ if (existing)
50
+ return existing;
51
+ const snapshot = preferredSnapshot ??
52
+ `${ctx.prefix}_nested_${ctx.adapters.size}`;
53
+ const adapter = { snapshot, commit: "NULL" };
54
+ /* Register before walking children: recursive record/array types refer
55
+ * back to this prototype without recursively generating helpers. */
56
+ ctx.adapters.set(key, adapter);
57
+ E.walkerProtos.push(`static ScrDyn *${snapshot}(void *sc_p); /* materialize live stream value ${key} */`);
58
+ adapter.commit = streamTypedRefCommitAdapter(E, t, snapshot, ctx.defs);
59
+ const box = (child, expr) => {
60
+ if (!streamTypedRefEligible(child)) {
61
+ return `${E.toDynHelper(child)}(${expr})`;
62
+ }
63
+ const nested = streamTypedRefAdapter(E, child, ctx);
64
+ const rc = vAdapters(child);
65
+ const childKey = typeKey(child);
66
+ const keyLit = cStringLiteral(Buffer.from(childKey, "utf8"));
67
+ return `scr_dyn_new_typed_ref(${expr}, &${rc.retain}, &${rc.release}, ${keyLit}, ${Buffer.byteLength(childKey, "utf8")}, &${nested.snapshot}, ${nested.commit})`;
68
+ };
69
+ const lines = [
70
+ `static ScrDyn *${snapshot}(void *sc_p) { /* materialize live stream value ${key} */`,
71
+ ` ${cDecl(t, "v")} = (${cType(t).trim()})sc_p;`,
72
+ ];
73
+ if (t.kind === "record") {
74
+ const shape = E.recordsById.get(t.shapeId);
75
+ if (!shape) {
76
+ throw new Error(`emitter bug: stream typed-ref materialize of unknown shape ${t.shapeId}`);
77
+ }
78
+ const ordinary = shape.indexValue || shape.fields.some((field) => field.name === "handleEvent" && field.type.kind === "func");
79
+ if (ordinary) {
80
+ lines.push(` return ${E.toDynHelper(t)}(v);`, `}`, ``);
81
+ ctx.defs.push(...lines);
82
+ return adapter;
83
+ }
84
+ if (shape.tuple) {
85
+ lines.push(` ScrDyn *d = scr_dyn_new_arr();`);
86
+ const fields = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
87
+ for (const field of fields) {
88
+ lines.push(` scr_dyn_arr_push(d, ${box(field.type, `v->${mangleField(field.name)}`)});`);
89
+ }
90
+ }
91
+ else {
92
+ lines.push(` ScrDyn *d = scr_dyn_new_obj();`);
93
+ const byName = new Map(shape.fields.map((field) => [field.name, field]));
94
+ const order = shape.declaredOrder ?? shape.fields.map((field) => field.name);
95
+ const inOrder = new Set(order);
96
+ const fields = [
97
+ ...order.map((name) => byName.get(name)).filter((field) => field !== undefined),
98
+ ...shape.fields.filter((field) => !inOrder.has(field.name)),
99
+ ];
100
+ for (const field of fields) {
101
+ const keyLit = cStringLiteral(Buffer.from(field.name, "utf8"));
102
+ lines.push(` scr_dyn_obj_set(d, ${keyLit}, ${Buffer.byteLength(field.name, "utf8")}, ${box(field.type, `v->${mangleField(field.name)}`)});`);
103
+ }
104
+ }
105
+ lines.push(` return d;`);
106
+ }
107
+ else if (t.kind === "array") {
108
+ const elem = t.elem;
109
+ lines.push(` ScrDyn *d = scr_dyn_new_arr();`, ` for (size_t sc_i = 0; sc_i < v->len; sc_i++) {`);
110
+ if (elem.kind === "f64") {
111
+ lines.push(` scr_dyn_arr_push(d, ${box(elem, "scr_arr_get_f64(v, (double)sc_i)")});`);
112
+ }
113
+ else if (elem.kind === "bool") {
114
+ lines.push(` scr_dyn_arr_push(d, ${box(elem, "scr_arr_get_bool(v, (double)sc_i)")});`);
115
+ }
116
+ else {
117
+ lines.push(` ${cDecl(elem, "sc_value")} = (${cType(elem).trim()})scr_arr_get_ref(v, (double)sc_i);`, ` scr_dyn_arr_push(d, ${box(elem, "sc_value")});`, ` ${releaseCallC(elem, "sc_value")};`);
118
+ }
119
+ lines.push(` }`, ` return d;`);
120
+ }
121
+ else {
122
+ lines.push(` return ${E.toDynHelper(t)}(v);`);
123
+ }
124
+ lines.push(`}`, ``);
125
+ ctx.defs.push(...lines);
126
+ return adapter;
127
+ }
128
+ /** One per-type capsule adapter for Web API arguments whose JavaScript
129
+ * contract preserves the exact input reference. */
130
+ function liveDynRefAdapter(E, t) {
131
+ const key = typeKey(t);
132
+ const existing = E.liveDynRefAdapters.get(key);
133
+ if (existing)
134
+ return existing;
135
+ if (!streamTypedRefEligible(t)) {
136
+ throw new Error(`emitter bug: live dyn ref of ${key}`);
137
+ }
138
+ const prefix = `sc_ldr_${E.liveDynRefAdapters.size}`;
139
+ const defs = [];
140
+ const adapter = streamTypedRefAdapter(E, t, { prefix, defs, adapters: new Map() }, `${prefix}_materialize`);
141
+ E.liveDynRefAdapters.set(key, adapter);
142
+ E.walkerDefs.push(...defs);
143
+ return adapter;
144
+ }
145
+ /** A union itself is only the tagged box; the identity Web APIs expose is
146
+ * the selected mutable arm. Dispatch on the tag and point the typed capsule
147
+ * directly at that arm's payload. Non-mutable arms keep ordinary dynFrom
148
+ * semantics. */
149
+ function liveDynUnionRefAdapter(E, t) {
150
+ const key = typeKey(t);
151
+ const existing = E.liveDynUnionRefAdapters.get(key);
152
+ if (existing)
153
+ return existing;
154
+ const union = E.unionsById.get(t.unionId);
155
+ if (!union) {
156
+ throw new Error(`emitter bug: live dyn ref of unknown union ${t.unionId}`);
157
+ }
158
+ const mutableArms = union.arms
159
+ .map((arm, tag) => ({ arm, tag }))
160
+ .filter(({ arm }) => streamTypedRefEligible(arm));
161
+ if (mutableArms.length === 0) {
162
+ throw new Error(`emitter bug: live dyn ref of immutable union ${key}`);
163
+ }
164
+ const sym = `sc_ldu_${E.liveDynUnionRefAdapters.size}`;
165
+ E.liveDynUnionRefAdapters.set(key, sym);
166
+ const sig = `static ScrDyn *${sym}(ScrUnion *sc_u)`;
167
+ E.walkerProtos.push(`${sig}; /* materialize live union value ${key} */`);
168
+ const defs = [];
169
+ const adapters = new Map();
170
+ for (const { arm, tag } of mutableArms) {
171
+ const prefix = `${sym}_${tag}`;
172
+ adapters.set(tag, streamTypedRefAdapter(E, arm, { prefix, defs, adapters: new Map() }, `${prefix}_materialize`));
173
+ }
174
+ defs.push(`${sig} {`, ` switch (sc_u->tag) {`);
175
+ for (const { arm, tag } of mutableArms) {
176
+ const adapter = adapters.get(tag);
177
+ const rc = vAdapters(arm);
178
+ const armKey = typeKey(arm);
179
+ const keyLit = cStringLiteral(Buffer.from(armKey, "utf8"));
180
+ defs.push(` case ${tag}:`, ` return scr_dyn_new_typed_ref(scr_union_peek(sc_u), &${rc.retain}, &${rc.release}, ${keyLit}, ${Buffer.byteLength(armKey, "utf8")}, &${adapter.snapshot}, ${adapter.commit});`);
181
+ }
182
+ defs.push(` default:`, ` return ${E.toDynHelper(t)}(sc_u);`, ` }`, `}`, ``);
183
+ E.walkerDefs.push(...defs);
184
+ return sym;
185
+ }
186
+ function streamFromArrayAdapter(E, t) {
187
+ const elem = t.elem;
188
+ const key = typeKey(elem);
189
+ const existing = E.streamFromArrayAdapters.get(key);
190
+ if (existing)
191
+ return existing;
192
+ const sym = `sc_sfa_${E.streamFromArrayAdapters.size}`;
193
+ E.streamFromArrayAdapters.set(key, sym);
194
+ const sig = `static ScrDyn *${sym}(ScrArr *sc_a, double sc_i)`;
195
+ E.walkerProtos.push(`${sig}; /* ReadableStream.from array<${key}> */`);
196
+ const unionDef = elem.kind === "union"
197
+ ? E.unionsById.get(elem.unionId)
198
+ : undefined;
199
+ if (elem.kind === "union" && !unionDef) {
200
+ throw new Error(`emitter bug: streamFrom of unknown union ${elem.unionId}`);
201
+ }
202
+ const unionRefArms = unionDef?.arms
203
+ .map((arm, tag) => ({ arm, tag }))
204
+ .filter(({ arm }) => isRefCounted(arm) && arm.kind !== "dyn" && arm.kind !== "string") ?? [];
205
+ const typedRef = isRefCounted(elem) &&
206
+ elem.kind !== "dyn" &&
207
+ elem.kind !== "string" &&
208
+ elem.kind !== "union";
209
+ const snapshot = `${sym}_materialize`;
210
+ const d = [];
211
+ let commit = "NULL";
212
+ if (typedRef) {
213
+ if (streamTypedRefEligible(elem)) {
214
+ const adapter = streamTypedRefAdapter(E, elem, { prefix: snapshot, defs: d, adapters: new Map() }, snapshot);
215
+ commit = adapter.commit;
216
+ }
217
+ else {
218
+ const snapshotSig = `static ScrDyn *${snapshot}(void *sc_p)`;
219
+ E.walkerProtos.push(`${snapshotSig}; /* materialize stream element ${key} */`);
220
+ d.push(`${snapshotSig} {`, ` return ${E.toDynHelper(elem)}((${cType(elem).trim()})sc_p);`, `}`, ``);
221
+ commit = streamTypedRefCommitAdapter(E, elem, snapshot, d);
222
+ }
223
+ }
224
+ const unionCommits = new Map();
225
+ for (const { arm, tag } of unionRefArms) {
226
+ const armKey = typeKey(arm);
227
+ const armSnapshot = `${snapshot}_${tag}`;
228
+ const snapshotSig = `static ScrDyn *${armSnapshot}(void *sc_p)`;
229
+ E.walkerProtos.push(`${snapshotSig}; /* materialize stream union arm ${armKey} */`);
230
+ d.push(`${snapshotSig} {`, ` return ${E.toDynHelper(arm)}((${cType(arm).trim()})sc_p);`, `}`, ``);
231
+ unionCommits.set(tag, streamTypedRefCommitAdapter(E, arm, armSnapshot, d));
232
+ }
233
+ d.push(`${sig} { /* ReadableStream.from array<${key}> */`);
234
+ if (elem.kind === "f64") {
235
+ d.push(` return ${E.toDynHelper(elem)}(scr_arr_get_f64(sc_a, sc_i));`);
236
+ }
237
+ else if (elem.kind === "bool") {
238
+ d.push(` return ${E.toDynHelper(elem)}(scr_arr_get_bool(sc_a, sc_i));`);
239
+ }
240
+ else {
241
+ d.push(` ${cDecl(elem, "sc_v")} = (${cType(elem).trim()})scr_arr_get_ref(sc_a, sc_i);`);
242
+ if (elem.kind === "union") {
243
+ d.push(` ScrDyn *sc_d;`, ` switch (sc_v->tag) {`);
244
+ for (const { arm, tag } of unionRefArms) {
245
+ const armKey = typeKey(arm);
246
+ const rc = vAdapters(arm);
247
+ const keyLit = cStringLiteral(Buffer.from(armKey, "utf8"));
248
+ d.push(` case ${tag}:`, ` sc_d = scr_dyn_new_typed_ref(scr_union_peek(sc_v), &${rc.retain}, &${rc.release}, ${keyLit}, ${Buffer.byteLength(armKey, "utf8")}, &${snapshot}_${tag}, ${unionCommits.get(tag) ?? "NULL"});`, ` break;`);
249
+ }
250
+ d.push(` default:`, ` sc_d = ${E.toDynHelper(elem)}(sc_v);`, ` break;`, ` }`);
251
+ }
252
+ else if (typedRef) {
253
+ const rc = vAdapters(elem);
254
+ const keyLit = cStringLiteral(Buffer.from(key, "utf8"));
255
+ const keyLen = Buffer.byteLength(key, "utf8");
256
+ d.push(` ScrDyn *sc_d = scr_dyn_new_typed_ref(sc_v, &${rc.retain}, &${rc.release}, ${keyLit}, ${keyLen}, &${snapshot}, ${commit});`);
257
+ }
258
+ else {
259
+ d.push(` ScrDyn *sc_d = ${E.toDynHelper(elem)}(sc_v);`);
260
+ }
261
+ d.push(` ${releaseCallC(elem, "sc_v")};`, ` return sc_d;`);
262
+ }
263
+ d.push(`}`, ``);
264
+ E.walkerDefs.push(...d);
265
+ return sym;
266
+ }
267
+ function dynPromiseAdapter(E, inner) {
268
+ if (!isRefCounted(inner) || inner.kind === "dyn") {
269
+ throw new Error(`dynamic promise adapter requires a concrete reference type, got ${typeKey(inner)}`);
270
+ }
271
+ const key = typeKey(inner);
272
+ const existing = E.dynPromiseAdapters.get(key);
273
+ if (existing)
274
+ return existing;
275
+ const sym = `sc_dpa_${E.dynPromiseAdapters.size}`;
276
+ E.dynPromiseAdapters.set(key, sym);
277
+ const sig = `static void ${sym}(ScrPromise *sc_dst, ScrPromise *sc_src)`;
278
+ E.walkerProtos.push(`${sig}; /* checked-dynamic promise exit ${key} */`);
279
+ let fulfill;
280
+ if (inner.kind === "string") {
281
+ fulfill = `scr_promise_fulfill_str(sc_dst, sc_v);`;
282
+ }
283
+ else {
284
+ const rc = vAdapters(inner);
285
+ fulfill = `scr_promise_fulfill_ref(sc_dst, sc_v, &${rc.retain}, &${rc.release}, ${E.traceArgC(inner)});`;
286
+ }
287
+ E.walkerDefs.push(`${sig} { /* checked-dynamic promise exit ${key} */`, ` ScrDyn *sc_d = (ScrDyn *)scr_promise_payload_ref(sc_src);`, ` ${cDecl(inner, "sc_v")} = ${E.dynCheckHelper(inner)}(sc_d, NULL);`, ` scr_dyn_release(sc_d);`, ` if (scr_exc_pending()) {`, ` scr_promise_reject_pending(sc_dst);`, ` return;`, ` }`, ` ${fulfill}`, `}`, ``);
288
+ return sym;
289
+ }
290
+ /** True when evaluating an index/value expression cannot overwrite the bytes
291
+ * receiver binding. This deliberately small whitelist enables borrowed
292
+ * receivers in typed-array hot loops while side-effecting/calling shapes
293
+ * retain the full snapshot ownership required by JS evaluation order. */
294
+ export function isStableBytesOperand(e, receiverLocalId) {
295
+ switch (e.kind) {
296
+ case "numLit":
297
+ case "boolLit":
298
+ case "varRef":
299
+ case "incDec":
300
+ return true;
301
+ case "assignExpr":
302
+ // An assignment nested under truthiness/ternary can still produce the
303
+ // numeric index/value while overwriting the bytes-typed receiver.
304
+ return e.localId !== receiverLocalId && isStableBytesOperand(e.value, receiverLocalId);
305
+ case "bin":
306
+ return (isStableBytesOperand(e.left, receiverLocalId) &&
307
+ isStableBytesOperand(e.right, receiverLocalId));
308
+ case "unary":
309
+ case "toBool":
310
+ return isStableBytesOperand(e.operand, receiverLocalId);
311
+ case "logical":
312
+ return (isStableBytesOperand(e.left, receiverLocalId) &&
313
+ isStableBytesOperand(e.right, receiverLocalId));
314
+ case "ternary":
315
+ return (isStableBytesOperand(e.cond, receiverLocalId) &&
316
+ isStableBytesOperand(e.then, receiverLocalId) &&
317
+ isStableBytesOperand(e.else_, receiverLocalId));
318
+ case "bytesIntrinsic":
319
+ return ((e.method === "get" || e.method === "length" || e.method === "byteLength") &&
320
+ e.receiver.kind === "varRef" &&
321
+ e.args.every((arg) => isStableBytesOperand(arg, receiverLocalId)));
322
+ default:
323
+ return false;
324
+ }
325
+ }
326
+ /** Evaluate a bytes receiver as a borrow when it is a direct, unboxed
327
+ * binding and all later operands are stable. The binding's scope/global
328
+ * owner then keeps the value alive, avoiding retain/release traffic around
329
+ * every indexed access. Any uncertain shape falls back to an owned temp. */
330
+ export function emitBytesReceiver(E, receiver, following) {
331
+ if (receiver.kind === "varRef" &&
332
+ following.every((operand) => isStableBytesOperand(operand, receiver.localId))) {
333
+ const local = E.currentLocals.get(receiver.localId);
334
+ if (local && !local.boxed) {
335
+ return E.newBorrowedTemp(receiver.type, mangleLocal(receiver.localId));
336
+ }
337
+ if (!local && E.globalsById.has(receiver.localId)) {
338
+ return E.newBorrowedTemp(receiver.type, mangleGlobal(receiver.localId));
339
+ }
340
+ }
341
+ return E.emitExpr(receiver);
342
+ }
7
343
  export function emitExpr(E, e) {
8
344
  switch (e.kind) {
9
345
  case "numLit":
@@ -1020,17 +1356,26 @@ export function emitExpr(E, e) {
1020
1356
  E.emitPendingCheck();
1021
1357
  return t;
1022
1358
  }
1023
- const r = E.emitExpr(e.receiver);
1024
- const args = e.args.map((a) => E.emitExpr(a));
1025
1359
  const method = e.method;
1360
+ const directElementAccess = method === "length" || method === "byteLength" || method === "get";
1361
+ const r = directElementAccess
1362
+ ? emitBytesReceiver(E, e.receiver, e.args)
1363
+ : E.emitExpr(e.receiver);
1364
+ const args = e.args.map((a) => E.emitExpr(a));
1026
1365
  switch (method) {
1027
1366
  case "length":
1028
- return E.newTemp(e.type, `scr_bytes_len(${r.name})`);
1367
+ return E.newTemp(e.type, `(double)${r.name}->len`);
1029
1368
  case "byteLength":
1030
- return E.newTemp(e.type, `scr_bytes_byte_len(${r.name})`);
1369
+ if (e.receiver.type.kind !== "bytes") {
1370
+ throw new Error("emitter bug: bytesIntrinsic byteLength on non-bytes");
1371
+ }
1372
+ return E.newTemp(e.type, `(double)(${r.name}->len * ${e.receiver.type.elem === "u8" ? "1" : "4"})`);
1031
1373
  case "get":
1032
1374
  // Any invalid index traps (the array runtime's discipline).
1033
- return E.newTemp(e.type, `scr_bytes_get(${r.name}, ${args[0].name})`);
1375
+ if (e.receiver.type.kind !== "bytes") {
1376
+ throw new Error("emitter bug: bytesIntrinsic get on non-bytes");
1377
+ }
1378
+ return E.newTemp(e.type, `${E.bytesElementHelper("get", e.receiver.type.elem)}(${r.name}, ${args[0].name})`);
1034
1379
  case "slice":
1035
1380
  // Omitted relative indices default like string slice: start 0,
1036
1381
  // end +Infinity (math.h is always included). Fresh copy, +1.
@@ -1692,6 +2037,17 @@ export function emitExpr(E, e) {
1692
2037
  : "scr_dyn_new_null()");
1693
2038
  }
1694
2039
  const v = E.emitExpr(e.value);
2040
+ if (e.liveRef) {
2041
+ if (v.type.kind === "union") {
2042
+ const adapter = liveDynUnionRefAdapter(E, v.type);
2043
+ return E.newTemp(e.type, `${adapter}(${v.name})`);
2044
+ }
2045
+ const adapter = liveDynRefAdapter(E, v.type);
2046
+ const rc = vAdapters(v.type);
2047
+ const key = typeKey(v.type);
2048
+ const keyLit = cStringLiteral(Buffer.from(key, "utf8"));
2049
+ return E.newTemp(e.type, `scr_dyn_new_typed_ref(${v.name}, &${rc.retain}, &${rc.release}, ${keyLit}, ${Buffer.byteLength(key, "utf8")}, &${adapter.snapshot}, ${adapter.commit})`);
2050
+ }
1695
2051
  if (v.type.kind === "func") {
1696
2052
  // A closure boxes as the checked-dynamic tree's function kind: retained closure +
1697
2053
  // the per-signature call thunk + the interned signature key. The
@@ -2039,12 +2395,9 @@ export function emitExpr(E, e) {
2039
2395
  // engine's own typeof.
2040
2396
  `(${d.name}->kind == SCR_DYN_OBJ || ${d.name}->kind == SCR_DYN_ARR || ${d.name}->kind == SCR_DYN_BYTES || ${d.name}->kind == SCR_DYN_HANDLE || ${d.name}->kind == SCR_DYN_PROMISE || ${d.name}->kind == SCR_DYN_NULL || scr_dyn_isl_typeof_is(${d.name}, "object"))`
2041
2397
  : e.test === "truthy"
2042
- ? // ToBoolean over the checked-dynamic tree: bool by value; number falsy for
2043
- // 0, -0 (== 0 in C), and NaN (self-inequality); string
2044
- // falsy when empty; obj/arr/bytes/func/handle always true;
2045
- // JSVAL through the runtime's routed arm (the bigint 0n
2046
- // edge); the remaining kinds (undefined, null) always false.
2047
- `(${d.name}->kind == SCR_DYN_BOOL ? ${d.name}->v.b : ${d.name}->kind == SCR_DYN_NUM ? (${d.name}->v.num == ${d.name}->v.num && ${d.name}->v.num != 0) : ${d.name}->kind == SCR_DYN_STR ? ${d.name}->v.str->len != 0 : ${d.name}->kind == SCR_DYN_JSVAL ? scr_dyn_truthy(${d.name}) : (${d.name}->kind == SCR_DYN_OBJ || ${d.name}->kind == SCR_DYN_ARR || ${d.name}->kind == SCR_DYN_BYTES || ${d.name}->kind == SCR_DYN_FUNC || ${d.name}->kind == SCR_DYN_HANDLE || ${d.name}->kind == SCR_DYN_PROMISE))`
2398
+ ? // Runtime ToBoolean includes typed-reference capsules and
2399
+ // keeps this backend in lockstep with LLVM.
2400
+ `scr_dyn_truthy(${d.name})`
2048
2401
  : e.test === "error"
2049
2402
  ? // `u instanceof Error`: the checked-dynamic tree's error encoding — an
2050
2403
  // object carrying the reserved "%error" marker key
@@ -2212,6 +2565,7 @@ export function emitExpr(E, e) {
2212
2565
  const t = e.args[0].type;
2213
2566
  switch (t.kind) {
2214
2567
  case "f64":
2568
+ case "date":
2215
2569
  E.line(`scr_promise_fulfill_f64(${p.name}, ${v.name});${E.srcComment(e.loc)}`);
2216
2570
  break;
2217
2571
  case "bool":
@@ -2294,6 +2648,62 @@ export function emitExpr(E, e) {
2294
2648
  };
2295
2649
  const fn = e.fn;
2296
2650
  switch (fn) {
2651
+ case "fetch.start":
2652
+ E.usesTimers = true;
2653
+ return finish(`scr_fetch_static(${arg(0)}, ${arg(1)})`);
2654
+ case "fetch.responseNew":
2655
+ return finish(`scr_fetch_response_new(${arg(0)}, ${arg(1)})`);
2656
+ case "fetch.responseJson":
2657
+ return finish(`scr_fetch_response_json(${arg(0)})`);
2658
+ case "fetch.responseText":
2659
+ case "fetch.responseBytes": {
2660
+ if (e.type.kind !== "promise") {
2661
+ throw new Error(`emitter bug: ${fn} result`);
2662
+ }
2663
+ const runtimeFn = fn === "fetch.responseText"
2664
+ ? "scr_fetch_response_text"
2665
+ : "scr_fetch_response_bytes";
2666
+ const source = E.newTemp({ kind: "promise", inner: DYN }, `${runtimeFn}(${arg(0)})`);
2667
+ const result = E.newTemp(e.type, `scr_promise_new()`);
2668
+ const adapter = dynPromiseAdapter(E, e.type.inner);
2669
+ E.line(`scr_promise_race_add(${result.name}, ${source.name}, &${adapter});${E.srcComment(e.loc)}`);
2670
+ return result;
2671
+ }
2672
+ case "fetch.abortControllerNew":
2673
+ return finish(`scr_fetch_abort_controller_new()`);
2674
+ case "fetch.abortTimeout":
2675
+ E.usesTimers = true;
2676
+ return finish(`scr_fetch_abort_timeout(${arg(0)})`);
2677
+ case "fetch.abortNow":
2678
+ return finish(`scr_fetch_abort_now(${arg(0)})`);
2679
+ case "fetch.abortAny":
2680
+ return finish(`scr_fetch_abort_any(${arg(0)})`);
2681
+ case "fetch.streamNew":
2682
+ E.usesTimers = true;
2683
+ return finish(`scr_fetch_stream_new(${arg(0)})`);
2684
+ case "fetch.streamFrom":
2685
+ E.usesTimers = true;
2686
+ if (e.args[0].type.kind === "array") {
2687
+ const adapter = streamFromArrayAdapter(E, e.args[0].type);
2688
+ return finish(`scr_fetch_stream_from_array(${arg(0)}, &${adapter})`);
2689
+ }
2690
+ if (e.args[0].type.kind === "bytes") {
2691
+ return finish(`scr_fetch_stream_from_bytes(${arg(0)})`);
2692
+ }
2693
+ if (e.args[0].type.kind === "string") {
2694
+ return finish(`scr_fetch_stream_from_string(${arg(0)})`);
2695
+ }
2696
+ return finish(`scr_fetch_stream_from(${arg(0)})`);
2697
+ case "fetch.readerRead": {
2698
+ if (e.type.kind !== "promise" || e.type.inner.kind !== "record") {
2699
+ throw new Error("emitter bug: fetch.readerRead result");
2700
+ }
2701
+ const source = E.newTemp({ kind: "promise", inner: DYN }, `scr_fetch_reader_read(${arg(0)})`);
2702
+ const result = E.newTemp(e.type, `scr_promise_new()`);
2703
+ const adapter = dynPromiseAdapter(E, e.type.inner);
2704
+ E.line(`scr_promise_race_add(${result.name}, ${source.name}, &${adapter});${E.srcComment(e.loc)}`);
2705
+ return result;
2706
+ }
2297
2707
  case "island.eval":
2298
2708
  // --dynamic builds only (the frontend fences the intrinsic, so
2299
2709
  // scr_island_eval is always linked when this emits). Borrows
@@ -2821,7 +3231,7 @@ export function emitExpr(E, e) {
2821
3231
  // comes back as f64.
2822
3232
  return finish(`scr_fs_open(${arg(0)}, ${arg(1)})`);
2823
3233
  case "fs.readSync":
2824
- return finish(`scr_fs_read_sync(${arg(0)}, ${arg(1)}, ${arg(2)}, ${arg(3)})`);
3234
+ return finish(`scr_fs_read_sync(${arg(0)}, ${arg(1)}, ${arg(2)}, ${arg(3)}, ${arg(4)})`);
2825
3235
  case "fs.closeSync":
2826
3236
  return finish(`scr_fs_close(${arg(0)})`);
2827
3237
  case "fs.watch":
@@ -5789,7 +6199,17 @@ export function emitExpr(E, e) {
5789
6199
  case "date.now":
5790
6200
  // Node's integer milliseconds since epoch. Never throws.
5791
6201
  return finish(`scr_date_now()`);
6202
+ case "date.newNow":
6203
+ return finish(`scr_date_now()`);
6204
+ case "date.newMs":
6205
+ return finish(`scr_date_new_ms(${arg(0)})`);
6206
+ case "date.newString":
6207
+ return finish(`scr_date_parse_get_time(${arg(0)})`);
6208
+ case "date.getTime":
6209
+ case "date.valueOf":
6210
+ return finish(`${arg(0)}`);
5792
6211
  case "date.toISOString":
6212
+ case "date.toISOStringValue":
5793
6213
  // +1 string, or Node's "Invalid time value" RangeError
5794
6214
  // (may-throw seed set).
5795
6215
  return finish(`scr_date_to_iso(${arg(0)})`);
@@ -5801,6 +6221,39 @@ export function emitExpr(E, e) {
5801
6221
  // MakeDay/MakeTime/TimeClip over seven completed number
5802
6222
  // arguments; NaN outside the time range. Never throws.
5803
6223
  return finish(`scr_date_utc(${arg(0)}, ${arg(1)}, ${arg(2)}, ${arg(3)}, ${arg(4)}, ${arg(5)}, ${arg(6)})`);
6224
+ case "date.getFullYear":
6225
+ return finish(`scr_date_get_full_year(${arg(0)}, false)`);
6226
+ case "date.getUTCFullYear":
6227
+ return finish(`scr_date_get_full_year(${arg(0)}, true)`);
6228
+ case "date.getMonth":
6229
+ return finish(`scr_date_get_month(${arg(0)}, false)`);
6230
+ case "date.getUTCMonth":
6231
+ return finish(`scr_date_get_month(${arg(0)}, true)`);
6232
+ case "date.getDate":
6233
+ return finish(`scr_date_get_date(${arg(0)}, false)`);
6234
+ case "date.getUTCDate":
6235
+ return finish(`scr_date_get_date(${arg(0)}, true)`);
6236
+ case "date.getDay":
6237
+ return finish(`scr_date_get_day(${arg(0)}, false)`);
6238
+ case "date.getUTCDay":
6239
+ return finish(`scr_date_get_day(${arg(0)}, true)`);
6240
+ case "date.getHours":
6241
+ return finish(`scr_date_get_hours(${arg(0)}, false)`);
6242
+ case "date.getUTCHours":
6243
+ return finish(`scr_date_get_hours(${arg(0)}, true)`);
6244
+ case "date.getMinutes":
6245
+ return finish(`scr_date_get_minutes(${arg(0)}, false)`);
6246
+ case "date.getUTCMinutes":
6247
+ return finish(`scr_date_get_minutes(${arg(0)}, true)`);
6248
+ case "date.getSeconds":
6249
+ return finish(`scr_date_get_seconds(${arg(0)}, false)`);
6250
+ case "date.getUTCSeconds":
6251
+ return finish(`scr_date_get_seconds(${arg(0)}, true)`);
6252
+ case "date.getMilliseconds":
6253
+ case "date.getUTCMilliseconds":
6254
+ return finish(`scr_date_get_milliseconds(${arg(0)})`);
6255
+ case "date.getTimezoneOffset":
6256
+ return finish(`scr_date_get_timezone_offset(${arg(0)})`);
5804
6257
  case "text.decode":
5805
6258
  // WHATWG utf-8 decode with the leading BOM stripped
5806
6259
  // (scr_bytes.c). Borrowed bytes; +1 string; never throws.
@@ -5959,7 +6412,7 @@ export function emitExpr(E, e) {
5959
6412
  throw new Error("emitter bug: yieldExpr with no operand (frontend fills undefined)");
5960
6413
  const v = E.emitExpr(e.value);
5961
6414
  const yt = e.value.type;
5962
- if (yt.kind === "f64") {
6415
+ if (yt.kind === "f64" || yt.kind === "date") {
5963
6416
  E.line(`scr_gen_yield_f64(${v.name});${E.srcComment(e.loc)}`);
5964
6417
  }
5965
6418
  else if (yt.kind === "bool") {
@@ -5976,6 +6429,7 @@ export function emitExpr(E, e) {
5976
6429
  // fences value-position yields on this channel).
5977
6430
  return { name: "", type: e.type };
5978
6431
  case "f64":
6432
+ case "date":
5979
6433
  return E.newTemp(e.type, `scr_gen_take_in_f64()`);
5980
6434
  case "bool":
5981
6435
  return E.newTemp(e.type, `scr_gen_take_in_bool()`);
@@ -6013,7 +6467,7 @@ export function emitExpr(E, e) {
6013
6467
  }
6014
6468
  else {
6015
6469
  const nt = e.arg.type;
6016
- sendArg((a) => nt.kind === "f64" ? `scr_gen_in_f64(${g.name}, ${a.name});${E.srcComment(e.loc)}`
6470
+ sendArg((a) => nt.kind === "f64" || nt.kind === "date" ? `scr_gen_in_f64(${g.name}, ${a.name});${E.srcComment(e.loc)}`
6017
6471
  : nt.kind === "bool" ? `scr_gen_in_bool(${g.name}, ${a.name});${E.srcComment(e.loc)}`
6018
6472
  : `scr_gen_in_ref(${g.name}, ${a.name}, ${vAdapters(nt).release});${E.srcComment(e.loc)}`);
6019
6473
  }
@@ -6025,7 +6479,7 @@ export function emitExpr(E, e) {
6025
6479
  }
6026
6480
  else {
6027
6481
  const rt = e.arg.type;
6028
- sendArg((a) => rt.kind === "f64" ? `scr_gen_ret_f64(${g.name}, ${a.name});${E.srcComment(e.loc)}`
6482
+ sendArg((a) => rt.kind === "f64" || rt.kind === "date" ? `scr_gen_ret_f64(${g.name}, ${a.name});${E.srcComment(e.loc)}`
6029
6483
  : rt.kind === "bool" ? `scr_gen_ret_bool(${g.name}, ${a.name});${E.srcComment(e.loc)}`
6030
6484
  : `scr_gen_ret_ref(${g.name}, ${a.name}, ${vAdapters(rt).release});${E.srcComment(e.loc)}`);
6031
6485
  }
@@ -6042,7 +6496,10 @@ export function emitExpr(E, e) {
6042
6496
  const t = e.arg.type;
6043
6497
  if (isRefCounted(t))
6044
6498
  E.moveTemp(a); // the cell takes ownership
6045
- if (t.kind === "f64") {
6499
+ if (t.kind === "date") {
6500
+ throw new Error("emitter bug: Date generator throw reached backend");
6501
+ }
6502
+ else if (t.kind === "f64") {
6046
6503
  E.line(`scr_throw_f64(${a.name});${E.srcComment(e.loc)}`);
6047
6504
  }
6048
6505
  else if (t.kind === "bool") {
@@ -6075,6 +6532,7 @@ export function emitExpr(E, e) {
6075
6532
  let read;
6076
6533
  switch (e.type.kind) {
6077
6534
  case "f64":
6535
+ case "date":
6078
6536
  read = `scr_await_f64(${pr.name})`;
6079
6537
  break;
6080
6538
  case "bool":
@@ -6197,6 +6655,7 @@ export function emitExpr(E, e) {
6197
6655
  let mk;
6198
6656
  switch (inner.kind) {
6199
6657
  case "f64":
6658
+ case "date":
6200
6659
  mk = `scr_make_resolve(${p.name}, 0)`;
6201
6660
  break;
6202
6661
  case "bool":
@@ -6251,6 +6710,7 @@ export function emitExpr(E, e) {
6251
6710
  let mk;
6252
6711
  switch (inner.kind) {
6253
6712
  case "f64":
6713
+ case "date":
6254
6714
  mk = `scr_make_resolve(${p.name}, 0)`;
6255
6715
  break;
6256
6716
  case "bool":
@@ -6421,6 +6881,10 @@ export function emitExpr(E, e) {
6421
6881
  const pack = argPack(args.slice(1).map((x) => x.name));
6422
6882
  return finishFallible(`scr_jsval_call_method(${a(0)}, ${nameSym()}, ${args.length - 1}, ${pack})`);
6423
6883
  }
6884
+ case "callFnThis": {
6885
+ const pack = argPack(args.slice(2).map((x) => x.name));
6886
+ return finishFallible(`scr_jsval_call_this(${a(0)}, ${a(1)}, ${args.length - 2}, ${pack})`);
6887
+ }
6424
6888
  case "callFn": {
6425
6889
  const pack = argPack(args.slice(1).map((x) => x.name));
6426
6890
  return finishFallible(`scr_jsval_call(${a(0)}, ${args.length - 1}, ${pack})`);