@scriptc/compiler 0.0.0 → 0.0.1
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/LICENSE +202 -0
- package/ambient/package.json +3 -0
- package/ambient/scriptc-node-fallback.d.ts +3001 -0
- package/ambient/scriptc-overrides.d.ts +95 -0
- package/ambient/scriptc.d.ts +74 -0
- package/dist/backend/cc.d.ts +169 -0
- package/dist/backend/cc.js +897 -0
- package/dist/backend/cc.js.map +1 -0
- package/dist/backend/emission/emit-async.d.ts +145 -0
- package/dist/backend/emission/emit-async.js +996 -0
- package/dist/backend/emission/emit-async.js.map +1 -0
- package/dist/backend/emission/emit-exprs.d.ts +3 -0
- package/dist/backend/emission/emit-exprs.js +5947 -0
- package/dist/backend/emission/emit-exprs.js.map +1 -0
- package/dist/backend/emission/emit-island.d.ts +45 -0
- package/dist/backend/emission/emit-island.js +271 -0
- package/dist/backend/emission/emit-island.js.map +1 -0
- package/dist/backend/emission/emit-shapes.d.ts +142 -0
- package/dist/backend/emission/emit-shapes.js +575 -0
- package/dist/backend/emission/emit-shapes.js.map +1 -0
- package/dist/backend/emission/emit-stmts.d.ts +78 -0
- package/dist/backend/emission/emit-stmts.js +960 -0
- package/dist/backend/emission/emit-stmts.js.map +1 -0
- package/dist/backend/emission/emit-types.d.ts +65 -0
- package/dist/backend/emission/emit-types.js +652 -0
- package/dist/backend/emission/emit-types.js.map +1 -0
- package/dist/backend/emission/emit-walkers.d.ts +154 -0
- package/dist/backend/emission/emit-walkers.js +1587 -0
- package/dist/backend/emission/emit-walkers.js.map +1 -0
- package/dist/backend/emission/emitter.d.ts +452 -0
- package/dist/backend/emission/emitter.js +1249 -0
- package/dist/backend/emission/emitter.js.map +1 -0
- package/dist/backend/emission/may-throw.d.ts +15 -0
- package/dist/backend/emission/may-throw.js +250 -0
- package/dist/backend/emission/may-throw.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +21 -0
- package/dist/backend/llvm/blocks.js +68 -0
- package/dist/backend/llvm/blocks.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +84 -0
- package/dist/backend/llvm/classes.js +438 -0
- package/dist/backend/llvm/classes.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +123 -0
- package/dist/backend/llvm/dyn.js +2624 -0
- package/dist/backend/llvm/dyn.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +10087 -0
- package/dist/backend/llvm/emitter.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +78 -0
- package/dist/backend/llvm/shapes.js +754 -0
- package/dist/backend/llvm/shapes.js.map +1 -0
- package/dist/backend/llvm/unsupported.d.ts +6 -0
- package/dist/backend/llvm/unsupported.js +12 -0
- package/dist/backend/llvm/unsupported.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +58 -0
- package/dist/backend/llvm/walkers.js +793 -0
- package/dist/backend/llvm/walkers.js.map +1 -0
- package/dist/backend/mangle.d.ts +124 -0
- package/dist/backend/mangle.js +232 -0
- package/dist/backend/mangle.js.map +1 -0
- package/dist/coverage/report.d.ts +64 -0
- package/dist/coverage/report.js +226 -0
- package/dist/coverage/report.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +140 -0
- package/dist/diagnostics/diagnostic.js +458 -0
- package/dist/diagnostics/diagnostic.js.map +1 -0
- package/dist/diagnostics/render.d.ts +11 -0
- package/dist/diagnostics/render.js +58 -0
- package/dist/diagnostics/render.js.map +1 -0
- package/dist/frontend/cjs-lexer.d.ts +20 -0
- package/dist/frontend/cjs-lexer.js +813 -0
- package/dist/frontend/cjs-lexer.js.map +1 -0
- package/dist/frontend/lowering/http2-constants.d.ts +1 -0
- package/dist/frontend/lowering/http2-constants.js +251 -0
- package/dist/frontend/lowering/http2-constants.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
- package/dist/frontend/lowering/lib-boundary.js +143 -0
- package/dist/frontend/lowering/lib-boundary.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.d.ts +18 -0
- package/dist/frontend/lowering/lower-assert.js +1269 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
- package/dist/frontend/lowering/lower-builtins.js +5249 -0
- package/dist/frontend/lowering/lower-builtins.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.d.ts +581 -0
- package/dist/frontend/lowering/lower-calls.js +6281 -0
- package/dist/frontend/lowering/lower-calls.js.map +1 -0
- package/dist/frontend/lowering/lower-classes.d.ts +727 -0
- package/dist/frontend/lowering/lower-classes.js +4701 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -0
- package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
- package/dist/frontend/lowering/lower-comptime.js +244 -0
- package/dist/frontend/lowering/lower-comptime.js.map +1 -0
- package/dist/frontend/lowering/lower-containers.d.ts +483 -0
- package/dist/frontend/lowering/lower-containers.js +6281 -0
- package/dist/frontend/lowering/lower-containers.js.map +1 -0
- package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
- package/dist/frontend/lowering/lower-dgram.js +319 -0
- package/dist/frontend/lowering/lower-dgram.js.map +1 -0
- package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
- package/dist/frontend/lowering/lower-emitter.js +688 -0
- package/dist/frontend/lowering/lower-emitter.js.map +1 -0
- package/dist/frontend/lowering/lower-enums.d.ts +22 -0
- package/dist/frontend/lowering/lower-enums.js +235 -0
- package/dist/frontend/lowering/lower-enums.js.map +1 -0
- package/dist/frontend/lowering/lower-expando.d.ts +35 -0
- package/dist/frontend/lowering/lower-expando.js +276 -0
- package/dist/frontend/lowering/lower-expando.js.map +1 -0
- package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
- package/dist/frontend/lowering/lower-exprs.js +8099 -0
- package/dist/frontend/lowering/lower-exprs.js.map +1 -0
- package/dist/frontend/lowering/lower-generators.d.ts +50 -0
- package/dist/frontend/lowering/lower-generators.js +421 -0
- package/dist/frontend/lowering/lower-generators.js.map +1 -0
- package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
- package/dist/frontend/lowering/lower-inspect.js +1069 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -0
- package/dist/frontend/lowering/lower-island.d.ts +108 -0
- package/dist/frontend/lowering/lower-island.js +650 -0
- package/dist/frontend/lowering/lower-island.js.map +1 -0
- package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
- package/dist/frontend/lowering/lower-mixins.js +534 -0
- package/dist/frontend/lowering/lower-mixins.js.map +1 -0
- package/dist/frontend/lowering/lower-modules.d.ts +135 -0
- package/dist/frontend/lowering/lower-modules.js +1613 -0
- package/dist/frontend/lowering/lower-modules.js.map +1 -0
- package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
- package/dist/frontend/lowering/lower-namespaces.js +706 -0
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
- package/dist/frontend/lowering/lower-server.d.ts +90 -0
- package/dist/frontend/lowering/lower-server.js +3471 -0
- package/dist/frontend/lowering/lower-server.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
- package/dist/frontend/lowering/lower-stmts.js +5341 -0
- package/dist/frontend/lowering/lower-stmts.js.map +1 -0
- package/dist/frontend/lowering/lower-stream.d.ts +88 -0
- package/dist/frontend/lowering/lower-stream.js +1533 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -0
- package/dist/frontend/lowering/lower-test.d.ts +27 -0
- package/dist/frontend/lowering/lower-test.js +353 -0
- package/dist/frontend/lowering/lower-test.js.map +1 -0
- package/dist/frontend/lowering/lowerer.d.ts +1748 -0
- package/dist/frontend/lowering/lowerer.js +6256 -0
- package/dist/frontend/lowering/lowerer.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +272 -0
- package/dist/frontend/lowering/surfaces.js +1096 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +55 -0
- package/dist/frontend/npm-static.js +300 -0
- package/dist/frontend/npm-static.js.map +1 -0
- package/dist/frontend/npm.d.ts +330 -0
- package/dist/frontend/npm.js +1259 -0
- package/dist/frontend/npm.js.map +1 -0
- package/dist/frontend/program.d.ts +186 -0
- package/dist/frontend/program.js +2172 -0
- package/dist/frontend/program.js.map +1 -0
- package/dist/frontend/provenance-registry.d.ts +48 -0
- package/dist/frontend/provenance-registry.js +87 -0
- package/dist/frontend/provenance-registry.js.map +1 -0
- package/dist/frontend/provenance.d.ts +6 -0
- package/dist/frontend/provenance.js +459 -0
- package/dist/frontend/provenance.js.map +1 -0
- package/dist/frontend/resolve.d.ts +56 -0
- package/dist/frontend/resolve.js +682 -0
- package/dist/frontend/resolve.js.map +1 -0
- package/dist/frontend/shared.d.ts +60 -0
- package/dist/frontend/shared.js +232 -0
- package/dist/frontend/shared.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +7 -0
- package/dist/frontend/ts7/adapter.js +54 -0
- package/dist/frontend/ts7/adapter.js.map +1 -0
- package/dist/frontend/ts7/ast.d.ts +50 -0
- package/dist/frontend/ts7/ast.js +211 -0
- package/dist/frontend/ts7/ast.js.map +1 -0
- package/dist/frontend/ts7/census-check.d.ts +1 -0
- package/dist/frontend/ts7/census-check.js +12 -0
- package/dist/frontend/ts7/census-check.js.map +1 -0
- package/dist/frontend/ts7/checker.d.ts +140 -0
- package/dist/frontend/ts7/checker.js +544 -0
- package/dist/frontend/ts7/checker.js.map +1 -0
- package/dist/frontend/ts7/enums.d.ts +18 -0
- package/dist/frontend/ts7/enums.js +43 -0
- package/dist/frontend/ts7/enums.js.map +1 -0
- package/dist/frontend/ts7/program.d.ts +99 -0
- package/dist/frontend/ts7/program.js +278 -0
- package/dist/frontend/ts7/program.js.map +1 -0
- package/dist/frontend/ts7/world-check.d.ts +3 -0
- package/dist/frontend/ts7/world-check.js +34 -0
- package/dist/frontend/ts7/world-check.js.map +1 -0
- package/dist/frontend/types.d.ts +205 -0
- package/dist/frontend/types.js +2485 -0
- package/dist/frontend/types.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/nodes.d.ts +4334 -0
- package/dist/ir/nodes.js +1906 -0
- package/dist/ir/nodes.js.map +1 -0
- package/dist/ir/serialize.d.ts +4 -0
- package/dist/ir/serialize.js +34 -0
- package/dist/ir/serialize.js.map +1 -0
- package/dist/ir/validate.d.ts +36 -0
- package/dist/ir/validate.js +4884 -0
- package/dist/ir/validate.js.map +1 -0
- package/package.json +30 -6
- package/README.md +0 -3
- package/index.js +0 -2
|
@@ -0,0 +1,2624 @@
|
|
|
1
|
+
import { DYN_HANDLE_KINDS, isRefCounted, typeKey } from "../../ir/nodes.js";
|
|
2
|
+
import { mangleRecordNew, mangleRecordStruct } from "../mangle.js";
|
|
3
|
+
import { BlockBuilder } from "./blocks.js";
|
|
4
|
+
import { arrNewCall, elemAccess, llFieldType, releaseSym, traceArg, vAdapters } from "./shapes.js";
|
|
5
|
+
import { LlvmUnsupportedError } from "./unsupported.js";
|
|
6
|
+
/** ScrDynKind values (scr_runtime.h). */
|
|
7
|
+
/** ScrDynHandleTag numeric values (scr_runtime.h's enum order). */
|
|
8
|
+
export const DYN_HANDLE_TAG_NUM = {
|
|
9
|
+
httpReq: 0,
|
|
10
|
+
httpRes: 1,
|
|
11
|
+
netSocket: 2,
|
|
12
|
+
netServer: 3,
|
|
13
|
+
};
|
|
14
|
+
export const DK = {
|
|
15
|
+
NULL: 0,
|
|
16
|
+
BOOL: 1,
|
|
17
|
+
NUM: 2,
|
|
18
|
+
STR: 3,
|
|
19
|
+
ARR: 4,
|
|
20
|
+
OBJ: 5,
|
|
21
|
+
UNDEF: 6,
|
|
22
|
+
BYTES: 7,
|
|
23
|
+
FUNC: 8,
|
|
24
|
+
HANDLE: 9,
|
|
25
|
+
PROMISE: 10,
|
|
26
|
+
};
|
|
27
|
+
/** Exact double literal (the emitter's f64Lit — the walkers' copy). */
|
|
28
|
+
function f64Lit(n) {
|
|
29
|
+
const buf = new ArrayBuffer(8);
|
|
30
|
+
new DataView(buf).setFloat64(0, n);
|
|
31
|
+
return `0x${[...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase()}`;
|
|
32
|
+
}
|
|
33
|
+
const FN_ATTRS = "#0";
|
|
34
|
+
/** i64 spelling of (SIZE_MAX - 9) / 10 — the canonical-index overflow
|
|
35
|
+
* guard the C helpers compare against. */
|
|
36
|
+
const IDX_MAX_DIV10 = "1844674407370955160";
|
|
37
|
+
export class LlDyn {
|
|
38
|
+
host;
|
|
39
|
+
dynMatchers = new Map();
|
|
40
|
+
dynBuilders = new Map();
|
|
41
|
+
toDynFns = new Map();
|
|
42
|
+
dynFuncThunks = new Map();
|
|
43
|
+
dynFuncBoxes = new Map();
|
|
44
|
+
dynFuncAdapters = new Map();
|
|
45
|
+
promiseDynAdapters = new Map();
|
|
46
|
+
dynToStrFn = null;
|
|
47
|
+
caughtToDynFn = null;
|
|
48
|
+
/** Emitted function definitions, in interning order. */
|
|
49
|
+
defs = [];
|
|
50
|
+
constructor(host) {
|
|
51
|
+
this.host = host;
|
|
52
|
+
}
|
|
53
|
+
/** The value LLVM type of a dynCheck result / toDyn operand for `t`. */
|
|
54
|
+
valTy(t) {
|
|
55
|
+
return t.kind === "f64" ? "double" : t.kind === "bool" ? "i1" : "ptr";
|
|
56
|
+
}
|
|
57
|
+
/* ── DOM node plumbing ─────────────────────────────────────────────── */
|
|
58
|
+
/** Loads a DOM node's kind tag (i32 at +8). */
|
|
59
|
+
kindOf(B, d) {
|
|
60
|
+
const p = B.tmp();
|
|
61
|
+
const k = B.tmp();
|
|
62
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 8 ; ->kind`);
|
|
63
|
+
B.line(`${k} = load i32, ptr ${p}`);
|
|
64
|
+
return k;
|
|
65
|
+
}
|
|
66
|
+
/** Loads the 8-byte payload slot at +16 as the given LLVM type. */
|
|
67
|
+
payloadOf(B, d, ty) {
|
|
68
|
+
const p = B.tmp();
|
|
69
|
+
const v = B.tmp();
|
|
70
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 16 ; ->v`);
|
|
71
|
+
B.line(`${v} = load ${ty}, ptr ${p}`);
|
|
72
|
+
return v;
|
|
73
|
+
}
|
|
74
|
+
/** Loads v.b (i8 at +16) as i1. */
|
|
75
|
+
boolOf(B, d) {
|
|
76
|
+
const p = B.tmp();
|
|
77
|
+
const raw = B.tmp();
|
|
78
|
+
const b = B.tmp();
|
|
79
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 16 ; ->v.b`);
|
|
80
|
+
B.line(`${raw} = load i8, ptr ${p}`);
|
|
81
|
+
B.line(`${b} = trunc i8 ${raw} to i1`);
|
|
82
|
+
return b;
|
|
83
|
+
}
|
|
84
|
+
/** Loads v.arr/v.obj length (i64 at +16). */
|
|
85
|
+
lenOf(B, d) {
|
|
86
|
+
const p = B.tmp();
|
|
87
|
+
const n = B.tmp();
|
|
88
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 16 ; ->v.arr.len`);
|
|
89
|
+
B.line(`${n} = load i64, ptr ${p}`);
|
|
90
|
+
return n;
|
|
91
|
+
}
|
|
92
|
+
/** Loads v.arr.items / v.obj.entries (ptr at +32). */
|
|
93
|
+
itemsOf(B, d) {
|
|
94
|
+
const p = B.tmp();
|
|
95
|
+
const it = B.tmp();
|
|
96
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 32 ; ->v.arr.items`);
|
|
97
|
+
B.line(`${it} = load ptr, ptr ${p}`);
|
|
98
|
+
return it;
|
|
99
|
+
}
|
|
100
|
+
/** Loads items[i] (borrowed ScrDyn *). */
|
|
101
|
+
itemAt(B, items, i) {
|
|
102
|
+
const p = B.tmp();
|
|
103
|
+
const e = B.tmp();
|
|
104
|
+
B.line(`${p} = getelementptr inbounds ptr, ptr ${items}, i64 ${i}`);
|
|
105
|
+
B.line(`${e} = load ptr, ptr ${p}`);
|
|
106
|
+
return e;
|
|
107
|
+
}
|
|
108
|
+
/** Object entry field addresses: entries + i*24 (+0 key, +8 key_len,
|
|
109
|
+
* +16 value). */
|
|
110
|
+
entryAt(B, entries, i) {
|
|
111
|
+
const off = B.tmp();
|
|
112
|
+
const base = B.tmp();
|
|
113
|
+
B.line(`${off} = mul i64 ${i}, 24 ; sizeof(ScrDynEntry)`);
|
|
114
|
+
B.line(`${base} = getelementptr inbounds i8, ptr ${entries}, i64 ${off}`);
|
|
115
|
+
const key = B.tmp();
|
|
116
|
+
B.line(`${key} = load ptr, ptr ${base}`);
|
|
117
|
+
const klp = B.tmp();
|
|
118
|
+
const keyLen = B.tmp();
|
|
119
|
+
B.line(`${klp} = getelementptr inbounds i8, ptr ${base}, i64 8`);
|
|
120
|
+
B.line(`${keyLen} = load i64, ptr ${klp}`);
|
|
121
|
+
const vp = B.tmp();
|
|
122
|
+
const value = B.tmp();
|
|
123
|
+
B.line(`${vp} = getelementptr inbounds i8, ptr ${base}, i64 16`);
|
|
124
|
+
B.line(`${value} = load ptr, ptr ${vp}`);
|
|
125
|
+
return { key, keyLen, value };
|
|
126
|
+
}
|
|
127
|
+
/** An ScrStr's (len, data) pair — len via the %ScrStr header, data the
|
|
128
|
+
* flexible tail at +24. */
|
|
129
|
+
strParts(B, s) {
|
|
130
|
+
const lp = B.tmp();
|
|
131
|
+
const len = B.tmp();
|
|
132
|
+
const data = B.tmp();
|
|
133
|
+
B.line(`${lp} = getelementptr inbounds %ScrStr, ptr ${s}, i64 0, i32 1`);
|
|
134
|
+
B.line(`${len} = load i64, ptr ${lp}`);
|
|
135
|
+
B.line(`${data} = getelementptr inbounds i8, ptr ${s}, i64 24 ; ->data`);
|
|
136
|
+
return { len, data };
|
|
137
|
+
}
|
|
138
|
+
/** `call ptr @scr_dyn_retain_v(x)` (+1; immortals skip). */
|
|
139
|
+
retainDyn(B, x) {
|
|
140
|
+
this.host.declare(`declare ptr @scr_dyn_retain_v(ptr)`);
|
|
141
|
+
const t = B.tmp();
|
|
142
|
+
B.line(`${t} = call ptr @scr_dyn_retain_v(ptr ${x})`);
|
|
143
|
+
return t;
|
|
144
|
+
}
|
|
145
|
+
/** THE immortal undefined DOM value (borrowed — retain to own). */
|
|
146
|
+
undef(B) {
|
|
147
|
+
this.host.declare(`declare ptr @scr_dyn_undefined()`);
|
|
148
|
+
const t = B.tmp();
|
|
149
|
+
B.line(`${t} = call ptr @scr_dyn_undefined()`);
|
|
150
|
+
return t;
|
|
151
|
+
}
|
|
152
|
+
/** scr_dyn_obj_get with a compile-time key: borrowed member or null. */
|
|
153
|
+
objGetLit(B, d, key) {
|
|
154
|
+
this.host.declare(`declare ptr @scr_dyn_obj_get(ptr, ptr, i64)`);
|
|
155
|
+
const t = B.tmp();
|
|
156
|
+
const len = Buffer.byteLength(key, "utf8");
|
|
157
|
+
B.line(`${t} = call ptr @scr_dyn_obj_get(ptr ${d}, ptr ${this.host.cstr(key)}, i64 ${len}) ; .${key}`);
|
|
158
|
+
return t;
|
|
159
|
+
}
|
|
160
|
+
/** Appends an ScrStr's bytes into a ScrJsonBuf (walkers' putScrStr). */
|
|
161
|
+
putScrStr(B, buf, s) {
|
|
162
|
+
this.host.declare(`declare void @scr_jb_putc(ptr, i8)`);
|
|
163
|
+
const { len, data } = this.strParts(B, s);
|
|
164
|
+
const jSlot = B.slot();
|
|
165
|
+
B.entryAllocas.push(`${jSlot} = alloca i64`);
|
|
166
|
+
B.line(`store i64 0, ptr ${jSlot}`);
|
|
167
|
+
const lc = B.newLabel("dps.c");
|
|
168
|
+
const lb = B.newLabel("dps.b");
|
|
169
|
+
const le = B.newLabel("dps.e");
|
|
170
|
+
B.br(lc);
|
|
171
|
+
B.startBlock(lc);
|
|
172
|
+
const j = B.tmp();
|
|
173
|
+
const cont = B.tmp();
|
|
174
|
+
B.line(`${j} = load i64, ptr ${jSlot}`);
|
|
175
|
+
B.line(`${cont} = icmp ult i64 ${j}, ${len}`);
|
|
176
|
+
B.condBr(cont, lb, le);
|
|
177
|
+
B.startBlock(lb);
|
|
178
|
+
const cp = B.tmp();
|
|
179
|
+
const c = B.tmp();
|
|
180
|
+
B.line(`${cp} = getelementptr inbounds i8, ptr ${data}, i64 ${j}`);
|
|
181
|
+
B.line(`${c} = load i8, ptr ${cp}`);
|
|
182
|
+
B.line(`call void @scr_jb_putc(ptr ${buf}, i8 ${c})`);
|
|
183
|
+
const j2 = B.tmp();
|
|
184
|
+
B.line(`${j2} = add i64 ${j}, 1`);
|
|
185
|
+
B.line(`store i64 ${j2}, ptr ${jSlot}`);
|
|
186
|
+
B.br(lc);
|
|
187
|
+
B.startBlock(le);
|
|
188
|
+
}
|
|
189
|
+
puts(B, buf, text) {
|
|
190
|
+
this.host.declare(`declare void @scr_jb_puts(ptr, ptr)`);
|
|
191
|
+
B.line(`call void @scr_jb_puts(ptr ${buf}, ptr ${this.host.cstr(text)}) ; ${JSON.stringify(text)}`);
|
|
192
|
+
}
|
|
193
|
+
/** An i64 counting loop: emits header/body, calls `body(i)`, closes.
|
|
194
|
+
* The body must not terminate its final block. */
|
|
195
|
+
i64Loop(B, hint, limit, body) {
|
|
196
|
+
const iSlot = B.slot();
|
|
197
|
+
B.entryAllocas.push(`${iSlot} = alloca i64`);
|
|
198
|
+
B.line(`store i64 0, ptr ${iSlot}`);
|
|
199
|
+
const lc = B.newLabel(`${hint}.c`);
|
|
200
|
+
const lb = B.newLabel(`${hint}.b`);
|
|
201
|
+
const ln = B.newLabel(`${hint}.n`);
|
|
202
|
+
const le = B.newLabel(`${hint}.e`);
|
|
203
|
+
B.br(lc);
|
|
204
|
+
B.startBlock(lc);
|
|
205
|
+
const i = B.tmp();
|
|
206
|
+
const cont = B.tmp();
|
|
207
|
+
B.line(`${i} = load i64, ptr ${iSlot}`);
|
|
208
|
+
B.line(`${cont} = icmp ult i64 ${i}, ${limit}`);
|
|
209
|
+
B.condBr(cont, lb, le);
|
|
210
|
+
B.startBlock(lb);
|
|
211
|
+
body(i, () => B.br(ln), ln);
|
|
212
|
+
B.br(ln);
|
|
213
|
+
B.startBlock(ln);
|
|
214
|
+
const i2 = B.tmp();
|
|
215
|
+
B.line(`${i2} = add i64 ${i}, 1`);
|
|
216
|
+
B.line(`store i64 ${i2}, ptr ${iSlot}`);
|
|
217
|
+
B.br(lc);
|
|
218
|
+
B.startBlock(le);
|
|
219
|
+
}
|
|
220
|
+
/** Emit the standard in-helper pending check: on a pending exception
|
|
221
|
+
* run `cleanup` and return `dummy`. */
|
|
222
|
+
pendingBail(B, hint, cleanup, dummy) {
|
|
223
|
+
this.host.declare(`declare zeroext i1 @scr_exc_pending()`);
|
|
224
|
+
const p = B.tmp();
|
|
225
|
+
B.line(`${p} = call zeroext i1 @scr_exc_pending()`);
|
|
226
|
+
const lu = B.newLabel(`${hint}.u`);
|
|
227
|
+
const lk = B.newLabel(`${hint}.k`);
|
|
228
|
+
B.condBr(p, lu, lk);
|
|
229
|
+
B.startBlock(lu);
|
|
230
|
+
cleanup();
|
|
231
|
+
B.terminate(`ret ${dummy}`);
|
|
232
|
+
B.startBlock(lk);
|
|
233
|
+
}
|
|
234
|
+
/* ── dynDesc (ported verbatim — pure strings) ──────────────────────── */
|
|
235
|
+
/** Short human description of a dynCheck target for error messages. */
|
|
236
|
+
dynDesc(t) {
|
|
237
|
+
switch (t.kind) {
|
|
238
|
+
case "f64":
|
|
239
|
+
return "number";
|
|
240
|
+
case "string":
|
|
241
|
+
return "string";
|
|
242
|
+
case "bool":
|
|
243
|
+
return "boolean";
|
|
244
|
+
case "record":
|
|
245
|
+
return this.host.recordsById.get(t.shapeId)?.tuple ? "array" : "object";
|
|
246
|
+
case "array":
|
|
247
|
+
return "array";
|
|
248
|
+
case "nullT":
|
|
249
|
+
return "null";
|
|
250
|
+
case "undefinedT":
|
|
251
|
+
return "undefined";
|
|
252
|
+
case "dyn":
|
|
253
|
+
return "unknown";
|
|
254
|
+
case "bytes":
|
|
255
|
+
return "Uint8Array";
|
|
256
|
+
case "object":
|
|
257
|
+
return t.className.replace(/^%/, "");
|
|
258
|
+
case "union": {
|
|
259
|
+
const def = this.host.unionsById.get(t.unionId);
|
|
260
|
+
if (!def)
|
|
261
|
+
throw new Error(`llvm emitter bug: dynDesc of unknown union ${t.unionId}`);
|
|
262
|
+
return def.arms.map((a) => this.dynDesc(a)).join(" | ");
|
|
263
|
+
}
|
|
264
|
+
case "func":
|
|
265
|
+
return "function";
|
|
266
|
+
default: {
|
|
267
|
+
const h = DYN_HANDLE_KINDS.get(t.kind);
|
|
268
|
+
if (h)
|
|
269
|
+
return h.cls;
|
|
270
|
+
throw new Error(`llvm emitter bug: dynDesc of non-JSON type ${t.kind}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/* ── dynMatchHelper (emit-walkers.ts, ported) ──────────────────────── */
|
|
275
|
+
/** `sc_dm_<n>(ptr d) -> i1` — does this DOM fit T? Never throws. */
|
|
276
|
+
dynMatchHelper(t) {
|
|
277
|
+
const key = typeKey(t);
|
|
278
|
+
const existing = this.dynMatchers.get(key);
|
|
279
|
+
if (existing)
|
|
280
|
+
return existing;
|
|
281
|
+
const name = `sc_dm_${this.dynMatchers.size}`;
|
|
282
|
+
this.dynMatchers.set(key, name);
|
|
283
|
+
const B = new BlockBuilder();
|
|
284
|
+
const kindIs = (k) => {
|
|
285
|
+
const kd = this.kindOf(B, "%d");
|
|
286
|
+
const r = B.tmp();
|
|
287
|
+
B.line(`${r} = icmp eq i32 ${kd}, ${k}`);
|
|
288
|
+
B.terminate(`ret i1 ${r}`);
|
|
289
|
+
};
|
|
290
|
+
switch (t.kind) {
|
|
291
|
+
case "f64":
|
|
292
|
+
kindIs(DK.NUM);
|
|
293
|
+
break;
|
|
294
|
+
case "string":
|
|
295
|
+
kindIs(DK.STR);
|
|
296
|
+
break;
|
|
297
|
+
case "bool":
|
|
298
|
+
kindIs(DK.BOOL);
|
|
299
|
+
break;
|
|
300
|
+
case "nullT":
|
|
301
|
+
kindIs(DK.NULL);
|
|
302
|
+
break;
|
|
303
|
+
case "undefinedT":
|
|
304
|
+
kindIs(DK.UNDEF);
|
|
305
|
+
break;
|
|
306
|
+
case "dyn":
|
|
307
|
+
// An `unknown` target: every DOM value fits, undefined included.
|
|
308
|
+
B.terminate(`ret i1 true`);
|
|
309
|
+
break;
|
|
310
|
+
case "bytes":
|
|
311
|
+
if (t.elem !== "u8")
|
|
312
|
+
throw new Error(`llvm emitter bug: dynMatch of bytes<${t.elem}>`);
|
|
313
|
+
kindIs(DK.BYTES);
|
|
314
|
+
break;
|
|
315
|
+
case "record": {
|
|
316
|
+
const shape = this.host.recordsById.get(t.shapeId);
|
|
317
|
+
if (!shape)
|
|
318
|
+
throw new Error(`llvm emitter bug: dynCheck of unknown shape ${t.shapeId}`);
|
|
319
|
+
const fail = B.newLabel("dm.f");
|
|
320
|
+
const kd = this.kindOf(B, "%d");
|
|
321
|
+
// A tuple matches a JSON ARRAY of EXACTLY its arity, positionally.
|
|
322
|
+
if (shape.tuple) {
|
|
323
|
+
const byIndex = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
|
|
324
|
+
const isArr = B.tmp();
|
|
325
|
+
B.line(`${isArr} = icmp eq i32 ${kd}, ${DK.ARR}`);
|
|
326
|
+
const l1 = B.newLabel("dm.a");
|
|
327
|
+
B.condBr(isArr, l1, fail);
|
|
328
|
+
B.startBlock(l1);
|
|
329
|
+
const len = this.lenOf(B, "%d");
|
|
330
|
+
const lenOk = B.tmp();
|
|
331
|
+
B.line(`${lenOk} = icmp eq i64 ${len}, ${byIndex.length}`);
|
|
332
|
+
const l2 = B.newLabel("dm.l");
|
|
333
|
+
B.condBr(lenOk, l2, fail);
|
|
334
|
+
B.startBlock(l2);
|
|
335
|
+
const items = this.itemsOf(B, "%d");
|
|
336
|
+
for (const [i, f] of byIndex.entries()) {
|
|
337
|
+
const e = this.itemAt(B, items, `${i}`);
|
|
338
|
+
const m = B.tmp();
|
|
339
|
+
B.line(`${m} = call zeroext i1 @${this.dynMatchHelper(f.type)}(ptr ${e})`);
|
|
340
|
+
const ln = B.newLabel("dm.i");
|
|
341
|
+
B.condBr(m, ln, fail);
|
|
342
|
+
B.startBlock(ln);
|
|
343
|
+
}
|
|
344
|
+
B.terminate(`ret i1 true`);
|
|
345
|
+
B.startBlock(fail);
|
|
346
|
+
B.terminate(`ret i1 false`);
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
const isObj = B.tmp();
|
|
350
|
+
B.line(`${isObj} = icmp eq i32 ${kd}, ${DK.OBJ}`);
|
|
351
|
+
const l0 = B.newLabel("dm.o");
|
|
352
|
+
B.condBr(isObj, l0, fail);
|
|
353
|
+
B.startBlock(l0);
|
|
354
|
+
// dyn ('unknown') fields match ANY value, present or missing.
|
|
355
|
+
for (const f of shape.fields) {
|
|
356
|
+
if (f.type.kind === "dyn")
|
|
357
|
+
continue;
|
|
358
|
+
const m = this.objGetLit(B, "%d", f.name);
|
|
359
|
+
const has = B.tmp();
|
|
360
|
+
B.line(`${has} = icmp ne ptr ${m}, null`);
|
|
361
|
+
const lTest = B.newLabel("dm.t");
|
|
362
|
+
const lNext = B.newLabel("dm.n");
|
|
363
|
+
if (this.host.undefinedArmTag(f.type) >= 0) {
|
|
364
|
+
// Optional-flavored field: a MISSING key is the undefined arm
|
|
365
|
+
// (a match); a PRESENT key must fit the union as usual.
|
|
366
|
+
B.condBr(has, lTest, lNext);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
B.condBr(has, lTest, fail);
|
|
370
|
+
}
|
|
371
|
+
B.startBlock(lTest);
|
|
372
|
+
const ok = B.tmp();
|
|
373
|
+
B.line(`${ok} = call zeroext i1 @${this.dynMatchHelper(f.type)}(ptr ${m})`);
|
|
374
|
+
B.condBr(ok, lNext, fail);
|
|
375
|
+
B.startBlock(lNext);
|
|
376
|
+
}
|
|
377
|
+
// Index-signature shapes: UNDECLARED keys must fit the overflow
|
|
378
|
+
// value type. A dyn value type accepts anything.
|
|
379
|
+
if (shape.indexValue && shape.indexValue.kind !== "dyn") {
|
|
380
|
+
this.host.declare(`declare i32 @memcmp(ptr, ptr, i64)`);
|
|
381
|
+
const n = this.lenOf(B, "%d");
|
|
382
|
+
const entries = this.itemsOf(B, "%d");
|
|
383
|
+
this.i64Loop(B, "dm.ov", n, (i, brNext) => {
|
|
384
|
+
const ent = this.entryAt(B, entries, i);
|
|
385
|
+
// Skip declared keys.
|
|
386
|
+
for (const f of shape.fields) {
|
|
387
|
+
const klen = Buffer.byteLength(f.name, "utf8");
|
|
388
|
+
const lenEq = B.tmp();
|
|
389
|
+
B.line(`${lenEq} = icmp eq i64 ${ent.keyLen}, ${klen}`);
|
|
390
|
+
const lCmp = B.newLabel("dm.kc");
|
|
391
|
+
const lNo = B.newLabel("dm.kn");
|
|
392
|
+
B.condBr(lenEq, lCmp, lNo);
|
|
393
|
+
B.startBlock(lCmp);
|
|
394
|
+
const c = B.tmp();
|
|
395
|
+
const same = B.tmp();
|
|
396
|
+
B.line(`${c} = call i32 @memcmp(ptr ${ent.key}, ptr ${this.host.cstr(f.name)}, i64 ${klen}) ; ${f.name}`);
|
|
397
|
+
B.line(`${same} = icmp eq i32 ${c}, 0`);
|
|
398
|
+
const lNo2 = B.newLabel("dm.kn");
|
|
399
|
+
const skip = B.newLabel("dm.ks");
|
|
400
|
+
B.condBr(same, skip, lNo2);
|
|
401
|
+
B.startBlock(skip);
|
|
402
|
+
brNext();
|
|
403
|
+
B.startBlock(lNo2);
|
|
404
|
+
B.br(lNo);
|
|
405
|
+
B.startBlock(lNo);
|
|
406
|
+
}
|
|
407
|
+
const ok = B.tmp();
|
|
408
|
+
B.line(`${ok} = call zeroext i1 @${this.dynMatchHelper(shape.indexValue)}(ptr ${ent.value})`);
|
|
409
|
+
const lOk = B.newLabel("dm.vo");
|
|
410
|
+
B.condBr(ok, lOk, fail);
|
|
411
|
+
B.startBlock(lOk);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
B.terminate(`ret i1 true`);
|
|
415
|
+
B.startBlock(fail);
|
|
416
|
+
B.terminate(`ret i1 false`);
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
case "array": {
|
|
420
|
+
const m = this.dynMatchHelper(t.elem);
|
|
421
|
+
const fail = B.newLabel("dm.f");
|
|
422
|
+
const kd = this.kindOf(B, "%d");
|
|
423
|
+
const isArr = B.tmp();
|
|
424
|
+
B.line(`${isArr} = icmp eq i32 ${kd}, ${DK.ARR}`);
|
|
425
|
+
const l0 = B.newLabel("dm.a");
|
|
426
|
+
B.condBr(isArr, l0, fail);
|
|
427
|
+
B.startBlock(l0);
|
|
428
|
+
const n = this.lenOf(B, "%d");
|
|
429
|
+
const items = this.itemsOf(B, "%d");
|
|
430
|
+
this.i64Loop(B, "dm.el", n, (i) => {
|
|
431
|
+
const e = this.itemAt(B, items, i);
|
|
432
|
+
const ok = B.tmp();
|
|
433
|
+
B.line(`${ok} = call zeroext i1 @${m}(ptr ${e})`);
|
|
434
|
+
const lOk = B.newLabel("dm.eo");
|
|
435
|
+
B.condBr(ok, lOk, fail);
|
|
436
|
+
B.startBlock(lOk);
|
|
437
|
+
});
|
|
438
|
+
B.terminate(`ret i1 true`);
|
|
439
|
+
B.startBlock(fail);
|
|
440
|
+
B.terminate(`ret i1 false`);
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
case "union": {
|
|
444
|
+
const def = this.host.unionsById.get(t.unionId);
|
|
445
|
+
if (!def)
|
|
446
|
+
throw new Error(`llvm emitter bug: dynCheck of unknown union ${t.unionId}`);
|
|
447
|
+
// Arms in canonical order; any full match answers true.
|
|
448
|
+
const yes = B.newLabel("dm.y");
|
|
449
|
+
for (const arm of def.arms) {
|
|
450
|
+
const ok = B.tmp();
|
|
451
|
+
B.line(`${ok} = call zeroext i1 @${this.dynMatchHelper(arm)}(ptr %d)`);
|
|
452
|
+
const ln = B.newLabel("dm.n");
|
|
453
|
+
B.condBr(ok, yes, ln);
|
|
454
|
+
B.startBlock(ln);
|
|
455
|
+
}
|
|
456
|
+
B.terminate(`ret i1 false`);
|
|
457
|
+
B.startBlock(yes);
|
|
458
|
+
B.terminate(`ret i1 true`);
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
default:
|
|
462
|
+
throw new LlvmUnsupportedError(`dynMatch:${t.kind}`);
|
|
463
|
+
}
|
|
464
|
+
this.defs.push(`define internal zeroext i1 @${name}(ptr %d) ${FN_ATTRS} { ; matches ${key}`, B.render(), `}`, ``);
|
|
465
|
+
return name;
|
|
466
|
+
}
|
|
467
|
+
/* ── dynCheckHelper (emit-walkers.ts, ported) ──────────────────────── */
|
|
468
|
+
/** `sc_dc_<n>(ptr d, ptr path) -> T` — validate the DOM against T and
|
|
469
|
+
* BUILD the typed value (+1), or throw the catchable path-annotated
|
|
470
|
+
* TypeError and return a dummy with the pending flag set. */
|
|
471
|
+
dynCheckHelper(t) {
|
|
472
|
+
const key = typeKey(t);
|
|
473
|
+
const existing = this.dynBuilders.get(key);
|
|
474
|
+
if (existing)
|
|
475
|
+
return existing;
|
|
476
|
+
const name = `sc_dc_${this.dynBuilders.size}`;
|
|
477
|
+
this.dynBuilders.set(key, name);
|
|
478
|
+
const host = this.host;
|
|
479
|
+
const retTy = this.valTy(t);
|
|
480
|
+
const dummy = retTy === "double" ? `double ${f64Lit(0)}` : retTy === "i1" ? "i1 false" : "ptr null";
|
|
481
|
+
host.declare(`declare void @scr_dyn_check_fail(ptr, ptr, ptr)`);
|
|
482
|
+
const want = host.cstr(this.dynDesc(t));
|
|
483
|
+
const B = new BlockBuilder();
|
|
484
|
+
/** kind test with the standard fail path (got = %d). */
|
|
485
|
+
const requireKind = (k, hint) => {
|
|
486
|
+
const kd = this.kindOf(B, "%d");
|
|
487
|
+
const ok = B.tmp();
|
|
488
|
+
B.line(`${ok} = icmp eq i32 ${kd}, ${k}`);
|
|
489
|
+
const lo = B.newLabel(`${hint}.k`);
|
|
490
|
+
const lf = B.newLabel(`${hint}.f`);
|
|
491
|
+
B.condBr(ok, lo, lf);
|
|
492
|
+
B.startBlock(lf);
|
|
493
|
+
B.line(`call void @scr_dyn_check_fail(ptr %path, ptr ${want}, ptr %d)`);
|
|
494
|
+
B.terminate(`ret ${dummy}`);
|
|
495
|
+
B.startBlock(lo);
|
|
496
|
+
};
|
|
497
|
+
switch (t.kind) {
|
|
498
|
+
case "f64": {
|
|
499
|
+
requireKind(DK.NUM, "dc");
|
|
500
|
+
const v = this.payloadOf(B, "%d", "double");
|
|
501
|
+
B.terminate(`ret double ${v}`);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case "bool": {
|
|
505
|
+
requireKind(DK.BOOL, "dc");
|
|
506
|
+
const v = this.boolOf(B, "%d");
|
|
507
|
+
B.terminate(`ret i1 ${v}`);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case "string": {
|
|
511
|
+
requireKind(DK.STR, "dc");
|
|
512
|
+
host.declare(`declare ptr @scr_str_retain_v(ptr)`);
|
|
513
|
+
const s = this.payloadOf(B, "%d", "ptr");
|
|
514
|
+
const r = B.tmp();
|
|
515
|
+
B.line(`${r} = call ptr @scr_str_retain_v(ptr ${s})`);
|
|
516
|
+
B.terminate(`ret ptr ${r}`);
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case "dyn": {
|
|
520
|
+
// An `unknown` slot: the DOM subtree passes through as-is.
|
|
521
|
+
const r = this.retainDyn(B, "%d");
|
|
522
|
+
B.terminate(`ret ptr ${r}`);
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
case "bytes": {
|
|
526
|
+
// `u as Uint8Array`: kind check, then a fresh COPY out.
|
|
527
|
+
if (t.elem !== "u8")
|
|
528
|
+
throw new Error(`llvm emitter bug: dynCheck of bytes<${t.elem}>`);
|
|
529
|
+
requireKind(DK.BYTES, "dc");
|
|
530
|
+
host.declare(`declare ptr @scr_dyn_bytes_copy_out(ptr)`);
|
|
531
|
+
const r = B.tmp();
|
|
532
|
+
B.line(`${r} = call ptr @scr_dyn_bytes_copy_out(ptr %d)`);
|
|
533
|
+
B.terminate(`ret ptr ${r}`);
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
case "object": {
|
|
537
|
+
// The %Error extraction (an instanceof-Error narrow on unknown):
|
|
538
|
+
// validate the DOM's error encoding — the reserved "%error" marker
|
|
539
|
+
// caughtToDyn builds — and extract through the runtime's IDENTITY
|
|
540
|
+
// CACHE (scr_error_from_dyn): a DOM error that came from a runtime
|
|
541
|
+
// ScrError answers that very instance, so out-and-back crossings
|
|
542
|
+
// compare reference-equal (the tracing suite's shape); alien
|
|
543
|
+
// %error objects rebuild once and cache the pair. The C walker's
|
|
544
|
+
// arm exactly.
|
|
545
|
+
if (t.className !== "%Error") {
|
|
546
|
+
throw new Error(`llvm emitter bug: dynCheck of class ${t.className} (only %Error extracts from the DOM)`);
|
|
547
|
+
}
|
|
548
|
+
const kd = this.kindOf(B, "%d");
|
|
549
|
+
const isObj = B.tmp();
|
|
550
|
+
B.line(`${isObj} = icmp eq i32 ${kd}, ${DK.OBJ}`);
|
|
551
|
+
const lObj = B.newLabel("dce.o");
|
|
552
|
+
const lFail = B.newLabel("dce.f");
|
|
553
|
+
B.condBr(isObj, lObj, lFail);
|
|
554
|
+
B.startBlock(lObj);
|
|
555
|
+
const marker = this.objGetLit(B, "%d", "%error");
|
|
556
|
+
const hasMarker = B.tmp();
|
|
557
|
+
B.line(`${hasMarker} = icmp ne ptr ${marker}, null`);
|
|
558
|
+
const lGo = B.newLabel("dce.g");
|
|
559
|
+
B.condBr(hasMarker, lGo, lFail);
|
|
560
|
+
B.startBlock(lFail);
|
|
561
|
+
B.line(`call void @scr_dyn_check_fail(ptr %path, ptr ${want}, ptr %d)`);
|
|
562
|
+
B.terminate(`ret ptr null`);
|
|
563
|
+
B.startBlock(lGo);
|
|
564
|
+
host.declare(`declare ptr @scr_error_from_dyn(ptr)`);
|
|
565
|
+
const e = B.tmp();
|
|
566
|
+
B.line(`${e} = call ptr @scr_error_from_dyn(ptr %d)`);
|
|
567
|
+
B.terminate(`ret ptr ${e}`);
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
case "record": {
|
|
571
|
+
const shape = host.recordsById.get(t.shapeId);
|
|
572
|
+
if (!shape)
|
|
573
|
+
throw new Error(`llvm emitter bug: dynCheck of unknown shape ${t.shapeId}`);
|
|
574
|
+
const struct = mangleRecordStruct(t.shapeId);
|
|
575
|
+
const fieldIndex = new Map(shape.fields.map((f, i) => [f.name, i + 1]));
|
|
576
|
+
const releaseR = () => {
|
|
577
|
+
B.line(`call void ${releaseSym(host, t)}(ptr %r0)`);
|
|
578
|
+
};
|
|
579
|
+
// One shared path node, restored per use (lives only during the
|
|
580
|
+
// nested call).
|
|
581
|
+
const pathSlot = "%dcp";
|
|
582
|
+
B.entryAllocas.push(`${pathSlot} = alloca %ScrDynPath`);
|
|
583
|
+
const setPath = (keyText, index) => {
|
|
584
|
+
const pp = B.tmp();
|
|
585
|
+
const kp = B.tmp();
|
|
586
|
+
const ip = B.tmp();
|
|
587
|
+
B.line(`${pp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 0`);
|
|
588
|
+
B.line(`store ptr %path, ptr ${pp}`);
|
|
589
|
+
B.line(`${kp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 1`);
|
|
590
|
+
B.line(`store ptr ${keyText === null ? "null" : host.cstr(keyText)}, ptr ${kp}`);
|
|
591
|
+
B.line(`${ip} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 2`);
|
|
592
|
+
B.line(`store i64 ${index}, ptr ${ip}`);
|
|
593
|
+
};
|
|
594
|
+
const setPathKeyPtr = (keyPtr) => {
|
|
595
|
+
const pp = B.tmp();
|
|
596
|
+
const kp = B.tmp();
|
|
597
|
+
const ip = B.tmp();
|
|
598
|
+
B.line(`${pp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 0`);
|
|
599
|
+
B.line(`store ptr %path, ptr ${pp}`);
|
|
600
|
+
B.line(`${kp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 1`);
|
|
601
|
+
B.line(`store ptr ${keyPtr}, ptr ${kp}`);
|
|
602
|
+
B.line(`${ip} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 2`);
|
|
603
|
+
B.line(`store i64 0, ptr ${ip}`);
|
|
604
|
+
};
|
|
605
|
+
const storeInto = (fieldName, ft, value) => {
|
|
606
|
+
const idx = fieldIndex.get(fieldName);
|
|
607
|
+
const p = B.tmp();
|
|
608
|
+
B.line(`${p} = getelementptr inbounds %${struct}, ptr %r0, i64 0, i32 ${idx} ; .${fieldName}`);
|
|
609
|
+
if (llFieldType(ft) === "i8") {
|
|
610
|
+
const z = B.tmp();
|
|
611
|
+
B.line(`${z} = zext i1 ${value} to i8`);
|
|
612
|
+
B.line(`store i8 ${z}, ptr ${p}`);
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
B.line(`store ${llFieldType(ft)} ${value}, ptr ${p}`);
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
// Tuple targets: a JSON ARRAY of exactly the arity.
|
|
619
|
+
if (shape.tuple) {
|
|
620
|
+
const byIndex = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
|
|
621
|
+
const arityWant = host.cstr(`array of length ${byIndex.length}`);
|
|
622
|
+
requireKind(DK.ARR, "dct");
|
|
623
|
+
const len = this.lenOf(B, "%d");
|
|
624
|
+
const lenOk = B.tmp();
|
|
625
|
+
B.line(`${lenOk} = icmp eq i64 ${len}, ${byIndex.length}`);
|
|
626
|
+
const lGo = B.newLabel("dct.g");
|
|
627
|
+
const lAr = B.newLabel("dct.a");
|
|
628
|
+
B.condBr(lenOk, lGo, lAr);
|
|
629
|
+
B.startBlock(lAr);
|
|
630
|
+
B.line(`call void @scr_dyn_check_fail(ptr %path, ptr ${arityWant}, ptr %d)`);
|
|
631
|
+
B.terminate(`ret ptr null`);
|
|
632
|
+
B.startBlock(lGo);
|
|
633
|
+
B.line(`%r0 = call ptr @${mangleRecordNew(t.shapeId)}()`);
|
|
634
|
+
const items = this.itemsOf(B, "%d");
|
|
635
|
+
byIndex.forEach((f, i) => {
|
|
636
|
+
setPath(null, `${i}`);
|
|
637
|
+
const e = this.itemAt(B, items, `${i}`);
|
|
638
|
+
const v = B.tmp();
|
|
639
|
+
B.line(`${v} = call ${this.valTy(f.type)} @${this.dynCheckHelper(f.type)}(ptr ${e}, ptr ${pathSlot})`);
|
|
640
|
+
storeInto(f.name, f.type, v);
|
|
641
|
+
this.pendingBail(B, "dct", releaseR, "ptr null");
|
|
642
|
+
});
|
|
643
|
+
B.terminate(`ret ptr %r0`);
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
requireKind(DK.OBJ, "dcr");
|
|
647
|
+
B.line(`%r0 = call ptr @${mangleRecordNew(t.shapeId)}()`);
|
|
648
|
+
for (const f of shape.fields) {
|
|
649
|
+
const fieldWant = host.cstr(this.dynDesc(f.type));
|
|
650
|
+
const utag = f.type.kind === "union" ? host.undefinedArmTag(f.type) : -1;
|
|
651
|
+
const m = this.objGetLit(B, "%d", f.name);
|
|
652
|
+
if (f.type.kind === "dyn") {
|
|
653
|
+
// An `unknown` field: a present key passes through, a missing
|
|
654
|
+
// one IS the undefined DOM value.
|
|
655
|
+
const has = B.tmp();
|
|
656
|
+
B.line(`${has} = icmp ne ptr ${m}, null`);
|
|
657
|
+
const sel = B.tmp();
|
|
658
|
+
const u = this.undef(B);
|
|
659
|
+
B.line(`${sel} = select i1 ${has}, ptr ${m}, ptr ${u}`);
|
|
660
|
+
storeInto(f.name, f.type, this.retainDyn(B, sel));
|
|
661
|
+
}
|
|
662
|
+
else if (utag >= 0 && f.type.kind === "union") {
|
|
663
|
+
const unit = host.unitInstanceRef(f.type.unionId, utag);
|
|
664
|
+
const has = B.tmp();
|
|
665
|
+
B.line(`${has} = icmp ne ptr ${m}, null`);
|
|
666
|
+
const lp = B.newLabel("dcr.p");
|
|
667
|
+
const la = B.newLabel("dcr.a");
|
|
668
|
+
const lj = B.newLabel("dcr.j");
|
|
669
|
+
B.condBr(has, lp, la);
|
|
670
|
+
B.startBlock(la);
|
|
671
|
+
storeInto(f.name, f.type, unit); // absent key -> the undefined arm
|
|
672
|
+
B.br(lj);
|
|
673
|
+
B.startBlock(lp);
|
|
674
|
+
setPath(f.name, "0");
|
|
675
|
+
const v = B.tmp();
|
|
676
|
+
B.line(`${v} = call ptr @${this.dynCheckHelper(f.type)}(ptr ${m}, ptr ${pathSlot})`);
|
|
677
|
+
storeInto(f.name, f.type, v);
|
|
678
|
+
this.pendingBail(B, "dcr", releaseR, "ptr null");
|
|
679
|
+
B.br(lj);
|
|
680
|
+
B.startBlock(lj);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
const has = B.tmp();
|
|
684
|
+
B.line(`${has} = icmp ne ptr ${m}, null`);
|
|
685
|
+
const lp = B.newLabel("dcr.p");
|
|
686
|
+
const la = B.newLabel("dcr.a");
|
|
687
|
+
B.condBr(has, lp, la);
|
|
688
|
+
B.startBlock(la);
|
|
689
|
+
setPath(f.name, "0");
|
|
690
|
+
B.line(`call void @scr_dyn_check_fail(ptr ${pathSlot}, ptr ${fieldWant}, ptr null)`);
|
|
691
|
+
releaseR();
|
|
692
|
+
B.terminate(`ret ptr null`);
|
|
693
|
+
B.startBlock(lp);
|
|
694
|
+
setPath(f.name, "0");
|
|
695
|
+
const v = B.tmp();
|
|
696
|
+
B.line(`${v} = call ${this.valTy(f.type)} @${this.dynCheckHelper(f.type)}(ptr ${m}, ptr ${pathSlot})`);
|
|
697
|
+
storeInto(f.name, f.type, v);
|
|
698
|
+
this.pendingBail(B, "dcr", releaseR, "ptr null");
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
// Index-signature shapes CAPTURE undeclared keys into the
|
|
702
|
+
// overflow map.
|
|
703
|
+
if (shape.indexValue) {
|
|
704
|
+
const iv = shape.indexValue;
|
|
705
|
+
host.declare(`declare i32 @memcmp(ptr, ptr, i64)`);
|
|
706
|
+
host.declare(`declare ptr @scr_str_new(ptr, i64)`);
|
|
707
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
708
|
+
const ovfp = B.tmp();
|
|
709
|
+
const ovf = B.tmp();
|
|
710
|
+
B.line(`${ovfp} = getelementptr inbounds %${struct}, ptr %r0, i64 0, i32 ${shape.fields.length + 1}`);
|
|
711
|
+
B.line(`${ovf} = load ptr, ptr ${ovfp} ; overflow map`);
|
|
712
|
+
const n = this.lenOf(B, "%d");
|
|
713
|
+
const entries = this.itemsOf(B, "%d");
|
|
714
|
+
this.i64Loop(B, "dcv", n, (i, brNext) => {
|
|
715
|
+
const ent = this.entryAt(B, entries, i);
|
|
716
|
+
for (const f of shape.fields) {
|
|
717
|
+
const klen = Buffer.byteLength(f.name, "utf8");
|
|
718
|
+
const lenEq = B.tmp();
|
|
719
|
+
B.line(`${lenEq} = icmp eq i64 ${ent.keyLen}, ${klen}`);
|
|
720
|
+
const lCmp = B.newLabel("dcv.kc");
|
|
721
|
+
const lNo = B.newLabel("dcv.kn");
|
|
722
|
+
B.condBr(lenEq, lCmp, lNo);
|
|
723
|
+
B.startBlock(lCmp);
|
|
724
|
+
const c = B.tmp();
|
|
725
|
+
const same = B.tmp();
|
|
726
|
+
B.line(`${c} = call i32 @memcmp(ptr ${ent.key}, ptr ${host.cstr(f.name)}, i64 ${klen}) ; ${f.name}`);
|
|
727
|
+
B.line(`${same} = icmp eq i32 ${c}, 0`);
|
|
728
|
+
const skip = B.newLabel("dcv.ks");
|
|
729
|
+
const lNo2 = B.newLabel("dcv.kn");
|
|
730
|
+
B.condBr(same, skip, lNo2);
|
|
731
|
+
B.startBlock(skip);
|
|
732
|
+
brNext();
|
|
733
|
+
B.startBlock(lNo2);
|
|
734
|
+
B.br(lNo);
|
|
735
|
+
B.startBlock(lNo);
|
|
736
|
+
}
|
|
737
|
+
let ev;
|
|
738
|
+
if (iv.kind === "dyn") {
|
|
739
|
+
ev = this.retainDyn(B, ent.value);
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
setPathKeyPtr(ent.key);
|
|
743
|
+
ev = B.tmp();
|
|
744
|
+
B.line(`${ev} = call ${this.valTy(iv)} @${this.dynCheckHelper(iv)}(ptr ${ent.value}, ptr ${pathSlot})`);
|
|
745
|
+
this.pendingBail(B, "dcv", releaseR, "ptr null");
|
|
746
|
+
}
|
|
747
|
+
const ek = B.tmp();
|
|
748
|
+
B.line(`${ek} = call ptr @scr_str_new(ptr ${ent.key}, i64 ${ent.keyLen})`);
|
|
749
|
+
if (iv.kind === "f64") {
|
|
750
|
+
host.declare(`declare void @scr_map_set_str_f64(ptr, ptr, double)`);
|
|
751
|
+
B.line(`call void @scr_map_set_str_f64(ptr ${ovf}, ptr ${ek}, double ${ev})`);
|
|
752
|
+
}
|
|
753
|
+
else if (iv.kind === "bool") {
|
|
754
|
+
host.declare(`declare void @scr_map_set_str_bool(ptr, ptr, i1 zeroext)`);
|
|
755
|
+
B.line(`call void @scr_map_set_str_bool(ptr ${ovf}, ptr ${ek}, i1 ${ev})`);
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
host.declare(`declare void @scr_map_set_str_ref(ptr, ptr, ptr)`);
|
|
759
|
+
B.line(`call void @scr_map_set_str_ref(ptr ${ovf}, ptr ${ek}, ptr ${ev}) ; v moves in`);
|
|
760
|
+
}
|
|
761
|
+
B.line(`call void @scr_str_release(ptr ${ek})`);
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
B.terminate(`ret ptr %r0`);
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
case "array": {
|
|
768
|
+
const elem = t.elem;
|
|
769
|
+
const c = this.dynCheckHelper(elem);
|
|
770
|
+
requireKind(DK.ARR, "dca");
|
|
771
|
+
const n = this.lenOf(B, "%d");
|
|
772
|
+
const a = B.tmp();
|
|
773
|
+
B.line(`${a} = ${arrNewCall(host, elem, n)}`);
|
|
774
|
+
const items = this.itemsOf(B, "%d");
|
|
775
|
+
const pathSlot = "%dcp";
|
|
776
|
+
B.entryAllocas.push(`${pathSlot} = alloca %ScrDynPath`);
|
|
777
|
+
const acc = elemAccess(elem);
|
|
778
|
+
const accTy = acc === "f64" ? "double" : acc === "bool" ? "i1" : "ptr";
|
|
779
|
+
host.declare(`declare double @scr_arr_push_${acc}(ptr, ${acc === "bool" ? "i1 zeroext" : accTy})`);
|
|
780
|
+
this.i64Loop(B, "dca", n, (i) => {
|
|
781
|
+
const pp = B.tmp();
|
|
782
|
+
const kp = B.tmp();
|
|
783
|
+
const ip = B.tmp();
|
|
784
|
+
B.line(`${pp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 0`);
|
|
785
|
+
B.line(`store ptr %path, ptr ${pp}`);
|
|
786
|
+
B.line(`${kp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 1`);
|
|
787
|
+
B.line(`store ptr null, ptr ${kp}`);
|
|
788
|
+
B.line(`${ip} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 2`);
|
|
789
|
+
B.line(`store i64 ${i}, ptr ${ip}`);
|
|
790
|
+
const e = this.itemAt(B, items, i);
|
|
791
|
+
const v = B.tmp();
|
|
792
|
+
B.line(`${v} = call ${this.valTy(elem)} @${c}(ptr ${e}, ptr ${pathSlot})`);
|
|
793
|
+
this.pendingBail(B, "dca", () => {
|
|
794
|
+
host.declare(`declare void @scr_arr_release(ptr)`);
|
|
795
|
+
B.line(`call void @scr_arr_release(ptr ${a})`);
|
|
796
|
+
}, "ptr null");
|
|
797
|
+
const pushed = B.tmp();
|
|
798
|
+
B.line(`${pushed} = call double @scr_arr_push_${acc}(ptr ${a}, ${accTy} ${v})`);
|
|
799
|
+
});
|
|
800
|
+
B.terminate(`ret ptr ${a}`);
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
case "union": {
|
|
804
|
+
const def = host.unionsById.get(t.unionId);
|
|
805
|
+
if (!def)
|
|
806
|
+
throw new Error(`llvm emitter bug: dynCheck of unknown union ${t.unionId}`);
|
|
807
|
+
// Arms in CANONICAL order, first FULL match wins. The matched
|
|
808
|
+
// arm's builder can no longer fail.
|
|
809
|
+
def.arms.forEach((arm, i) => {
|
|
810
|
+
const m = this.dynMatchHelper(arm);
|
|
811
|
+
const hit = B.tmp();
|
|
812
|
+
B.line(`${hit} = call zeroext i1 @${m}(ptr %d)`);
|
|
813
|
+
const lHit = B.newLabel("dcu.h");
|
|
814
|
+
const lNext = B.newLabel("dcu.n");
|
|
815
|
+
B.condBr(hit, lHit, lNext);
|
|
816
|
+
B.startBlock(lHit);
|
|
817
|
+
if (arm.kind === "undefinedT" || arm.kind === "nullT") {
|
|
818
|
+
// A matched unit arm builds nothing: THE interned immortal
|
|
819
|
+
// instance (rc == SIZE_MAX — no retain owed).
|
|
820
|
+
B.terminate(`ret ptr ${host.unitInstanceRef(t.unionId, i)}`);
|
|
821
|
+
}
|
|
822
|
+
else if (arm.kind === "f64") {
|
|
823
|
+
host.declare(`declare ptr @scr_union_new_f64(i32, double)`);
|
|
824
|
+
const x = B.tmp();
|
|
825
|
+
const u = B.tmp();
|
|
826
|
+
B.line(`${x} = call double @${this.dynCheckHelper(arm)}(ptr %d, ptr %path)`);
|
|
827
|
+
B.line(`${u} = call ptr @scr_union_new_f64(i32 ${i}, double ${x})`);
|
|
828
|
+
B.terminate(`ret ptr ${u}`);
|
|
829
|
+
}
|
|
830
|
+
else if (arm.kind === "bool") {
|
|
831
|
+
host.declare(`declare ptr @scr_union_new_bool(i32, i1 zeroext)`);
|
|
832
|
+
const x = B.tmp();
|
|
833
|
+
const u = B.tmp();
|
|
834
|
+
B.line(`${x} = call zeroext i1 @${this.dynCheckHelper(arm)}(ptr %d, ptr %path)`);
|
|
835
|
+
B.line(`${u} = call ptr @scr_union_new_bool(i32 ${i}, i1 ${x})`);
|
|
836
|
+
B.terminate(`ret ptr ${u}`);
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
const rc = vAdapters(host, arm);
|
|
840
|
+
host.declare(`declare ptr @scr_union_new_ref(i32, ptr, ptr, ptr, ptr)`);
|
|
841
|
+
const x = B.tmp();
|
|
842
|
+
const u = B.tmp();
|
|
843
|
+
B.line(`${x} = call ptr @${this.dynCheckHelper(arm)}(ptr %d, ptr %path)`);
|
|
844
|
+
B.line(`${u} = call ptr @scr_union_new_ref(i32 ${i}, ptr ${x}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(host, arm)})`);
|
|
845
|
+
B.terminate(`ret ptr ${u}`);
|
|
846
|
+
}
|
|
847
|
+
B.startBlock(lNext);
|
|
848
|
+
});
|
|
849
|
+
B.line(`call void @scr_dyn_check_fail(ptr %path, ptr ${want}, ptr %d)`);
|
|
850
|
+
B.terminate(`ret ptr null`);
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
case "func": {
|
|
854
|
+
// The checked-dynamic function boundary, OUT direction: an
|
|
855
|
+
// IDENTICAL boxed signature unwraps the closure directly;
|
|
856
|
+
// anything else wraps in the per-target adapter closure whose
|
|
857
|
+
// caps[0] obj-box owns the dyn value (untraced).
|
|
858
|
+
const adapter = this.dynFuncAdapterHelper(t);
|
|
859
|
+
const sigLit = host.cstr(key);
|
|
860
|
+
requireKind(DK.FUNC, "dcf");
|
|
861
|
+
host.declare(`declare i32 @strcmp(ptr, ptr)`);
|
|
862
|
+
const sigp = B.tmp();
|
|
863
|
+
const sig = B.tmp();
|
|
864
|
+
B.line(`${sigp} = getelementptr inbounds i8, ptr %d, i64 32 ; ->v.fn.sig`);
|
|
865
|
+
B.line(`${sig} = load ptr, ptr ${sigp}`);
|
|
866
|
+
const cmp = B.tmp();
|
|
867
|
+
const same = B.tmp();
|
|
868
|
+
B.line(`${cmp} = call i32 @strcmp(ptr ${sig}, ptr ${sigLit})`);
|
|
869
|
+
B.line(`${same} = icmp eq i32 ${cmp}, 0`);
|
|
870
|
+
const lSame = B.newLabel("dcf.s");
|
|
871
|
+
const lWrap = B.newLabel("dcf.w");
|
|
872
|
+
B.condBr(same, lSame, lWrap);
|
|
873
|
+
B.startBlock(lSame);
|
|
874
|
+
host.declare(`declare ptr @scr_closure_retain_v(ptr)`);
|
|
875
|
+
const clop = B.tmp();
|
|
876
|
+
const clo = B.tmp();
|
|
877
|
+
const r = B.tmp();
|
|
878
|
+
B.line(`${clop} = getelementptr inbounds i8, ptr %d, i64 16 ; ->v.fn.clo`);
|
|
879
|
+
B.line(`${clo} = load ptr, ptr ${clop}`);
|
|
880
|
+
B.line(`${r} = call ptr @scr_closure_retain_v(ptr ${clo})`);
|
|
881
|
+
B.terminate(`ret ptr ${r}`);
|
|
882
|
+
B.startBlock(lWrap);
|
|
883
|
+
host.declare(`declare ptr @scr_closure_new(ptr, i64)`);
|
|
884
|
+
host.declare(`declare ptr @scr_box_new_obj(ptr, ptr, ptr)`);
|
|
885
|
+
host.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
|
|
886
|
+
host.declare(`declare ptr @scr_dyn_retain_v(ptr)`);
|
|
887
|
+
host.declare(`declare void @scr_dyn_release_v(ptr)`);
|
|
888
|
+
const a = B.tmp();
|
|
889
|
+
B.line(`${a} = call ptr @scr_closure_new(ptr @${adapter}, i64 1)`);
|
|
890
|
+
const box = B.tmp();
|
|
891
|
+
B.line(`${box} = call ptr @scr_box_new_obj(ptr @scr_dyn_retain_v, ptr @scr_dyn_release_v, ptr null)`);
|
|
892
|
+
const capp = B.tmp();
|
|
893
|
+
B.line(`${capp} = getelementptr inbounds i8, ptr ${a}, i64 32 ; caps[0]`);
|
|
894
|
+
B.line(`store ptr ${box}, ptr ${capp}`);
|
|
895
|
+
const rd = this.retainDyn(B, "%d");
|
|
896
|
+
B.line(`call void @scr_box_set_ref(ptr ${box}, ptr ${rd})`);
|
|
897
|
+
B.terminate(`ret ptr ${a}`);
|
|
898
|
+
break;
|
|
899
|
+
}
|
|
900
|
+
default: {
|
|
901
|
+
// Runtime HANDLE targets: a tag-checked reference unwrap (+1 —
|
|
902
|
+
// identity, no copy; the runtime throws the path-annotated
|
|
903
|
+
// TypeError on any other kind or tag).
|
|
904
|
+
const h = DYN_HANDLE_KINDS.get(t.kind);
|
|
905
|
+
if (h) {
|
|
906
|
+
host.declare(`declare ptr @scr_dyn_handle_unbox(ptr, i32, ptr, ptr)`);
|
|
907
|
+
const r = B.tmp();
|
|
908
|
+
B.line(`${r} = call ptr @scr_dyn_handle_unbox(ptr %d, i32 ${DYN_HANDLE_TAG_NUM[t.kind]}, ptr %path, ptr ${want})`);
|
|
909
|
+
B.terminate(`ret ptr ${r}`);
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
throw new LlvmUnsupportedError(`type:${t.kind}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
this.defs.push(`define internal ${retTy === "i1" ? "zeroext i1" : retTy} @${name}(ptr %d, ptr %path) ${FN_ATTRS} { ; check ${key}`, B.render(), `}`, ``);
|
|
916
|
+
return name;
|
|
917
|
+
}
|
|
918
|
+
/* ── toDynHelper (emit-walkers.ts, ported) ─────────────────────────── */
|
|
919
|
+
/** `sc_td_<n>(<T> v) -> ptr` — build a fresh dyn DOM value from a
|
|
920
|
+
* static one (+1), DEEP-COPYING composites. Borrows the operand.
|
|
921
|
+
* Never throws. */
|
|
922
|
+
toDynHelper(t) {
|
|
923
|
+
const key = typeKey(t);
|
|
924
|
+
const existing = this.toDynFns.get(key);
|
|
925
|
+
if (existing)
|
|
926
|
+
return existing;
|
|
927
|
+
const name = `sc_td_${this.toDynFns.size}`;
|
|
928
|
+
this.toDynFns.set(key, name);
|
|
929
|
+
const host = this.host;
|
|
930
|
+
const B = new BlockBuilder();
|
|
931
|
+
switch (t.kind) {
|
|
932
|
+
case "f64": {
|
|
933
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
934
|
+
const r = B.tmp();
|
|
935
|
+
B.line(`${r} = call ptr @scr_dyn_new_num(double %v)`);
|
|
936
|
+
B.terminate(`ret ptr ${r}`);
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
case "bool": {
|
|
940
|
+
host.declare(`declare ptr @scr_dyn_new_bool(i1 zeroext)`);
|
|
941
|
+
const r = B.tmp();
|
|
942
|
+
B.line(`${r} = call ptr @scr_dyn_new_bool(i1 %v)`);
|
|
943
|
+
B.terminate(`ret ptr ${r}`);
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
case "string": {
|
|
947
|
+
host.declare(`declare ptr @scr_dyn_new_str(ptr)`);
|
|
948
|
+
const r = B.tmp();
|
|
949
|
+
B.line(`${r} = call ptr @scr_dyn_new_str(ptr %v) ; retains v`);
|
|
950
|
+
B.terminate(`ret ptr ${r}`);
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
case "object": {
|
|
954
|
+
// %Error only (canConvertToDyn's gate): the DOM's error encoding.
|
|
955
|
+
if (t.className !== "%Error") {
|
|
956
|
+
throw new Error(`llvm emitter bug: to-dyn of class ${t.className}`);
|
|
957
|
+
}
|
|
958
|
+
host.declare(`declare ptr @scr_dyn_from_error(ptr)`);
|
|
959
|
+
const r = B.tmp();
|
|
960
|
+
B.line(`${r} = call ptr @scr_dyn_from_error(ptr %v)`);
|
|
961
|
+
B.terminate(`ret ptr ${r}`);
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
case "dyn": {
|
|
965
|
+
const r = this.retainDyn(B, "%v");
|
|
966
|
+
B.terminate(`ret ptr ${r}`);
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
case "bytes": {
|
|
970
|
+
if (t.elem !== "u8")
|
|
971
|
+
throw new Error(`llvm emitter bug: to-dyn of bytes<${t.elem}>`);
|
|
972
|
+
host.declare(`declare ptr @scr_dyn_new_bytes_copy(ptr)`);
|
|
973
|
+
const r = B.tmp();
|
|
974
|
+
B.line(`${r} = call ptr @scr_dyn_new_bytes_copy(ptr %v)`);
|
|
975
|
+
B.terminate(`ret ptr ${r}`);
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
case "record": {
|
|
979
|
+
const shape = host.recordsById.get(t.shapeId);
|
|
980
|
+
if (!shape)
|
|
981
|
+
throw new Error(`llvm emitter bug: to-dyn of unknown shape ${t.shapeId}`);
|
|
982
|
+
const struct = mangleRecordStruct(t.shapeId);
|
|
983
|
+
const fieldIndex = new Map(shape.fields.map((f, i) => [f.name, i + 1]));
|
|
984
|
+
const loadFieldOf = (fname, ft) => {
|
|
985
|
+
const p = B.tmp();
|
|
986
|
+
B.line(`${p} = getelementptr inbounds %${struct}, ptr %v, i64 0, i32 ${fieldIndex.get(fname)} ; .${fname}`);
|
|
987
|
+
const raw = B.tmp();
|
|
988
|
+
B.line(`${raw} = load ${llFieldType(ft)}, ptr ${p}`);
|
|
989
|
+
if (llFieldType(ft) !== "i8")
|
|
990
|
+
return raw;
|
|
991
|
+
const b = B.tmp();
|
|
992
|
+
B.line(`${b} = trunc i8 ${raw} to i1`);
|
|
993
|
+
return b;
|
|
994
|
+
};
|
|
995
|
+
host.declare(`declare void @scr_dyn_obj_set(ptr, ptr, i64, ptr)`);
|
|
996
|
+
if (shape.tuple) {
|
|
997
|
+
// A tuple converts as the JSON ARRAY it is everywhere else.
|
|
998
|
+
host.declare(`declare ptr @scr_dyn_new_arr()`);
|
|
999
|
+
host.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
|
|
1000
|
+
const byIndex = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
|
|
1001
|
+
const d = B.tmp();
|
|
1002
|
+
B.line(`${d} = call ptr @scr_dyn_new_arr()`);
|
|
1003
|
+
for (const f of byIndex) {
|
|
1004
|
+
const fv = loadFieldOf(f.name, f.type);
|
|
1005
|
+
const conv = B.tmp();
|
|
1006
|
+
B.line(`${conv} = call ptr @${this.toDynHelper(f.type)}(${this.valTy(f.type)} ${fv})`);
|
|
1007
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${d}, ptr ${conv})`);
|
|
1008
|
+
}
|
|
1009
|
+
B.terminate(`ret ptr ${d}`);
|
|
1010
|
+
break;
|
|
1011
|
+
}
|
|
1012
|
+
host.declare(`declare ptr @scr_dyn_new_obj()`);
|
|
1013
|
+
const d = B.tmp();
|
|
1014
|
+
B.line(`${d} = call ptr @scr_dyn_new_obj()`);
|
|
1015
|
+
// Keys insert in DECLARED order (JS insertion order); internal
|
|
1016
|
+
// '%'-fields follow so a record→dyn→record round trip keeps them.
|
|
1017
|
+
const byName = new Map(shape.fields.map((f) => [f.name, f]));
|
|
1018
|
+
const order = shape.declaredOrder ?? shape.fields.map((f) => f.name);
|
|
1019
|
+
const inOrder = new Set(order);
|
|
1020
|
+
const dynFields = [
|
|
1021
|
+
...order.map((n) => byName.get(n)).filter((f) => f !== undefined),
|
|
1022
|
+
...shape.fields.filter((f) => !inOrder.has(f.name)),
|
|
1023
|
+
];
|
|
1024
|
+
for (const f of dynFields) {
|
|
1025
|
+
const klen = Buffer.byteLength(f.name, "utf8");
|
|
1026
|
+
const fv = loadFieldOf(f.name, f.type);
|
|
1027
|
+
const conv = B.tmp();
|
|
1028
|
+
B.line(`${conv} = call ptr @${this.toDynHelper(f.type)}(${this.valTy(f.type)} ${fv})`);
|
|
1029
|
+
B.line(`call void @scr_dyn_obj_set(ptr ${d}, ptr ${host.cstr(f.name)}, i64 ${klen}, ptr ${conv}) ; ${f.name}`);
|
|
1030
|
+
}
|
|
1031
|
+
if (shape.indexValue) {
|
|
1032
|
+
const iv = shape.indexValue;
|
|
1033
|
+
host.declare(`declare ptr @scr_map_keys_js_order(ptr)`);
|
|
1034
|
+
host.declare(`declare double @scr_arr_len(ptr)`);
|
|
1035
|
+
host.declare(`declare ptr @scr_arr_get_ref(ptr, double)`);
|
|
1036
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
1037
|
+
host.declare(`declare void @scr_arr_release(ptr)`);
|
|
1038
|
+
const ovfp = B.tmp();
|
|
1039
|
+
const ovf = B.tmp();
|
|
1040
|
+
B.line(`${ovfp} = getelementptr inbounds %${struct}, ptr %v, i64 0, i32 ${shape.fields.length + 1}`);
|
|
1041
|
+
B.line(`${ovf} = load ptr, ptr ${ovfp} ; overflow map`);
|
|
1042
|
+
const ks = B.tmp();
|
|
1043
|
+
const len = B.tmp();
|
|
1044
|
+
B.line(`${ks} = call ptr @scr_map_keys_js_order(ptr ${ovf})`);
|
|
1045
|
+
B.line(`${len} = call double @scr_arr_len(ptr ${ks})`);
|
|
1046
|
+
const iSlot = B.slot();
|
|
1047
|
+
B.entryAllocas.push(`${iSlot} = alloca double`);
|
|
1048
|
+
B.line(`store double ${f64Lit(0)}, ptr ${iSlot}`);
|
|
1049
|
+
const lc = B.newLabel("tdo.c");
|
|
1050
|
+
const lb = B.newLabel("tdo.b");
|
|
1051
|
+
const le = B.newLabel("tdo.e");
|
|
1052
|
+
B.br(lc);
|
|
1053
|
+
B.startBlock(lc);
|
|
1054
|
+
const i = B.tmp();
|
|
1055
|
+
const cont = B.tmp();
|
|
1056
|
+
B.line(`${i} = load double, ptr ${iSlot}`);
|
|
1057
|
+
B.line(`${cont} = fcmp olt double ${i}, ${len}`);
|
|
1058
|
+
B.condBr(cont, lb, le);
|
|
1059
|
+
B.startBlock(lb);
|
|
1060
|
+
const k = B.tmp();
|
|
1061
|
+
B.line(`${k} = call ptr @scr_arr_get_ref(ptr ${ks}, double ${i}) ; key (+1)`);
|
|
1062
|
+
const { len: klen, data: kdata } = this.strParts(B, k);
|
|
1063
|
+
if (iv.kind === "f64" || iv.kind === "bool") {
|
|
1064
|
+
const outTy = iv.kind === "f64" ? "double" : "i8";
|
|
1065
|
+
const outSlot = B.slot();
|
|
1066
|
+
B.entryAllocas.push(`${outSlot} = alloca ${outTy}`);
|
|
1067
|
+
B.line(`store ${outTy} ${iv.kind === "f64" ? f64Lit(0) : "0"}, ptr ${outSlot}`);
|
|
1068
|
+
host.declare(`declare zeroext i1 @scr_map_get_str_${iv.kind === "f64" ? "f64" : "bool"}(ptr, ptr, ptr)`);
|
|
1069
|
+
const found = B.tmp();
|
|
1070
|
+
B.line(`${found} = call zeroext i1 @scr_map_get_str_${iv.kind === "f64" ? "f64" : "bool"}(ptr ${ovf}, ptr ${k}, ptr ${outSlot})`);
|
|
1071
|
+
const raw = B.tmp();
|
|
1072
|
+
B.line(`${raw} = load ${outTy}, ptr ${outSlot}`);
|
|
1073
|
+
let val = raw;
|
|
1074
|
+
if (iv.kind === "bool") {
|
|
1075
|
+
val = B.tmp();
|
|
1076
|
+
B.line(`${val} = trunc i8 ${raw} to i1`);
|
|
1077
|
+
}
|
|
1078
|
+
const boxed = B.tmp();
|
|
1079
|
+
if (iv.kind === "f64") {
|
|
1080
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
1081
|
+
B.line(`${boxed} = call ptr @scr_dyn_new_num(double ${val})`);
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
host.declare(`declare ptr @scr_dyn_new_bool(i1 zeroext)`);
|
|
1085
|
+
B.line(`${boxed} = call ptr @scr_dyn_new_bool(i1 ${val})`);
|
|
1086
|
+
}
|
|
1087
|
+
B.line(`call void @scr_dyn_obj_set(ptr ${d}, ptr ${kdata}, i64 ${klen}, ptr ${boxed})`);
|
|
1088
|
+
}
|
|
1089
|
+
else if (iv.kind === "dyn") {
|
|
1090
|
+
// get_str_ref returns +1 — exactly the ownership obj_set takes.
|
|
1091
|
+
host.declare(`declare ptr @scr_map_get_str_ref(ptr, ptr)`);
|
|
1092
|
+
const hit = B.tmp();
|
|
1093
|
+
B.line(`${hit} = call ptr @scr_map_get_str_ref(ptr ${ovf}, ptr ${k})`);
|
|
1094
|
+
B.line(`call void @scr_dyn_obj_set(ptr ${d}, ptr ${kdata}, i64 ${klen}, ptr ${hit})`);
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
host.declare(`declare ptr @scr_map_get_str_ref(ptr, ptr)`);
|
|
1098
|
+
const hit = B.tmp();
|
|
1099
|
+
B.line(`${hit} = call ptr @scr_map_get_str_ref(ptr ${ovf}, ptr ${k})`);
|
|
1100
|
+
const conv = B.tmp();
|
|
1101
|
+
B.line(`${conv} = call ptr @${this.toDynHelper(iv)}(ptr ${hit})`);
|
|
1102
|
+
B.line(`call void @scr_dyn_obj_set(ptr ${d}, ptr ${kdata}, i64 ${klen}, ptr ${conv})`);
|
|
1103
|
+
B.line(`call void ${releaseSym(host, iv)}(ptr ${hit})`);
|
|
1104
|
+
}
|
|
1105
|
+
B.line(`call void @scr_str_release(ptr ${k})`);
|
|
1106
|
+
const i2 = B.tmp();
|
|
1107
|
+
B.line(`${i2} = fadd double ${i}, ${f64Lit(1)}`);
|
|
1108
|
+
B.line(`store double ${i2}, ptr ${iSlot}`);
|
|
1109
|
+
B.br(lc);
|
|
1110
|
+
B.startBlock(le);
|
|
1111
|
+
B.line(`call void @scr_arr_release(ptr ${ks})`);
|
|
1112
|
+
}
|
|
1113
|
+
B.terminate(`ret ptr ${d}`);
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
case "array": {
|
|
1117
|
+
const elem = t.elem;
|
|
1118
|
+
host.declare(`declare ptr @scr_dyn_new_arr()`);
|
|
1119
|
+
host.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
|
|
1120
|
+
host.declare(`declare double @scr_arr_len(ptr)`);
|
|
1121
|
+
const d = B.tmp();
|
|
1122
|
+
B.line(`${d} = call ptr @scr_dyn_new_arr()`);
|
|
1123
|
+
const len = B.tmp();
|
|
1124
|
+
B.line(`${len} = call double @scr_arr_len(ptr %v)`);
|
|
1125
|
+
const iSlot = B.slot();
|
|
1126
|
+
B.entryAllocas.push(`${iSlot} = alloca double`);
|
|
1127
|
+
B.line(`store double ${f64Lit(0)}, ptr ${iSlot}`);
|
|
1128
|
+
const lc = B.newLabel("tda.c");
|
|
1129
|
+
const lb = B.newLabel("tda.b");
|
|
1130
|
+
const le = B.newLabel("tda.e");
|
|
1131
|
+
B.br(lc);
|
|
1132
|
+
B.startBlock(lc);
|
|
1133
|
+
const i = B.tmp();
|
|
1134
|
+
const cont = B.tmp();
|
|
1135
|
+
B.line(`${i} = load double, ptr ${iSlot}`);
|
|
1136
|
+
B.line(`${cont} = fcmp olt double ${i}, ${len}`);
|
|
1137
|
+
B.condBr(cont, lb, le);
|
|
1138
|
+
B.startBlock(lb);
|
|
1139
|
+
if (elem.kind === "f64" || elem.kind === "bool") {
|
|
1140
|
+
const acc = elem.kind;
|
|
1141
|
+
const accTy = elem.kind === "f64" ? "double" : "i1";
|
|
1142
|
+
host.declare(`declare ${elem.kind === "bool" ? "zeroext i1" : accTy} @scr_arr_get_${acc}(ptr, double)`);
|
|
1143
|
+
const e = B.tmp();
|
|
1144
|
+
B.line(`${e} = call ${accTy} @scr_arr_get_${acc}(ptr %v, double ${i})`);
|
|
1145
|
+
const boxed = B.tmp();
|
|
1146
|
+
if (elem.kind === "f64") {
|
|
1147
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
1148
|
+
B.line(`${boxed} = call ptr @scr_dyn_new_num(double ${e})`);
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
host.declare(`declare ptr @scr_dyn_new_bool(i1 zeroext)`);
|
|
1152
|
+
B.line(`${boxed} = call ptr @scr_dyn_new_bool(i1 ${e})`);
|
|
1153
|
+
}
|
|
1154
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${d}, ptr ${boxed})`);
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
host.declare(`declare ptr @scr_arr_get_ref(ptr, double)`);
|
|
1158
|
+
const e = B.tmp();
|
|
1159
|
+
B.line(`${e} = call ptr @scr_arr_get_ref(ptr %v, double ${i}) ; +1`);
|
|
1160
|
+
const conv = B.tmp();
|
|
1161
|
+
B.line(`${conv} = call ptr @${this.toDynHelper(elem)}(ptr ${e})`);
|
|
1162
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${d}, ptr ${conv})`);
|
|
1163
|
+
B.line(`call void ${releaseSym(host, elem)}(ptr ${e})`);
|
|
1164
|
+
}
|
|
1165
|
+
const i2 = B.tmp();
|
|
1166
|
+
B.line(`${i2} = fadd double ${i}, ${f64Lit(1)}`);
|
|
1167
|
+
B.line(`store double ${i2}, ptr ${iSlot}`);
|
|
1168
|
+
B.br(lc);
|
|
1169
|
+
B.startBlock(le);
|
|
1170
|
+
B.terminate(`ret ptr ${d}`);
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
case "union": {
|
|
1174
|
+
const def = host.unionsById.get(t.unionId);
|
|
1175
|
+
if (!def)
|
|
1176
|
+
throw new Error(`llvm emitter bug: to-dyn of unknown union ${t.unionId}`);
|
|
1177
|
+
const tagp = B.tmp();
|
|
1178
|
+
const tag = B.tmp();
|
|
1179
|
+
B.line(`${tagp} = getelementptr inbounds %ScrUnion, ptr %v, i64 0, i32 1`);
|
|
1180
|
+
B.line(`${tag} = load i32, ptr ${tagp}`);
|
|
1181
|
+
const bad = B.newLabel("tdu.bad");
|
|
1182
|
+
const labels = def.arms.map(() => B.newLabel("tdu.a"));
|
|
1183
|
+
B.terminate(`switch i32 ${tag}, label %${bad} [ ${def.arms.map((_, i) => `i32 ${i}, label %${labels[i]}`).join(" ")} ]`);
|
|
1184
|
+
def.arms.forEach((arm, i) => {
|
|
1185
|
+
B.startBlock(labels[i]);
|
|
1186
|
+
if (arm.kind === "undefinedT") {
|
|
1187
|
+
const u = this.undef(B);
|
|
1188
|
+
const r = this.retainDyn(B, u);
|
|
1189
|
+
B.terminate(`ret ptr ${r}`);
|
|
1190
|
+
}
|
|
1191
|
+
else if (arm.kind === "nullT") {
|
|
1192
|
+
host.declare(`declare ptr @scr_dyn_new_null()`);
|
|
1193
|
+
const r = B.tmp();
|
|
1194
|
+
B.line(`${r} = call ptr @scr_dyn_new_null()`);
|
|
1195
|
+
B.terminate(`ret ptr ${r}`);
|
|
1196
|
+
}
|
|
1197
|
+
else if (arm.kind === "f64") {
|
|
1198
|
+
host.declare(`declare double @scr_union_get_f64(ptr)`);
|
|
1199
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
1200
|
+
const x = B.tmp();
|
|
1201
|
+
const r = B.tmp();
|
|
1202
|
+
B.line(`${x} = call double @scr_union_get_f64(ptr %v)`);
|
|
1203
|
+
B.line(`${r} = call ptr @scr_dyn_new_num(double ${x})`);
|
|
1204
|
+
B.terminate(`ret ptr ${r}`);
|
|
1205
|
+
}
|
|
1206
|
+
else if (arm.kind === "bool") {
|
|
1207
|
+
host.declare(`declare zeroext i1 @scr_union_get_bool(ptr)`);
|
|
1208
|
+
host.declare(`declare ptr @scr_dyn_new_bool(i1 zeroext)`);
|
|
1209
|
+
const x = B.tmp();
|
|
1210
|
+
const r = B.tmp();
|
|
1211
|
+
B.line(`${x} = call zeroext i1 @scr_union_get_bool(ptr %v)`);
|
|
1212
|
+
B.line(`${r} = call ptr @scr_dyn_new_bool(i1 ${x})`);
|
|
1213
|
+
B.terminate(`ret ptr ${r}`);
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
const pp = B.tmp();
|
|
1217
|
+
const p = B.tmp();
|
|
1218
|
+
B.line(`${pp} = getelementptr inbounds %ScrUnion, ptr %v, i64 0, i32 5`);
|
|
1219
|
+
B.line(`${p} = load ptr, ptr ${pp}`);
|
|
1220
|
+
const r = B.tmp();
|
|
1221
|
+
B.line(`${r} = call ptr @${this.toDynHelper(arm)}(ptr ${p}) ; ${arm.kind}`);
|
|
1222
|
+
B.terminate(`ret ptr ${r}`);
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
B.startBlock(bad);
|
|
1226
|
+
host.needBadTag();
|
|
1227
|
+
B.line(`call void @sc_bad_tag()`);
|
|
1228
|
+
B.terminate(`unreachable`);
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
case "promise": {
|
|
1232
|
+
// Promises box by REFERENCE (SCR_DYN_PROMISE — identity is the
|
|
1233
|
+
// promise): promise<dyn> carries its ScrPromise directly (the
|
|
1234
|
+
// payload is already a DOM value); any other inner boxes an
|
|
1235
|
+
// ADAPTER promise whose settle callback converts the payload
|
|
1236
|
+
// (rejections copy raw inside the runtime's cb-waiter machinery).
|
|
1237
|
+
if (t.inner.kind === "dyn") {
|
|
1238
|
+
host.declare(`declare ptr @scr_dyn_new_promise(ptr)`);
|
|
1239
|
+
const r = B.tmp();
|
|
1240
|
+
B.line(`${r} = call ptr @scr_dyn_new_promise(ptr %v)`);
|
|
1241
|
+
B.terminate(`ret ptr ${r}`);
|
|
1242
|
+
break;
|
|
1243
|
+
}
|
|
1244
|
+
host.declare(`declare ptr @scr_dyn_new_promise_adapting(ptr, ptr)`);
|
|
1245
|
+
const adapter = this.promiseDynAdapterHelper(t.inner);
|
|
1246
|
+
const r = B.tmp();
|
|
1247
|
+
B.line(`${r} = call ptr @scr_dyn_new_promise_adapting(ptr %v, ptr @${adapter})`);
|
|
1248
|
+
B.terminate(`ret ptr ${r}`);
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
default: {
|
|
1252
|
+
// Runtime HANDLE kinds box by REFERENCE (identity — no copy):
|
|
1253
|
+
// scr_dyn_new_handle retains the borrowed operand through the
|
|
1254
|
+
// tag's installed ops.
|
|
1255
|
+
const h = DYN_HANDLE_KINDS.get(t.kind);
|
|
1256
|
+
if (h) {
|
|
1257
|
+
host.declare(`declare ptr @scr_dyn_new_handle(ptr, i32)`);
|
|
1258
|
+
const r = B.tmp();
|
|
1259
|
+
B.line(`${r} = call ptr @scr_dyn_new_handle(ptr %v, i32 ${DYN_HANDLE_TAG_NUM[t.kind]})`);
|
|
1260
|
+
B.terminate(`ret ptr ${r}`);
|
|
1261
|
+
break;
|
|
1262
|
+
}
|
|
1263
|
+
throw new LlvmUnsupportedError(`type:${t.kind}`);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
this.defs.push(`define internal ptr @${name}(${this.valTy(t)} %v) ${FN_ATTRS} { ; to-dyn ${key}`, B.render(), `}`, ``);
|
|
1267
|
+
return name;
|
|
1268
|
+
}
|
|
1269
|
+
/** The DOM-promise settle adapter for one fulfillment payload type —
|
|
1270
|
+
* promiseDynAdapterHelper (emit-walkers.ts), ported:
|
|
1271
|
+
* `void sc_pda_<n>(ptr %dst, ptr %src)` reads src's fulfilled payload
|
|
1272
|
+
* by its compile-time kind, converts it to a DOM value, and fulfills
|
|
1273
|
+
* dst with it (scr_dyn_new_promise_adapting's callback; rejections
|
|
1274
|
+
* never reach an adapter — the runtime copies them raw). Interned per
|
|
1275
|
+
* inner typeKey. */
|
|
1276
|
+
promiseDynAdapterHelper(inner) {
|
|
1277
|
+
const key = typeKey(inner);
|
|
1278
|
+
const existing = this.promiseDynAdapters.get(key);
|
|
1279
|
+
if (existing)
|
|
1280
|
+
return existing;
|
|
1281
|
+
const name = `sc_pda_${this.promiseDynAdapters.size}`;
|
|
1282
|
+
this.promiseDynAdapters.set(key, name);
|
|
1283
|
+
const host = this.host;
|
|
1284
|
+
const B = new BlockBuilder();
|
|
1285
|
+
host.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
|
|
1286
|
+
host.declare(`declare ptr @scr_dyn_retain_v(ptr)`);
|
|
1287
|
+
host.declare(`declare void @scr_dyn_release_v(ptr)`);
|
|
1288
|
+
const fulfill = (dv) => {
|
|
1289
|
+
B.line(`call void @scr_promise_fulfill_ref(ptr %dst, ptr ${dv}, ptr @scr_dyn_retain_v, ptr @scr_dyn_release_v, ptr null)`);
|
|
1290
|
+
};
|
|
1291
|
+
switch (inner.kind) {
|
|
1292
|
+
case "void":
|
|
1293
|
+
case "undefinedT": {
|
|
1294
|
+
const u = this.undef(B);
|
|
1295
|
+
fulfill(this.retainDyn(B, u));
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1298
|
+
case "nullT": {
|
|
1299
|
+
host.declare(`declare ptr @scr_dyn_new_null()`);
|
|
1300
|
+
const dv = B.tmp();
|
|
1301
|
+
B.line(`${dv} = call ptr @scr_dyn_new_null()`);
|
|
1302
|
+
fulfill(dv);
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
case "f64": {
|
|
1306
|
+
host.declare(`declare double @scr_promise_payload_f64(ptr)`);
|
|
1307
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
1308
|
+
const x = B.tmp();
|
|
1309
|
+
const dv = B.tmp();
|
|
1310
|
+
B.line(`${x} = call double @scr_promise_payload_f64(ptr %src)`);
|
|
1311
|
+
B.line(`${dv} = call ptr @scr_dyn_new_num(double ${x})`);
|
|
1312
|
+
fulfill(dv);
|
|
1313
|
+
break;
|
|
1314
|
+
}
|
|
1315
|
+
case "bool": {
|
|
1316
|
+
host.declare(`declare zeroext i1 @scr_promise_payload_bool(ptr)`);
|
|
1317
|
+
host.declare(`declare ptr @scr_dyn_new_bool(i1 zeroext)`);
|
|
1318
|
+
const x = B.tmp();
|
|
1319
|
+
const dv = B.tmp();
|
|
1320
|
+
B.line(`${x} = call zeroext i1 @scr_promise_payload_bool(ptr %src)`);
|
|
1321
|
+
B.line(`${dv} = call ptr @scr_dyn_new_bool(i1 ${x})`);
|
|
1322
|
+
fulfill(dv);
|
|
1323
|
+
break;
|
|
1324
|
+
}
|
|
1325
|
+
case "string": {
|
|
1326
|
+
host.declare(`declare ptr @scr_promise_payload_str(ptr)`);
|
|
1327
|
+
host.declare(`declare ptr @scr_dyn_new_str(ptr)`);
|
|
1328
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
1329
|
+
const s = B.tmp();
|
|
1330
|
+
const dv = B.tmp();
|
|
1331
|
+
B.line(`${s} = call ptr @scr_promise_payload_str(ptr %src) ; +1`);
|
|
1332
|
+
B.line(`${dv} = call ptr @scr_dyn_new_str(ptr ${s}) ; retains s`);
|
|
1333
|
+
B.line(`call void @scr_str_release(ptr ${s})`);
|
|
1334
|
+
fulfill(dv);
|
|
1335
|
+
break;
|
|
1336
|
+
}
|
|
1337
|
+
default: {
|
|
1338
|
+
// Ref-payload inners (records, arrays, bytes, %Error, unions,
|
|
1339
|
+
// nested promises, handles): extract (+1 via the stored retain),
|
|
1340
|
+
// convert through the shared to-dyn spelling, release the extract.
|
|
1341
|
+
host.declare(`declare ptr @scr_promise_payload_ref(ptr)`);
|
|
1342
|
+
const pv = B.tmp();
|
|
1343
|
+
B.line(`${pv} = call ptr @scr_promise_payload_ref(ptr %src) ; +1`);
|
|
1344
|
+
const dv = this.toDynExpr(B, inner, pv);
|
|
1345
|
+
if (isRefCounted(inner))
|
|
1346
|
+
B.line(`call void ${releaseSym(host, inner)}(ptr ${pv})`);
|
|
1347
|
+
fulfill(dv);
|
|
1348
|
+
break;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
B.terminate(`ret void`);
|
|
1352
|
+
this.defs.push(`define internal void @${name}(ptr %dst, ptr %src) ${FN_ATTRS} { ; dyn-box settle adapter for promise<${key}>`, B.render(), `}`, ``);
|
|
1353
|
+
return name;
|
|
1354
|
+
}
|
|
1355
|
+
/* ── the dyn ToString pair (dynToStrHelper, ported) ────────────────── */
|
|
1356
|
+
/** Node's String() over a DOM value — sc_ds (+1 result) over the
|
|
1357
|
+
* recursive sc_ds_buf walker. Borrowed operand; never throws. */
|
|
1358
|
+
dynToStrHelper() {
|
|
1359
|
+
if (this.dynToStrFn)
|
|
1360
|
+
return this.dynToStrFn;
|
|
1361
|
+
const name = "sc_ds";
|
|
1362
|
+
this.dynToStrFn = name;
|
|
1363
|
+
const host = this.host;
|
|
1364
|
+
host.declare(`declare void @scr_jb_init(ptr)`);
|
|
1365
|
+
host.declare(`declare ptr @scr_jb_finish(ptr)`);
|
|
1366
|
+
host.declare(`declare void @scr_jb_putc(ptr, i8)`);
|
|
1367
|
+
host.declare(`declare void @scr_jb_puts(ptr, ptr)`);
|
|
1368
|
+
host.declare(`declare ptr @scr_f64_to_scrstr(double)`);
|
|
1369
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
1370
|
+
// The recursive buffer walker.
|
|
1371
|
+
{
|
|
1372
|
+
const B = new BlockBuilder();
|
|
1373
|
+
const kd = this.kindOf(B, "%d");
|
|
1374
|
+
const done = B.newLabel("ds.d");
|
|
1375
|
+
const labels = new Map();
|
|
1376
|
+
for (const k of [DK.NULL, DK.BOOL, DK.NUM, DK.STR, DK.ARR, DK.OBJ, DK.UNDEF, DK.BYTES, DK.FUNC, DK.HANDLE, DK.PROMISE]) {
|
|
1377
|
+
labels.set(k, B.newLabel(`ds.k${k}`));
|
|
1378
|
+
}
|
|
1379
|
+
B.terminate(`switch i32 ${kd}, label %${done} [ ${[...labels].map(([k, l]) => `i32 ${k}, label %${l}`).join(" ")} ]`);
|
|
1380
|
+
B.startBlock(labels.get(DK.UNDEF));
|
|
1381
|
+
this.puts(B, "%b", "undefined");
|
|
1382
|
+
B.br(done);
|
|
1383
|
+
B.startBlock(labels.get(DK.NULL));
|
|
1384
|
+
this.puts(B, "%b", "null");
|
|
1385
|
+
B.br(done);
|
|
1386
|
+
B.startBlock(labels.get(DK.BOOL));
|
|
1387
|
+
{
|
|
1388
|
+
const bv = this.boolOf(B, "%d");
|
|
1389
|
+
const s = B.tmp();
|
|
1390
|
+
B.line(`${s} = select i1 ${bv}, ptr ${host.cstr("true")}, ptr ${host.cstr("false")}`);
|
|
1391
|
+
B.line(`call void @scr_jb_puts(ptr %b, ptr ${s})`);
|
|
1392
|
+
B.br(done);
|
|
1393
|
+
}
|
|
1394
|
+
B.startBlock(labels.get(DK.NUM));
|
|
1395
|
+
{
|
|
1396
|
+
// String(n): NaN/Infinity spelled out, not the JSON null.
|
|
1397
|
+
const x = this.payloadOf(B, "%d", "double");
|
|
1398
|
+
const s = B.tmp();
|
|
1399
|
+
B.line(`${s} = call ptr @scr_f64_to_scrstr(double ${x})`);
|
|
1400
|
+
this.putScrStr(B, "%b", s);
|
|
1401
|
+
B.line(`call void @scr_str_release(ptr ${s})`);
|
|
1402
|
+
B.br(done);
|
|
1403
|
+
}
|
|
1404
|
+
B.startBlock(labels.get(DK.STR));
|
|
1405
|
+
{
|
|
1406
|
+
const s = this.payloadOf(B, "%d", "ptr");
|
|
1407
|
+
this.putScrStr(B, "%b", s);
|
|
1408
|
+
B.br(done);
|
|
1409
|
+
}
|
|
1410
|
+
B.startBlock(labels.get(DK.ARR));
|
|
1411
|
+
{
|
|
1412
|
+
// Array.prototype.toString: join(",") — null/undefined ELEMENTS
|
|
1413
|
+
// print empty (unlike top level), nested arrays flatten.
|
|
1414
|
+
const n = this.lenOf(B, "%d");
|
|
1415
|
+
const items = this.itemsOf(B, "%d");
|
|
1416
|
+
this.i64Loop(B, "ds.ar", n, (i, brNext) => {
|
|
1417
|
+
const nz = B.tmp();
|
|
1418
|
+
B.line(`${nz} = icmp ugt i64 ${i}, 0`);
|
|
1419
|
+
const lcm = B.newLabel("ds.cm");
|
|
1420
|
+
const lel = B.newLabel("ds.el");
|
|
1421
|
+
B.condBr(nz, lcm, lel);
|
|
1422
|
+
B.startBlock(lcm);
|
|
1423
|
+
B.line(`call void @scr_jb_putc(ptr %b, i8 44)`);
|
|
1424
|
+
B.br(lel);
|
|
1425
|
+
B.startBlock(lel);
|
|
1426
|
+
const e = this.itemAt(B, items, i);
|
|
1427
|
+
const ek = this.kindOf(B, e);
|
|
1428
|
+
const isU = B.tmp();
|
|
1429
|
+
const isN = B.tmp();
|
|
1430
|
+
const unit = B.tmp();
|
|
1431
|
+
B.line(`${isU} = icmp eq i32 ${ek}, ${DK.UNDEF}`);
|
|
1432
|
+
B.line(`${isN} = icmp eq i32 ${ek}, ${DK.NULL}`);
|
|
1433
|
+
B.line(`${unit} = or i1 ${isU}, ${isN}`);
|
|
1434
|
+
const lSkip = B.newLabel("ds.sk");
|
|
1435
|
+
const lRec = B.newLabel("ds.rc");
|
|
1436
|
+
B.condBr(unit, lSkip, lRec);
|
|
1437
|
+
B.startBlock(lSkip);
|
|
1438
|
+
brNext();
|
|
1439
|
+
B.startBlock(lRec);
|
|
1440
|
+
B.line(`call void @sc_ds_buf(ptr %b, ptr ${e})`);
|
|
1441
|
+
});
|
|
1442
|
+
B.br(done);
|
|
1443
|
+
}
|
|
1444
|
+
B.startBlock(labels.get(DK.OBJ));
|
|
1445
|
+
{
|
|
1446
|
+
// The DOM's error encoding renders Error.prototype.toString;
|
|
1447
|
+
// plain objects are "[object Object]".
|
|
1448
|
+
const marker = this.objGetLit(B, "%d", "%error");
|
|
1449
|
+
const isErr = B.tmp();
|
|
1450
|
+
B.line(`${isErr} = icmp ne ptr ${marker}, null`);
|
|
1451
|
+
const lErr = B.newLabel("ds.er");
|
|
1452
|
+
const lPlain = B.newLabel("ds.pl");
|
|
1453
|
+
B.condBr(isErr, lErr, lPlain);
|
|
1454
|
+
B.startBlock(lPlain);
|
|
1455
|
+
this.puts(B, "%b", "[object Object]");
|
|
1456
|
+
B.br(done);
|
|
1457
|
+
B.startBlock(lErr);
|
|
1458
|
+
const en = this.objGetLit(B, "%d", "name");
|
|
1459
|
+
const em = this.objGetLit(B, "%d", "message");
|
|
1460
|
+
// ens/ems: the member's ScrStr when it is a string, else null.
|
|
1461
|
+
const strOf = (m, hint) => {
|
|
1462
|
+
const slot = B.slot();
|
|
1463
|
+
B.entryAllocas.push(`${slot} = alloca ptr`);
|
|
1464
|
+
B.line(`store ptr null, ptr ${slot}`);
|
|
1465
|
+
const nn = B.tmp();
|
|
1466
|
+
B.line(`${nn} = icmp ne ptr ${m}, null`);
|
|
1467
|
+
const lt = B.newLabel(`${hint}.t`);
|
|
1468
|
+
const lj = B.newLabel(`${hint}.j`);
|
|
1469
|
+
B.condBr(nn, lt, lj);
|
|
1470
|
+
B.startBlock(lt);
|
|
1471
|
+
const k = this.kindOf(B, m);
|
|
1472
|
+
const isStr = B.tmp();
|
|
1473
|
+
B.line(`${isStr} = icmp eq i32 ${k}, ${DK.STR}`);
|
|
1474
|
+
const ls = B.newLabel(`${hint}.s`);
|
|
1475
|
+
B.condBr(isStr, ls, lj);
|
|
1476
|
+
B.startBlock(ls);
|
|
1477
|
+
const sv = this.payloadOf(B, m, "ptr");
|
|
1478
|
+
B.line(`store ptr ${sv}, ptr ${slot}`);
|
|
1479
|
+
B.br(lj);
|
|
1480
|
+
B.startBlock(lj);
|
|
1481
|
+
const out = B.tmp();
|
|
1482
|
+
B.line(`${out} = load ptr, ptr ${slot}`);
|
|
1483
|
+
return out;
|
|
1484
|
+
};
|
|
1485
|
+
const ens = strOf(en, "ds.en");
|
|
1486
|
+
const ems = strOf(em, "ds.em");
|
|
1487
|
+
const putIf = (s, hint) => {
|
|
1488
|
+
const nn = B.tmp();
|
|
1489
|
+
B.line(`${nn} = icmp ne ptr ${s}, null`);
|
|
1490
|
+
const lt = B.newLabel(`${hint}.t`);
|
|
1491
|
+
const lj = B.newLabel(`${hint}.j`);
|
|
1492
|
+
B.condBr(nn, lt, lj);
|
|
1493
|
+
B.startBlock(lt);
|
|
1494
|
+
this.putScrStr(B, "%b", s);
|
|
1495
|
+
B.br(lj);
|
|
1496
|
+
B.startBlock(lj);
|
|
1497
|
+
};
|
|
1498
|
+
putIf(ens, "ds.pn");
|
|
1499
|
+
{
|
|
1500
|
+
// if (ens && ens->len && ems && ems->len) ": "
|
|
1501
|
+
const lenNz = (s, hint) => {
|
|
1502
|
+
const slot = B.slot();
|
|
1503
|
+
B.entryAllocas.push(`${slot} = alloca i1`);
|
|
1504
|
+
B.line(`store i1 false, ptr ${slot}`);
|
|
1505
|
+
const nn = B.tmp();
|
|
1506
|
+
B.line(`${nn} = icmp ne ptr ${s}, null`);
|
|
1507
|
+
const lt = B.newLabel(`${hint}.t`);
|
|
1508
|
+
const lj = B.newLabel(`${hint}.j`);
|
|
1509
|
+
B.condBr(nn, lt, lj);
|
|
1510
|
+
B.startBlock(lt);
|
|
1511
|
+
const { len } = this.strParts(B, s);
|
|
1512
|
+
const nz = B.tmp();
|
|
1513
|
+
B.line(`${nz} = icmp ne i64 ${len}, 0`);
|
|
1514
|
+
B.line(`store i1 ${nz}, ptr ${slot}`);
|
|
1515
|
+
B.br(lj);
|
|
1516
|
+
B.startBlock(lj);
|
|
1517
|
+
const out = B.tmp();
|
|
1518
|
+
B.line(`${out} = load i1, ptr ${slot}`);
|
|
1519
|
+
return out;
|
|
1520
|
+
};
|
|
1521
|
+
const a = lenNz(ens, "ds.ln");
|
|
1522
|
+
const b2 = lenNz(ems, "ds.lm");
|
|
1523
|
+
const both = B.tmp();
|
|
1524
|
+
B.line(`${both} = and i1 ${a}, ${b2}`);
|
|
1525
|
+
const lt = B.newLabel("ds.cl");
|
|
1526
|
+
const lj = B.newLabel("ds.cj");
|
|
1527
|
+
B.condBr(both, lt, lj);
|
|
1528
|
+
B.startBlock(lt);
|
|
1529
|
+
this.puts(B, "%b", ": ");
|
|
1530
|
+
B.br(lj);
|
|
1531
|
+
B.startBlock(lj);
|
|
1532
|
+
}
|
|
1533
|
+
putIf(ems, "ds.pm");
|
|
1534
|
+
B.br(done);
|
|
1535
|
+
}
|
|
1536
|
+
B.startBlock(labels.get(DK.BYTES));
|
|
1537
|
+
{
|
|
1538
|
+
// Buffer-flavored values coerce utf8; plain Uint8Array joins
|
|
1539
|
+
// its elements.
|
|
1540
|
+
const bufp = B.tmp();
|
|
1541
|
+
const bufRaw = B.tmp();
|
|
1542
|
+
const isBuf = B.tmp();
|
|
1543
|
+
B.line(`${bufp} = getelementptr inbounds i8, ptr %d, i64 12 ; ->buffer`);
|
|
1544
|
+
B.line(`${bufRaw} = load i8, ptr ${bufp}`);
|
|
1545
|
+
B.line(`${isBuf} = icmp ne i8 ${bufRaw}, 0`);
|
|
1546
|
+
const lBuf = B.newLabel("ds.bu");
|
|
1547
|
+
const lJoin = B.newLabel("ds.bj");
|
|
1548
|
+
B.condBr(isBuf, lBuf, lJoin);
|
|
1549
|
+
B.startBlock(lBuf);
|
|
1550
|
+
host.declare(`declare ptr @scr_bytes_to_str(ptr, ptr)`);
|
|
1551
|
+
const bytes = this.payloadOf(B, "%d", "ptr");
|
|
1552
|
+
const enc = host.internLiteral("utf8");
|
|
1553
|
+
const txt = B.tmp();
|
|
1554
|
+
B.line(`${txt} = call ptr @scr_bytes_to_str(ptr ${bytes}, ptr ${enc})`);
|
|
1555
|
+
this.putScrStr(B, "%b", txt);
|
|
1556
|
+
B.line(`call void @scr_str_release(ptr ${txt})`);
|
|
1557
|
+
B.br(done);
|
|
1558
|
+
B.startBlock(lJoin);
|
|
1559
|
+
const bts = this.payloadOf(B, "%d", "ptr");
|
|
1560
|
+
const blenp = B.tmp();
|
|
1561
|
+
const blen = B.tmp();
|
|
1562
|
+
const bdatap = B.tmp();
|
|
1563
|
+
const bdata = B.tmp();
|
|
1564
|
+
B.line(`${blenp} = getelementptr inbounds i8, ptr ${bts}, i64 8 ; ->len`);
|
|
1565
|
+
B.line(`${blen} = load i64, ptr ${blenp}`);
|
|
1566
|
+
B.line(`${bdatap} = getelementptr inbounds i8, ptr ${bts}, i64 24 ; ->data`);
|
|
1567
|
+
B.line(`${bdata} = load ptr, ptr ${bdatap}`);
|
|
1568
|
+
this.i64Loop(B, "ds.by", blen, (i) => {
|
|
1569
|
+
const nz = B.tmp();
|
|
1570
|
+
B.line(`${nz} = icmp ugt i64 ${i}, 0`);
|
|
1571
|
+
const lcm = B.newLabel("ds.bc");
|
|
1572
|
+
const lv = B.newLabel("ds.bv");
|
|
1573
|
+
B.condBr(nz, lcm, lv);
|
|
1574
|
+
B.startBlock(lcm);
|
|
1575
|
+
B.line(`call void @scr_jb_putc(ptr %b, i8 44)`);
|
|
1576
|
+
B.br(lv);
|
|
1577
|
+
B.startBlock(lv);
|
|
1578
|
+
const cp = B.tmp();
|
|
1579
|
+
const c = B.tmp();
|
|
1580
|
+
const cd = B.tmp();
|
|
1581
|
+
B.line(`${cp} = getelementptr inbounds i8, ptr ${bdata}, i64 ${i}`);
|
|
1582
|
+
B.line(`${c} = load i8, ptr ${cp}`);
|
|
1583
|
+
B.line(`${cd} = uitofp i8 ${c} to double`);
|
|
1584
|
+
const s = B.tmp();
|
|
1585
|
+
B.line(`${s} = call ptr @scr_f64_to_scrstr(double ${cd})`);
|
|
1586
|
+
this.putScrStr(B, "%b", s);
|
|
1587
|
+
B.line(`call void @scr_str_release(ptr ${s})`);
|
|
1588
|
+
});
|
|
1589
|
+
B.br(done);
|
|
1590
|
+
}
|
|
1591
|
+
B.startBlock(labels.get(DK.FUNC));
|
|
1592
|
+
{
|
|
1593
|
+
// Function.prototype.toString: the native-code form.
|
|
1594
|
+
this.puts(B, "%b", "function ");
|
|
1595
|
+
const namep = B.tmp();
|
|
1596
|
+
const nm = B.tmp();
|
|
1597
|
+
B.line(`${namep} = getelementptr inbounds i8, ptr %d, i64 40 ; ->v.fn.name`);
|
|
1598
|
+
B.line(`${nm} = load ptr, ptr ${namep}`);
|
|
1599
|
+
const nn = B.tmp();
|
|
1600
|
+
B.line(`${nn} = icmp ne ptr ${nm}, null`);
|
|
1601
|
+
const lt = B.newLabel("ds.fn");
|
|
1602
|
+
const lj = B.newLabel("ds.fj");
|
|
1603
|
+
B.condBr(nn, lt, lj);
|
|
1604
|
+
B.startBlock(lt);
|
|
1605
|
+
B.line(`call void @scr_jb_puts(ptr %b, ptr ${nm})`);
|
|
1606
|
+
B.br(lj);
|
|
1607
|
+
B.startBlock(lj);
|
|
1608
|
+
this.puts(B, "%b", "() { [native code] }");
|
|
1609
|
+
B.br(done);
|
|
1610
|
+
}
|
|
1611
|
+
B.startBlock(labels.get(DK.HANDLE));
|
|
1612
|
+
this.puts(B, "%b", "[object Object]");
|
|
1613
|
+
B.br(done);
|
|
1614
|
+
B.startBlock(labels.get(DK.PROMISE));
|
|
1615
|
+
// Object.prototype.toString with the Promise @@toStringTag.
|
|
1616
|
+
this.puts(B, "%b", "[object Promise]");
|
|
1617
|
+
B.br(done);
|
|
1618
|
+
B.startBlock(done);
|
|
1619
|
+
B.terminate(`ret void`);
|
|
1620
|
+
this.defs.push(`define internal void @${name}_buf(ptr %b, ptr %d) ${FN_ATTRS} { ; String(unknown), recursive`, B.render(), `}`, ``);
|
|
1621
|
+
}
|
|
1622
|
+
// The +1 wrapper with the string fast path.
|
|
1623
|
+
{
|
|
1624
|
+
const B = new BlockBuilder();
|
|
1625
|
+
const kd = this.kindOf(B, "%d");
|
|
1626
|
+
const isStr = B.tmp();
|
|
1627
|
+
B.line(`${isStr} = icmp eq i32 ${kd}, ${DK.STR}`);
|
|
1628
|
+
const lFast = B.newLabel("ds.f");
|
|
1629
|
+
const lSlow = B.newLabel("ds.s");
|
|
1630
|
+
B.condBr(isStr, lFast, lSlow);
|
|
1631
|
+
B.startBlock(lFast);
|
|
1632
|
+
host.declare(`declare ptr @scr_str_retain_v(ptr)`);
|
|
1633
|
+
const s = this.payloadOf(B, "%d", "ptr");
|
|
1634
|
+
const r = B.tmp();
|
|
1635
|
+
B.line(`${r} = call ptr @scr_str_retain_v(ptr ${s})`);
|
|
1636
|
+
B.terminate(`ret ptr ${r}`);
|
|
1637
|
+
B.startBlock(lSlow);
|
|
1638
|
+
const buf = "%dsb";
|
|
1639
|
+
B.entryAllocas.push(`${buf} = alloca %ScrJsonBuf`);
|
|
1640
|
+
B.line(`call void @scr_jb_init(ptr ${buf})`);
|
|
1641
|
+
B.line(`call void @${name}_buf(ptr ${buf}, ptr %d)`);
|
|
1642
|
+
const out = B.tmp();
|
|
1643
|
+
B.line(`${out} = call ptr @scr_jb_finish(ptr ${buf})`);
|
|
1644
|
+
B.terminate(`ret ptr ${out}`);
|
|
1645
|
+
this.defs.push(`define internal ptr @${name}(ptr %d) ${FN_ATTRS} { ; String(unknown) -> owned (+1)`, B.render(), `}`, ``);
|
|
1646
|
+
}
|
|
1647
|
+
return name;
|
|
1648
|
+
}
|
|
1649
|
+
/* ── caughtToDyn (emit-walkers.ts, ported) ─────────────────────────── */
|
|
1650
|
+
/** A catch binding flowing into an `unknown` slot: the typed→unknown
|
|
1651
|
+
* deep-copy stance over the exception snapshot's runtime kind. */
|
|
1652
|
+
caughtToDynHelper() {
|
|
1653
|
+
if (this.caughtToDynFn)
|
|
1654
|
+
return this.caughtToDynFn;
|
|
1655
|
+
const name = "sc_cd";
|
|
1656
|
+
this.caughtToDynFn = name;
|
|
1657
|
+
const host = this.host;
|
|
1658
|
+
const B = new BlockBuilder();
|
|
1659
|
+
const kp = B.tmp();
|
|
1660
|
+
const kd = B.tmp();
|
|
1661
|
+
B.line(`${kp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 1`);
|
|
1662
|
+
B.line(`${kd} = load i32, ptr ${kp}`);
|
|
1663
|
+
const lF64 = B.newLabel("cd.f");
|
|
1664
|
+
const lBool = B.newLabel("cd.b");
|
|
1665
|
+
const lStr = B.newLabel("cd.s");
|
|
1666
|
+
const lObj = B.newLabel("cd.o");
|
|
1667
|
+
const lDef = B.newLabel("cd.d");
|
|
1668
|
+
// SCR_EXC_F64=1, BOOL=2, STR=3, REF=4 (default), OBJ=5.
|
|
1669
|
+
B.terminate(`switch i32 ${kd}, label %${lDef} [ i32 1, label %${lF64} i32 2, label %${lBool} i32 3, label %${lStr} i32 5, label %${lObj} ]`);
|
|
1670
|
+
B.startBlock(lF64);
|
|
1671
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
1672
|
+
{
|
|
1673
|
+
const xp = B.tmp();
|
|
1674
|
+
const x = B.tmp();
|
|
1675
|
+
const r = B.tmp();
|
|
1676
|
+
B.line(`${xp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 2`);
|
|
1677
|
+
B.line(`${x} = load double, ptr ${xp}`);
|
|
1678
|
+
B.line(`${r} = call ptr @scr_dyn_new_num(double ${x})`);
|
|
1679
|
+
B.terminate(`ret ptr ${r}`);
|
|
1680
|
+
}
|
|
1681
|
+
B.startBlock(lBool);
|
|
1682
|
+
host.declare(`declare ptr @scr_dyn_new_bool(i1 zeroext)`);
|
|
1683
|
+
{
|
|
1684
|
+
const xp = B.tmp();
|
|
1685
|
+
const raw = B.tmp();
|
|
1686
|
+
const x = B.tmp();
|
|
1687
|
+
const r = B.tmp();
|
|
1688
|
+
B.line(`${xp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 3`);
|
|
1689
|
+
B.line(`${raw} = load i8, ptr ${xp}`);
|
|
1690
|
+
B.line(`${x} = trunc i8 ${raw} to i1`);
|
|
1691
|
+
B.line(`${r} = call ptr @scr_dyn_new_bool(i1 ${x})`);
|
|
1692
|
+
B.terminate(`ret ptr ${r}`);
|
|
1693
|
+
}
|
|
1694
|
+
B.startBlock(lStr);
|
|
1695
|
+
host.declare(`declare ptr @scr_dyn_new_str(ptr)`);
|
|
1696
|
+
{
|
|
1697
|
+
const pp = B.tmp();
|
|
1698
|
+
const p = B.tmp();
|
|
1699
|
+
const r = B.tmp();
|
|
1700
|
+
B.line(`${pp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 4`);
|
|
1701
|
+
B.line(`${p} = load ptr, ptr ${pp}`);
|
|
1702
|
+
B.line(`${r} = call ptr @scr_dyn_new_str(ptr ${p}) ; _new_str retains`);
|
|
1703
|
+
B.terminate(`ret ptr ${r}`);
|
|
1704
|
+
}
|
|
1705
|
+
B.startBlock(lObj);
|
|
1706
|
+
host.declare(`declare zeroext i1 @scr_error_is(ptr)`);
|
|
1707
|
+
{
|
|
1708
|
+
const pp = B.tmp();
|
|
1709
|
+
const p = B.tmp();
|
|
1710
|
+
B.line(`${pp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 4`);
|
|
1711
|
+
B.line(`${p} = load ptr, ptr ${pp}`);
|
|
1712
|
+
const isErr = B.tmp();
|
|
1713
|
+
B.line(`${isErr} = call zeroext i1 @scr_error_is(ptr ${p})`);
|
|
1714
|
+
const lErr = B.newLabel("cd.e");
|
|
1715
|
+
B.condBr(isErr, lErr, lDef);
|
|
1716
|
+
B.startBlock(lErr);
|
|
1717
|
+
// The identity-cached crossing (scr_json.c): one error instance,
|
|
1718
|
+
// one DOM node — the C walker's arm exactly.
|
|
1719
|
+
host.declare(`declare ptr @scr_dyn_from_error(ptr)`);
|
|
1720
|
+
const r = B.tmp();
|
|
1721
|
+
B.line(`${r} = call ptr @scr_dyn_from_error(ptr ${p})`);
|
|
1722
|
+
B.terminate(`ret ptr ${r}`);
|
|
1723
|
+
}
|
|
1724
|
+
B.startBlock(lDef);
|
|
1725
|
+
// SCR_EXC_REF: a thrown DOM value passes back BY REFERENCE (identity
|
|
1726
|
+
// with every other holder — the dyn adapters discriminate); non-dyn
|
|
1727
|
+
// REF and non-Error objects keep the "[object Object]" approximation
|
|
1728
|
+
// — truthy, typeof "object", fields unreadable.
|
|
1729
|
+
host.declare(`declare ptr @scr_dyn_new_obj()`);
|
|
1730
|
+
host.declare(`declare ptr @scr_dyn_retain_v(ptr)`);
|
|
1731
|
+
{
|
|
1732
|
+
const rfp = B.tmp();
|
|
1733
|
+
const rf = B.tmp();
|
|
1734
|
+
B.line(`${rfp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 5`);
|
|
1735
|
+
B.line(`${rf} = load ptr, ptr ${rfp}`);
|
|
1736
|
+
const isDyn = B.tmp();
|
|
1737
|
+
B.line(`${isDyn} = icmp eq ptr ${rf}, @scr_dyn_retain_v`);
|
|
1738
|
+
const lRef = B.newLabel("cd.r");
|
|
1739
|
+
const lPlain = B.newLabel("cd.p");
|
|
1740
|
+
B.condBr(isDyn, lRef, lPlain);
|
|
1741
|
+
B.startBlock(lRef);
|
|
1742
|
+
const pp = B.tmp();
|
|
1743
|
+
const p = B.tmp();
|
|
1744
|
+
const r = B.tmp();
|
|
1745
|
+
B.line(`${pp} = getelementptr inbounds %ScrCaught, ptr %c, i64 0, i32 4`);
|
|
1746
|
+
B.line(`${p} = load ptr, ptr ${pp}`);
|
|
1747
|
+
B.line(`${r} = call ptr @scr_dyn_retain_v(ptr ${p})`);
|
|
1748
|
+
B.terminate(`ret ptr ${r}`);
|
|
1749
|
+
B.startBlock(lPlain);
|
|
1750
|
+
const e = B.tmp();
|
|
1751
|
+
B.line(`${e} = call ptr @scr_dyn_new_obj()`);
|
|
1752
|
+
B.terminate(`ret ptr ${e}`);
|
|
1753
|
+
}
|
|
1754
|
+
this.defs.push(`define internal ptr @${name}(ptr %c) ${FN_ATTRS} { ; caught -> unknown (+1, fresh tree)`, B.render(), `}`, ``);
|
|
1755
|
+
return name;
|
|
1756
|
+
}
|
|
1757
|
+
/* ── the keyed read (dynKeyGetHelper, ported) ──────────────────────── */
|
|
1758
|
+
/** `sc_dyn_key_get(ptr d, ptr k, i1 opt) -> ptr` — d[k] on a DOM value.
|
|
1759
|
+
* Result +1; throws on non-optional nullish receivers. */
|
|
1760
|
+
dynKeyGetHelper() {
|
|
1761
|
+
const memoKey = "%dynKeyGet";
|
|
1762
|
+
const existing = this.dynBuilders.get(memoKey);
|
|
1763
|
+
if (existing)
|
|
1764
|
+
return existing;
|
|
1765
|
+
const name = "sc_dyn_key_get";
|
|
1766
|
+
this.dynBuilders.set(memoKey, name);
|
|
1767
|
+
const host = this.host;
|
|
1768
|
+
const B = new BlockBuilder();
|
|
1769
|
+
const kd = this.kindOf(B, "%d");
|
|
1770
|
+
const kParts = this.strParts(B, "%k");
|
|
1771
|
+
const retainUndef = () => {
|
|
1772
|
+
const u = this.undef(B);
|
|
1773
|
+
const r = this.retainDyn(B, u);
|
|
1774
|
+
B.terminate(`ret ptr ${r}`);
|
|
1775
|
+
};
|
|
1776
|
+
// undefined/null receivers: opt answers undefined; otherwise throw
|
|
1777
|
+
// Node's TypeError with the key spliced in.
|
|
1778
|
+
{
|
|
1779
|
+
const isU = B.tmp();
|
|
1780
|
+
const isN = B.tmp();
|
|
1781
|
+
const unit = B.tmp();
|
|
1782
|
+
B.line(`${isU} = icmp eq i32 ${kd}, ${DK.UNDEF}`);
|
|
1783
|
+
B.line(`${isN} = icmp eq i32 ${kd}, ${DK.NULL}`);
|
|
1784
|
+
B.line(`${unit} = or i1 ${isU}, ${isN}`);
|
|
1785
|
+
const lUnit = B.newLabel("kg.u");
|
|
1786
|
+
const lNext = B.newLabel("kg.n");
|
|
1787
|
+
B.condBr(unit, lUnit, lNext);
|
|
1788
|
+
B.startBlock(lUnit);
|
|
1789
|
+
const lOpt = B.newLabel("kg.o");
|
|
1790
|
+
const lThrow = B.newLabel("kg.t");
|
|
1791
|
+
B.condBr("%opt", lOpt, lThrow);
|
|
1792
|
+
B.startBlock(lOpt);
|
|
1793
|
+
retainUndef();
|
|
1794
|
+
B.startBlock(lThrow);
|
|
1795
|
+
host.declare(`declare ptr @scr_str_new(ptr, i64)`);
|
|
1796
|
+
host.declare(`declare ptr @scr_str_concat(ptr, ptr)`);
|
|
1797
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
1798
|
+
host.declare(`declare void @scr_throw_error(i32, ptr)`);
|
|
1799
|
+
const baseU = "Cannot read properties of undefined (reading '";
|
|
1800
|
+
const baseN = "Cannot read properties of null (reading '";
|
|
1801
|
+
const base = B.tmp();
|
|
1802
|
+
B.line(`${base} = select i1 ${isU}, ptr ${host.cstr(baseU)}, ptr ${host.cstr(baseN)}`);
|
|
1803
|
+
const baseLen = B.tmp();
|
|
1804
|
+
B.line(`${baseLen} = select i1 ${isU}, i64 ${Buffer.byteLength(baseU)}, i64 ${Buffer.byteLength(baseN)}`);
|
|
1805
|
+
const head = B.tmp();
|
|
1806
|
+
B.line(`${head} = call ptr @scr_str_new(ptr ${base}, i64 ${baseLen})`);
|
|
1807
|
+
const withKey = B.tmp();
|
|
1808
|
+
B.line(`${withKey} = call ptr @scr_str_concat(ptr ${head}, ptr %k)`);
|
|
1809
|
+
B.line(`call void @scr_str_release(ptr ${head})`);
|
|
1810
|
+
const tail = B.tmp();
|
|
1811
|
+
B.line(`${tail} = call ptr @scr_str_new(ptr ${host.cstr("')")}, i64 2)`);
|
|
1812
|
+
const msg = B.tmp();
|
|
1813
|
+
B.line(`${msg} = call ptr @scr_str_concat(ptr ${withKey}, ptr ${tail})`);
|
|
1814
|
+
B.line(`call void @scr_str_release(ptr ${withKey})`);
|
|
1815
|
+
B.line(`call void @scr_str_release(ptr ${tail})`);
|
|
1816
|
+
B.line(`call void @scr_throw_error(i32 1, ptr ${msg}) ; SCR_ERR_TYPE; takes ownership`);
|
|
1817
|
+
B.terminate(`ret ptr null`);
|
|
1818
|
+
B.startBlock(lNext);
|
|
1819
|
+
}
|
|
1820
|
+
// OBJ: the own member (+1) or the undefined singleton.
|
|
1821
|
+
{
|
|
1822
|
+
const isObj = B.tmp();
|
|
1823
|
+
B.line(`${isObj} = icmp eq i32 ${kd}, ${DK.OBJ}`);
|
|
1824
|
+
const lObj = B.newLabel("kg.ob");
|
|
1825
|
+
const lNext = B.newLabel("kg.n");
|
|
1826
|
+
B.condBr(isObj, lObj, lNext);
|
|
1827
|
+
B.startBlock(lObj);
|
|
1828
|
+
host.declare(`declare ptr @scr_dyn_obj_get(ptr, ptr, i64)`);
|
|
1829
|
+
const m = B.tmp();
|
|
1830
|
+
B.line(`${m} = call ptr @scr_dyn_obj_get(ptr %d, ptr ${kParts.data}, i64 ${kParts.len})`);
|
|
1831
|
+
const has = B.tmp();
|
|
1832
|
+
B.line(`${has} = icmp ne ptr ${m}, null`);
|
|
1833
|
+
const u = this.undef(B);
|
|
1834
|
+
const sel = B.tmp();
|
|
1835
|
+
B.line(`${sel} = select i1 ${has}, ptr ${m}, ptr ${u}`);
|
|
1836
|
+
const r = this.retainDyn(B, sel);
|
|
1837
|
+
B.terminate(`ret ptr ${r}`);
|
|
1838
|
+
B.startBlock(lNext);
|
|
1839
|
+
}
|
|
1840
|
+
// HANDLE: the tag's modeled properties through the installed ops.
|
|
1841
|
+
{
|
|
1842
|
+
const isH = B.tmp();
|
|
1843
|
+
B.line(`${isH} = icmp eq i32 ${kd}, ${DK.HANDLE}`);
|
|
1844
|
+
const lH = B.newLabel("kg.h");
|
|
1845
|
+
const lNext = B.newLabel("kg.n");
|
|
1846
|
+
B.condBr(isH, lH, lNext);
|
|
1847
|
+
B.startBlock(lH);
|
|
1848
|
+
host.declare(`declare ptr @scr_dyn_handle_key_get(ptr, ptr)`);
|
|
1849
|
+
const r = B.tmp();
|
|
1850
|
+
B.line(`${r} = call ptr @scr_dyn_handle_key_get(ptr %d, ptr %k)`);
|
|
1851
|
+
B.terminate(`ret ptr ${r}`);
|
|
1852
|
+
B.startBlock(lNext);
|
|
1853
|
+
}
|
|
1854
|
+
// The canonical-index parse, shared by BYTES/ARR/STR: digits only,
|
|
1855
|
+
// no leading zero. Leaves (digits, idx).
|
|
1856
|
+
const parseIndex = () => {
|
|
1857
|
+
const digitsSlot = B.slot();
|
|
1858
|
+
const idxSlot = B.slot();
|
|
1859
|
+
B.entryAllocas.push(`${digitsSlot} = alloca i1`, `${idxSlot} = alloca i64`);
|
|
1860
|
+
B.line(`store i1 false, ptr ${digitsSlot}`);
|
|
1861
|
+
B.line(`store i64 0, ptr ${idxSlot}`);
|
|
1862
|
+
// k->len > 0 && !(k->len > 1 && k->data[0] == '0')
|
|
1863
|
+
const nz = B.tmp();
|
|
1864
|
+
B.line(`${nz} = icmp ugt i64 ${kParts.len}, 0`);
|
|
1865
|
+
const lGo = B.newLabel("kg.ix");
|
|
1866
|
+
const lOut = B.newLabel("kg.io");
|
|
1867
|
+
B.condBr(nz, lGo, lOut);
|
|
1868
|
+
B.startBlock(lGo);
|
|
1869
|
+
const multi = B.tmp();
|
|
1870
|
+
B.line(`${multi} = icmp ugt i64 ${kParts.len}, 1`);
|
|
1871
|
+
const c0 = B.tmp();
|
|
1872
|
+
B.line(`${c0} = load i8, ptr ${kParts.data}`);
|
|
1873
|
+
const isZero = B.tmp();
|
|
1874
|
+
B.line(`${isZero} = icmp eq i8 ${c0}, 48`);
|
|
1875
|
+
const leading = B.tmp();
|
|
1876
|
+
B.line(`${leading} = and i1 ${multi}, ${isZero}`);
|
|
1877
|
+
const lParse = B.newLabel("kg.ip");
|
|
1878
|
+
B.condBr(leading, lOut, lParse);
|
|
1879
|
+
B.startBlock(lParse);
|
|
1880
|
+
B.line(`store i1 true, ptr ${digitsSlot}`);
|
|
1881
|
+
this.i64Loop(B, "kg.id", kParts.len, (i) => {
|
|
1882
|
+
const cp = B.tmp();
|
|
1883
|
+
const c = B.tmp();
|
|
1884
|
+
B.line(`${cp} = getelementptr inbounds i8, ptr ${kParts.data}, i64 ${i}`);
|
|
1885
|
+
B.line(`${c} = load i8, ptr ${cp}`);
|
|
1886
|
+
const lt0 = B.tmp();
|
|
1887
|
+
const gt9 = B.tmp();
|
|
1888
|
+
B.line(`${lt0} = icmp ult i8 ${c}, 48`);
|
|
1889
|
+
B.line(`${gt9} = icmp ugt i8 ${c}, 57`);
|
|
1890
|
+
const cur = B.tmp();
|
|
1891
|
+
B.line(`${cur} = load i64, ptr ${idxSlot}`);
|
|
1892
|
+
const over = B.tmp();
|
|
1893
|
+
B.line(`${over} = icmp ugt i64 ${cur}, ${IDX_MAX_DIV10}`);
|
|
1894
|
+
const bad0 = B.tmp();
|
|
1895
|
+
const bad = B.tmp();
|
|
1896
|
+
B.line(`${bad0} = or i1 ${lt0}, ${gt9}`);
|
|
1897
|
+
B.line(`${bad} = or i1 ${bad0}, ${over}`);
|
|
1898
|
+
const lBad = B.newLabel("kg.ib");
|
|
1899
|
+
const lStep = B.newLabel("kg.is");
|
|
1900
|
+
B.condBr(bad, lBad, lStep);
|
|
1901
|
+
B.startBlock(lBad);
|
|
1902
|
+
B.line(`store i1 false, ptr ${digitsSlot}`);
|
|
1903
|
+
B.br(lOut);
|
|
1904
|
+
B.startBlock(lStep);
|
|
1905
|
+
const ten = B.tmp();
|
|
1906
|
+
const digit = B.tmp();
|
|
1907
|
+
const digit64 = B.tmp();
|
|
1908
|
+
const nx = B.tmp();
|
|
1909
|
+
B.line(`${ten} = mul i64 ${cur}, 10`);
|
|
1910
|
+
B.line(`${digit} = sub i8 ${c}, 48`);
|
|
1911
|
+
B.line(`${digit64} = zext i8 ${digit} to i64`);
|
|
1912
|
+
B.line(`${nx} = add i64 ${ten}, ${digit64}`);
|
|
1913
|
+
B.line(`store i64 ${nx}, ptr ${idxSlot}`);
|
|
1914
|
+
});
|
|
1915
|
+
B.br(lOut);
|
|
1916
|
+
B.startBlock(lOut);
|
|
1917
|
+
const digits = B.tmp();
|
|
1918
|
+
const idx = B.tmp();
|
|
1919
|
+
B.line(`${digits} = load i1, ptr ${digitsSlot}`);
|
|
1920
|
+
B.line(`${idx} = load i64, ptr ${idxSlot}`);
|
|
1921
|
+
return { digits, idx };
|
|
1922
|
+
};
|
|
1923
|
+
const isLength = () => {
|
|
1924
|
+
host.declare(`declare i32 @memcmp(ptr, ptr, i64)`);
|
|
1925
|
+
const slot = B.slot();
|
|
1926
|
+
B.entryAllocas.push(`${slot} = alloca i1`);
|
|
1927
|
+
B.line(`store i1 false, ptr ${slot}`);
|
|
1928
|
+
const len6 = B.tmp();
|
|
1929
|
+
B.line(`${len6} = icmp eq i64 ${kParts.len}, 6`);
|
|
1930
|
+
const lCmp = B.newLabel("kg.l6");
|
|
1931
|
+
const lj = B.newLabel("kg.lj");
|
|
1932
|
+
B.condBr(len6, lCmp, lj);
|
|
1933
|
+
B.startBlock(lCmp);
|
|
1934
|
+
const c = B.tmp();
|
|
1935
|
+
const same = B.tmp();
|
|
1936
|
+
B.line(`${c} = call i32 @memcmp(ptr ${kParts.data}, ptr ${host.cstr("length")}, i64 6)`);
|
|
1937
|
+
B.line(`${same} = icmp eq i32 ${c}, 0`);
|
|
1938
|
+
B.line(`store i1 ${same}, ptr ${slot}`);
|
|
1939
|
+
B.br(lj);
|
|
1940
|
+
B.startBlock(lj);
|
|
1941
|
+
const out = B.tmp();
|
|
1942
|
+
B.line(`${out} = load i1, ptr ${slot}`);
|
|
1943
|
+
return out;
|
|
1944
|
+
};
|
|
1945
|
+
// Parse once up front (pure), branch per kind below — C parses per
|
|
1946
|
+
// branch, but the computation is effect-free and identical.
|
|
1947
|
+
const lenHit = isLength();
|
|
1948
|
+
const { digits, idx } = parseIndex();
|
|
1949
|
+
// BYTES: .length and canonical-index byte reads answer like Node.
|
|
1950
|
+
{
|
|
1951
|
+
const isB = B.tmp();
|
|
1952
|
+
B.line(`${isB} = icmp eq i32 ${kd}, ${DK.BYTES}`);
|
|
1953
|
+
const lB = B.newLabel("kg.by");
|
|
1954
|
+
const lNext = B.newLabel("kg.n");
|
|
1955
|
+
B.condBr(isB, lB, lNext);
|
|
1956
|
+
B.startBlock(lB);
|
|
1957
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
1958
|
+
const bts = this.payloadOf(B, "%d", "ptr");
|
|
1959
|
+
const blenp = B.tmp();
|
|
1960
|
+
const blen = B.tmp();
|
|
1961
|
+
B.line(`${blenp} = getelementptr inbounds i8, ptr ${bts}, i64 8 ; ->len`);
|
|
1962
|
+
B.line(`${blen} = load i64, ptr ${blenp}`);
|
|
1963
|
+
const lLen = B.newLabel("kg.bl");
|
|
1964
|
+
const lIdx = B.newLabel("kg.bi");
|
|
1965
|
+
B.condBr(lenHit, lLen, lIdx);
|
|
1966
|
+
B.startBlock(lLen);
|
|
1967
|
+
const lenD = B.tmp();
|
|
1968
|
+
const r0 = B.tmp();
|
|
1969
|
+
B.line(`${lenD} = uitofp i64 ${blen} to double`);
|
|
1970
|
+
B.line(`${r0} = call ptr @scr_dyn_new_num(double ${lenD})`);
|
|
1971
|
+
B.terminate(`ret ptr ${r0}`);
|
|
1972
|
+
B.startBlock(lIdx);
|
|
1973
|
+
const inRange = B.tmp();
|
|
1974
|
+
const hit = B.tmp();
|
|
1975
|
+
B.line(`${inRange} = icmp ult i64 ${idx}, ${blen}`);
|
|
1976
|
+
B.line(`${hit} = and i1 ${digits}, ${inRange}`);
|
|
1977
|
+
const lHit = B.newLabel("kg.bh");
|
|
1978
|
+
const lMiss = B.newLabel("kg.bm");
|
|
1979
|
+
B.condBr(hit, lHit, lMiss);
|
|
1980
|
+
B.startBlock(lHit);
|
|
1981
|
+
const bdatap = B.tmp();
|
|
1982
|
+
const bdata = B.tmp();
|
|
1983
|
+
B.line(`${bdatap} = getelementptr inbounds i8, ptr ${bts}, i64 24 ; ->data`);
|
|
1984
|
+
B.line(`${bdata} = load ptr, ptr ${bdatap}`);
|
|
1985
|
+
const bp = B.tmp();
|
|
1986
|
+
const bv = B.tmp();
|
|
1987
|
+
const bd = B.tmp();
|
|
1988
|
+
const r1 = B.tmp();
|
|
1989
|
+
B.line(`${bp} = getelementptr inbounds i8, ptr ${bdata}, i64 ${idx}`);
|
|
1990
|
+
B.line(`${bv} = load i8, ptr ${bp}`);
|
|
1991
|
+
B.line(`${bd} = uitofp i8 ${bv} to double`);
|
|
1992
|
+
B.line(`${r1} = call ptr @scr_dyn_new_num(double ${bd})`);
|
|
1993
|
+
B.terminate(`ret ptr ${r1}`);
|
|
1994
|
+
B.startBlock(lMiss);
|
|
1995
|
+
retainUndef();
|
|
1996
|
+
B.startBlock(lNext);
|
|
1997
|
+
}
|
|
1998
|
+
// FUNC: own props (defineProperties writes), then name/length.
|
|
1999
|
+
{
|
|
2000
|
+
const isF = B.tmp();
|
|
2001
|
+
B.line(`${isF} = icmp eq i32 ${kd}, ${DK.FUNC}`);
|
|
2002
|
+
const lF = B.newLabel("kg.fn");
|
|
2003
|
+
const lNext = B.newLabel("kg.n");
|
|
2004
|
+
B.condBr(isF, lF, lNext);
|
|
2005
|
+
B.startBlock(lF);
|
|
2006
|
+
host.declare(`declare ptr @scr_dyn_fn_get(ptr, ptr, i64)`);
|
|
2007
|
+
const m = B.tmp();
|
|
2008
|
+
B.line(`${m} = call ptr @scr_dyn_fn_get(ptr %d, ptr ${kParts.data}, i64 ${kParts.len})`);
|
|
2009
|
+
const has = B.tmp();
|
|
2010
|
+
B.line(`${has} = icmp ne ptr ${m}, null`);
|
|
2011
|
+
const lHit = B.newLabel("kg.fh");
|
|
2012
|
+
const lMiss = B.newLabel("kg.fm");
|
|
2013
|
+
B.condBr(has, lHit, lMiss);
|
|
2014
|
+
B.startBlock(lHit);
|
|
2015
|
+
B.terminate(`ret ptr ${m}`);
|
|
2016
|
+
B.startBlock(lMiss);
|
|
2017
|
+
retainUndef();
|
|
2018
|
+
B.startBlock(lNext);
|
|
2019
|
+
}
|
|
2020
|
+
// ARR / STR: length + canonical-index element/char reads.
|
|
2021
|
+
{
|
|
2022
|
+
const isA = B.tmp();
|
|
2023
|
+
const isS = B.tmp();
|
|
2024
|
+
const either = B.tmp();
|
|
2025
|
+
B.line(`${isA} = icmp eq i32 ${kd}, ${DK.ARR}`);
|
|
2026
|
+
B.line(`${isS} = icmp eq i32 ${kd}, ${DK.STR}`);
|
|
2027
|
+
B.line(`${either} = or i1 ${isA}, ${isS}`);
|
|
2028
|
+
const lAS = B.newLabel("kg.as");
|
|
2029
|
+
const lNext = B.newLabel("kg.n");
|
|
2030
|
+
B.condBr(either, lAS, lNext);
|
|
2031
|
+
B.startBlock(lAS);
|
|
2032
|
+
host.declare(`declare double @scr_str_utf16_len(ptr)`);
|
|
2033
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
2034
|
+
const lLen = B.newLabel("kg.al");
|
|
2035
|
+
const lIdx = B.newLabel("kg.ai");
|
|
2036
|
+
B.condBr(lenHit, lLen, lIdx);
|
|
2037
|
+
B.startBlock(lLen);
|
|
2038
|
+
{
|
|
2039
|
+
const lArr = B.newLabel("kg.aa");
|
|
2040
|
+
const lStr = B.newLabel("kg.asr");
|
|
2041
|
+
B.condBr(isA, lArr, lStr);
|
|
2042
|
+
B.startBlock(lArr);
|
|
2043
|
+
const n = this.lenOf(B, "%d");
|
|
2044
|
+
const nd = B.tmp();
|
|
2045
|
+
const r = B.tmp();
|
|
2046
|
+
B.line(`${nd} = uitofp i64 ${n} to double`);
|
|
2047
|
+
B.line(`${r} = call ptr @scr_dyn_new_num(double ${nd})`);
|
|
2048
|
+
B.terminate(`ret ptr ${r}`);
|
|
2049
|
+
B.startBlock(lStr);
|
|
2050
|
+
const s = this.payloadOf(B, "%d", "ptr");
|
|
2051
|
+
const n2 = B.tmp();
|
|
2052
|
+
const r2 = B.tmp();
|
|
2053
|
+
B.line(`${n2} = call double @scr_str_utf16_len(ptr ${s})`);
|
|
2054
|
+
B.line(`${r2} = call ptr @scr_dyn_new_num(double ${n2})`);
|
|
2055
|
+
B.terminate(`ret ptr ${r2}`);
|
|
2056
|
+
}
|
|
2057
|
+
B.startBlock(lIdx);
|
|
2058
|
+
{
|
|
2059
|
+
const lTry = B.newLabel("kg.at");
|
|
2060
|
+
const lMiss = B.newLabel("kg.am");
|
|
2061
|
+
B.condBr(digits, lTry, lMiss);
|
|
2062
|
+
B.startBlock(lTry);
|
|
2063
|
+
const lArr = B.newLabel("kg.ae");
|
|
2064
|
+
const lStr = B.newLabel("kg.ac");
|
|
2065
|
+
B.condBr(isA, lArr, lStr);
|
|
2066
|
+
B.startBlock(lArr);
|
|
2067
|
+
const n = this.lenOf(B, "%d");
|
|
2068
|
+
const inR = B.tmp();
|
|
2069
|
+
B.line(`${inR} = icmp ult i64 ${idx}, ${n}`);
|
|
2070
|
+
const lHit = B.newLabel("kg.ah");
|
|
2071
|
+
B.condBr(inR, lHit, lMiss);
|
|
2072
|
+
B.startBlock(lHit);
|
|
2073
|
+
const items = this.itemsOf(B, "%d");
|
|
2074
|
+
const e = this.itemAt(B, items, idx);
|
|
2075
|
+
const r = this.retainDyn(B, e);
|
|
2076
|
+
B.terminate(`ret ptr ${r}`);
|
|
2077
|
+
B.startBlock(lStr);
|
|
2078
|
+
host.declare(`declare ptr @scr_str_char_at(ptr, double)`);
|
|
2079
|
+
host.declare(`declare ptr @scr_dyn_new_str(ptr)`);
|
|
2080
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
2081
|
+
const s = this.payloadOf(B, "%d", "ptr");
|
|
2082
|
+
const n2 = B.tmp();
|
|
2083
|
+
B.line(`${n2} = call double @scr_str_utf16_len(ptr ${s})`);
|
|
2084
|
+
const idxD = B.tmp();
|
|
2085
|
+
B.line(`${idxD} = uitofp i64 ${idx} to double`);
|
|
2086
|
+
const inR2 = B.tmp();
|
|
2087
|
+
B.line(`${inR2} = fcmp olt double ${idxD}, ${n2}`);
|
|
2088
|
+
const lHit2 = B.newLabel("kg.ash");
|
|
2089
|
+
B.condBr(inR2, lHit2, lMiss);
|
|
2090
|
+
B.startBlock(lHit2);
|
|
2091
|
+
const ch = B.tmp();
|
|
2092
|
+
B.line(`${ch} = call ptr @scr_str_char_at(ptr ${s}, double ${idxD})`);
|
|
2093
|
+
const r2 = B.tmp();
|
|
2094
|
+
B.line(`${r2} = call ptr @scr_dyn_new_str(ptr ${ch})`);
|
|
2095
|
+
B.line(`call void @scr_str_release(ptr ${ch})`);
|
|
2096
|
+
B.terminate(`ret ptr ${r2}`);
|
|
2097
|
+
B.startBlock(lMiss);
|
|
2098
|
+
retainUndef();
|
|
2099
|
+
}
|
|
2100
|
+
B.startBlock(lNext);
|
|
2101
|
+
}
|
|
2102
|
+
retainUndef();
|
|
2103
|
+
this.defs.push(`define internal ptr @${name}(ptr %d, ptr %k, i1 zeroext %opt) ${FN_ATTRS} { ; d[k] on dyn`, B.render(), `}`, ``);
|
|
2104
|
+
return name;
|
|
2105
|
+
}
|
|
2106
|
+
/* ── destructuring RequireObjectCoercible (ported) ─────────────────── */
|
|
2107
|
+
dynDestrCheckHelper() {
|
|
2108
|
+
const memoKey = "%dynDestrCheck";
|
|
2109
|
+
const existing = this.dynBuilders.get(memoKey);
|
|
2110
|
+
if (existing)
|
|
2111
|
+
return existing;
|
|
2112
|
+
const name = "sc_dyn_destr_check";
|
|
2113
|
+
this.dynBuilders.set(memoKey, name);
|
|
2114
|
+
const host = this.host;
|
|
2115
|
+
host.declare(`declare void @scr_jb_init(ptr)`);
|
|
2116
|
+
host.declare(`declare ptr @scr_jb_finish(ptr)`);
|
|
2117
|
+
host.declare(`declare void @scr_jb_puts(ptr, ptr)`);
|
|
2118
|
+
host.declare(`declare void @scr_throw_error(i32, ptr)`);
|
|
2119
|
+
const B = new BlockBuilder();
|
|
2120
|
+
const kd = this.kindOf(B, "%d");
|
|
2121
|
+
const isU = B.tmp();
|
|
2122
|
+
const isN = B.tmp();
|
|
2123
|
+
const unit = B.tmp();
|
|
2124
|
+
B.line(`${isU} = icmp eq i32 ${kd}, ${DK.UNDEF}`);
|
|
2125
|
+
B.line(`${isN} = icmp eq i32 ${kd}, ${DK.NULL}`);
|
|
2126
|
+
B.line(`${unit} = or i1 ${isU}, ${isN}`);
|
|
2127
|
+
const lThrow = B.newLabel("ddc.t");
|
|
2128
|
+
const lOk = B.newLabel("ddc.o");
|
|
2129
|
+
B.condBr(unit, lThrow, lOk);
|
|
2130
|
+
B.startBlock(lOk);
|
|
2131
|
+
B.terminate(`ret void`);
|
|
2132
|
+
B.startBlock(lThrow);
|
|
2133
|
+
const buf = "%ddb";
|
|
2134
|
+
B.entryAllocas.push(`${buf} = alloca %ScrJsonBuf`);
|
|
2135
|
+
B.line(`call void @scr_jb_init(ptr ${buf})`);
|
|
2136
|
+
const hasProp = B.tmp();
|
|
2137
|
+
B.line(`${hasProp} = icmp ne ptr %firstProp, null`);
|
|
2138
|
+
const lProp = B.newLabel("ddc.p");
|
|
2139
|
+
const lBare = B.newLabel("ddc.b");
|
|
2140
|
+
const lTail = B.newLabel("ddc.e");
|
|
2141
|
+
B.condBr(hasProp, lProp, lBare);
|
|
2142
|
+
B.startBlock(lProp);
|
|
2143
|
+
this.puts(B, buf, "Cannot destructure property '");
|
|
2144
|
+
B.line(`call void @scr_jb_puts(ptr ${buf}, ptr %firstProp)`);
|
|
2145
|
+
this.puts(B, buf, "' of '");
|
|
2146
|
+
B.line(`call void @scr_jb_puts(ptr ${buf}, ptr %spell)`);
|
|
2147
|
+
this.puts(B, buf, "' as it is ");
|
|
2148
|
+
B.br(lTail);
|
|
2149
|
+
B.startBlock(lBare);
|
|
2150
|
+
this.puts(B, buf, "Cannot destructure '");
|
|
2151
|
+
B.line(`call void @scr_jb_puts(ptr ${buf}, ptr %spell)`);
|
|
2152
|
+
this.puts(B, buf, "' as it is ");
|
|
2153
|
+
B.br(lTail);
|
|
2154
|
+
B.startBlock(lTail);
|
|
2155
|
+
const tail = B.tmp();
|
|
2156
|
+
B.line(`${tail} = select i1 ${isU}, ptr ${host.cstr("undefined.")}, ptr ${host.cstr("null.")}`);
|
|
2157
|
+
B.line(`call void @scr_jb_puts(ptr ${buf}, ptr ${tail})`);
|
|
2158
|
+
const msg = B.tmp();
|
|
2159
|
+
B.line(`${msg} = call ptr @scr_jb_finish(ptr ${buf})`);
|
|
2160
|
+
B.line(`call void @scr_throw_error(i32 1, ptr ${msg}) ; SCR_ERR_TYPE`);
|
|
2161
|
+
B.terminate(`ret void`);
|
|
2162
|
+
this.defs.push(`define internal void @${name}(ptr %d, ptr %spell, ptr %firstProp) ${FN_ATTRS} { ; destructuring RequireObjectCoercible`, B.render(), `}`, ``);
|
|
2163
|
+
return name;
|
|
2164
|
+
}
|
|
2165
|
+
/* ── GetIterator + first-N steps (dynIterNHelper, ported) ──────────── */
|
|
2166
|
+
dynIterNHelper() {
|
|
2167
|
+
const memoKey = "%dynIterN";
|
|
2168
|
+
const existing = this.dynBuilders.get(memoKey);
|
|
2169
|
+
if (existing)
|
|
2170
|
+
return existing;
|
|
2171
|
+
const name = "sc_dyn_iter_n";
|
|
2172
|
+
this.dynBuilders.set(memoKey, name);
|
|
2173
|
+
const host = this.host;
|
|
2174
|
+
host.declare(`declare void @scr_jb_init(ptr)`);
|
|
2175
|
+
host.declare(`declare ptr @scr_jb_finish(ptr)`);
|
|
2176
|
+
host.declare(`declare void @scr_jb_puts(ptr, ptr)`);
|
|
2177
|
+
host.declare(`declare void @scr_throw_error(i32, ptr)`);
|
|
2178
|
+
host.declare(`declare ptr @scr_dyn_new_arr()`);
|
|
2179
|
+
host.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
|
|
2180
|
+
host.declare(`declare ptr @scr_dyn_new_num(double)`);
|
|
2181
|
+
host.declare(`declare ptr @scr_f64_to_scrstr(double)`);
|
|
2182
|
+
host.declare(`declare void @scr_str_release(ptr)`);
|
|
2183
|
+
const B = new BlockBuilder();
|
|
2184
|
+
const kd = this.kindOf(B, "%d");
|
|
2185
|
+
const okA = B.tmp();
|
|
2186
|
+
const okS = B.tmp();
|
|
2187
|
+
const okB = B.tmp();
|
|
2188
|
+
const ok01 = B.tmp();
|
|
2189
|
+
const ok = B.tmp();
|
|
2190
|
+
B.line(`${okA} = icmp eq i32 ${kd}, ${DK.ARR}`);
|
|
2191
|
+
B.line(`${okS} = icmp eq i32 ${kd}, ${DK.STR}`);
|
|
2192
|
+
B.line(`${okB} = icmp eq i32 ${kd}, ${DK.BYTES}`);
|
|
2193
|
+
B.line(`${ok01} = or i1 ${okA}, ${okS}`);
|
|
2194
|
+
B.line(`${ok} = or i1 ${ok01}, ${okB}`);
|
|
2195
|
+
const lGo = B.newLabel("din.g");
|
|
2196
|
+
const lThrow = B.newLabel("din.t");
|
|
2197
|
+
B.condBr(ok, lGo, lThrow);
|
|
2198
|
+
// V8's exact not-iterable TypeError.
|
|
2199
|
+
B.startBlock(lThrow);
|
|
2200
|
+
const buf = "%dib";
|
|
2201
|
+
B.entryAllocas.push(`${buf} = alloca %ScrJsonBuf`);
|
|
2202
|
+
B.line(`call void @scr_jb_init(ptr ${buf})`);
|
|
2203
|
+
const lU = B.newLabel("din.u");
|
|
2204
|
+
const lN = B.newLabel("din.n");
|
|
2205
|
+
const lB2 = B.newLabel("din.b");
|
|
2206
|
+
const lNum = B.newLabel("din.m");
|
|
2207
|
+
const lF = B.newLabel("din.f");
|
|
2208
|
+
const lDef = B.newLabel("din.d");
|
|
2209
|
+
const lTail = B.newLabel("din.e");
|
|
2210
|
+
B.terminate(`switch i32 ${kd}, label %${lDef} [ i32 ${DK.UNDEF}, label %${lU} i32 ${DK.NULL}, label %${lN} i32 ${DK.BOOL}, label %${lB2} i32 ${DK.NUM}, label %${lNum} i32 ${DK.FUNC}, label %${lF} ]`);
|
|
2211
|
+
B.startBlock(lU);
|
|
2212
|
+
this.puts(B, buf, "undefined");
|
|
2213
|
+
B.br(lTail);
|
|
2214
|
+
B.startBlock(lN);
|
|
2215
|
+
this.puts(B, buf, "object null");
|
|
2216
|
+
B.br(lTail);
|
|
2217
|
+
B.startBlock(lB2);
|
|
2218
|
+
{
|
|
2219
|
+
const bv = this.boolOf(B, "%d");
|
|
2220
|
+
const s = B.tmp();
|
|
2221
|
+
B.line(`${s} = select i1 ${bv}, ptr ${host.cstr("boolean true")}, ptr ${host.cstr("boolean false")}`);
|
|
2222
|
+
B.line(`call void @scr_jb_puts(ptr ${buf}, ptr ${s})`);
|
|
2223
|
+
B.br(lTail);
|
|
2224
|
+
}
|
|
2225
|
+
B.startBlock(lNum);
|
|
2226
|
+
{
|
|
2227
|
+
this.puts(B, buf, "number ");
|
|
2228
|
+
const x = this.payloadOf(B, "%d", "double");
|
|
2229
|
+
const s = B.tmp();
|
|
2230
|
+
B.line(`${s} = call ptr @scr_f64_to_scrstr(double ${x})`);
|
|
2231
|
+
this.putScrStr(B, buf, s);
|
|
2232
|
+
B.line(`call void @scr_str_release(ptr ${s})`);
|
|
2233
|
+
B.br(lTail);
|
|
2234
|
+
}
|
|
2235
|
+
B.startBlock(lF);
|
|
2236
|
+
this.puts(B, buf, "function");
|
|
2237
|
+
B.br(lTail);
|
|
2238
|
+
B.startBlock(lDef);
|
|
2239
|
+
this.puts(B, buf, "object");
|
|
2240
|
+
B.br(lTail);
|
|
2241
|
+
B.startBlock(lTail);
|
|
2242
|
+
this.puts(B, buf, " is not iterable (cannot read property Symbol(Symbol.iterator))");
|
|
2243
|
+
const msg = B.tmp();
|
|
2244
|
+
B.line(`${msg} = call ptr @scr_jb_finish(ptr ${buf})`);
|
|
2245
|
+
B.line(`call void @scr_throw_error(i32 1, ptr ${msg}) ; SCR_ERR_TYPE`);
|
|
2246
|
+
B.terminate(`ret ptr null`);
|
|
2247
|
+
B.startBlock(lGo);
|
|
2248
|
+
const out = B.tmp();
|
|
2249
|
+
B.line(`${out} = call ptr @scr_dyn_new_arr()`);
|
|
2250
|
+
this.i64Loop(B, "din", "%n", (i) => {
|
|
2251
|
+
const itemSlot = B.slot();
|
|
2252
|
+
B.entryAllocas.push(`${itemSlot} = alloca ptr`);
|
|
2253
|
+
const lArr = B.newLabel("din.ia");
|
|
2254
|
+
const lBy = B.newLabel("din.ib");
|
|
2255
|
+
const lStr = B.newLabel("din.is");
|
|
2256
|
+
const lPush = B.newLabel("din.ip");
|
|
2257
|
+
const lNotA = B.newLabel("din.na");
|
|
2258
|
+
B.condBr(okA, lArr, lNotA);
|
|
2259
|
+
B.startBlock(lNotA);
|
|
2260
|
+
B.condBr(okB, lBy, lStr);
|
|
2261
|
+
// ARR: item = i < len ? retain(items[i]) : retain(undefined)
|
|
2262
|
+
B.startBlock(lArr);
|
|
2263
|
+
{
|
|
2264
|
+
const n = this.lenOf(B, "%d");
|
|
2265
|
+
const inR = B.tmp();
|
|
2266
|
+
B.line(`${inR} = icmp ult i64 ${i}, ${n}`);
|
|
2267
|
+
const lHit = B.newLabel("din.ah");
|
|
2268
|
+
const lMiss = B.newLabel("din.am");
|
|
2269
|
+
B.condBr(inR, lHit, lMiss);
|
|
2270
|
+
B.startBlock(lHit);
|
|
2271
|
+
const items = this.itemsOf(B, "%d");
|
|
2272
|
+
const e = this.itemAt(B, items, i);
|
|
2273
|
+
const r = this.retainDyn(B, e);
|
|
2274
|
+
B.line(`store ptr ${r}, ptr ${itemSlot}`);
|
|
2275
|
+
B.br(lPush);
|
|
2276
|
+
B.startBlock(lMiss);
|
|
2277
|
+
const u = this.undef(B);
|
|
2278
|
+
const r2 = this.retainDyn(B, u);
|
|
2279
|
+
B.line(`store ptr ${r2}, ptr ${itemSlot}`);
|
|
2280
|
+
B.br(lPush);
|
|
2281
|
+
}
|
|
2282
|
+
// BYTES: by byte.
|
|
2283
|
+
B.startBlock(lBy);
|
|
2284
|
+
{
|
|
2285
|
+
const bts = this.payloadOf(B, "%d", "ptr");
|
|
2286
|
+
const blenp = B.tmp();
|
|
2287
|
+
const blen = B.tmp();
|
|
2288
|
+
B.line(`${blenp} = getelementptr inbounds i8, ptr ${bts}, i64 8 ; ->len`);
|
|
2289
|
+
B.line(`${blen} = load i64, ptr ${blenp}`);
|
|
2290
|
+
const inR = B.tmp();
|
|
2291
|
+
B.line(`${inR} = icmp ult i64 ${i}, ${blen}`);
|
|
2292
|
+
const lHit = B.newLabel("din.bh");
|
|
2293
|
+
const lMiss = B.newLabel("din.bm");
|
|
2294
|
+
B.condBr(inR, lHit, lMiss);
|
|
2295
|
+
B.startBlock(lHit);
|
|
2296
|
+
const bdatap = B.tmp();
|
|
2297
|
+
const bdata = B.tmp();
|
|
2298
|
+
B.line(`${bdatap} = getelementptr inbounds i8, ptr ${bts}, i64 24 ; ->data`);
|
|
2299
|
+
B.line(`${bdata} = load ptr, ptr ${bdatap}`);
|
|
2300
|
+
const bp = B.tmp();
|
|
2301
|
+
const bv = B.tmp();
|
|
2302
|
+
const bd = B.tmp();
|
|
2303
|
+
const r = B.tmp();
|
|
2304
|
+
B.line(`${bp} = getelementptr inbounds i8, ptr ${bdata}, i64 ${i}`);
|
|
2305
|
+
B.line(`${bv} = load i8, ptr ${bp}`);
|
|
2306
|
+
B.line(`${bd} = uitofp i8 ${bv} to double`);
|
|
2307
|
+
B.line(`${r} = call ptr @scr_dyn_new_num(double ${bd})`);
|
|
2308
|
+
B.line(`store ptr ${r}, ptr ${itemSlot}`);
|
|
2309
|
+
B.br(lPush);
|
|
2310
|
+
B.startBlock(lMiss);
|
|
2311
|
+
const u = this.undef(B);
|
|
2312
|
+
const r2 = this.retainDyn(B, u);
|
|
2313
|
+
B.line(`store ptr ${r2}, ptr ${itemSlot}`);
|
|
2314
|
+
B.br(lPush);
|
|
2315
|
+
}
|
|
2316
|
+
// STR: whole code POINTS (the string iterator, not charAt).
|
|
2317
|
+
B.startBlock(lStr);
|
|
2318
|
+
{
|
|
2319
|
+
host.declare(`declare double @scr_str_utf16_len(ptr)`);
|
|
2320
|
+
host.declare(`declare ptr @scr_str_cp_at(ptr, double)`);
|
|
2321
|
+
host.declare(`declare ptr @scr_dyn_new_str(ptr)`);
|
|
2322
|
+
const s = this.payloadOf(B, "%d", "ptr");
|
|
2323
|
+
const len = B.tmp();
|
|
2324
|
+
B.line(`${len} = call double @scr_str_utf16_len(ptr ${s})`);
|
|
2325
|
+
const atSlot = B.slot();
|
|
2326
|
+
const stepSlot = B.slot();
|
|
2327
|
+
B.entryAllocas.push(`${atSlot} = alloca double`, `${stepSlot} = alloca i64`);
|
|
2328
|
+
B.line(`store double ${f64Lit(0)}, ptr ${atSlot}`);
|
|
2329
|
+
B.line(`store i64 0, ptr ${stepSlot}`);
|
|
2330
|
+
const lc = B.newLabel("din.sc");
|
|
2331
|
+
const lb = B.newLabel("din.sb");
|
|
2332
|
+
const le = B.newLabel("din.se");
|
|
2333
|
+
B.br(lc);
|
|
2334
|
+
B.startBlock(lc);
|
|
2335
|
+
const st = B.tmp();
|
|
2336
|
+
const at = B.tmp();
|
|
2337
|
+
const c1 = B.tmp();
|
|
2338
|
+
const c2 = B.tmp();
|
|
2339
|
+
const cont = B.tmp();
|
|
2340
|
+
B.line(`${st} = load i64, ptr ${stepSlot}`);
|
|
2341
|
+
B.line(`${at} = load double, ptr ${atSlot}`);
|
|
2342
|
+
B.line(`${c1} = icmp ult i64 ${st}, ${i}`);
|
|
2343
|
+
B.line(`${c2} = fcmp olt double ${at}, ${len}`);
|
|
2344
|
+
B.line(`${cont} = and i1 ${c1}, ${c2}`);
|
|
2345
|
+
B.condBr(cont, lb, le);
|
|
2346
|
+
B.startBlock(lb);
|
|
2347
|
+
const cp = B.tmp();
|
|
2348
|
+
const cpl = B.tmp();
|
|
2349
|
+
const at2 = B.tmp();
|
|
2350
|
+
B.line(`${cp} = call ptr @scr_str_cp_at(ptr ${s}, double ${at})`);
|
|
2351
|
+
B.line(`${cpl} = call double @scr_str_utf16_len(ptr ${cp})`);
|
|
2352
|
+
B.line(`${at2} = fadd double ${at}, ${cpl}`);
|
|
2353
|
+
B.line(`store double ${at2}, ptr ${atSlot}`);
|
|
2354
|
+
B.line(`call void @scr_str_release(ptr ${cp})`);
|
|
2355
|
+
const st2 = B.tmp();
|
|
2356
|
+
B.line(`${st2} = add i64 ${st}, 1`);
|
|
2357
|
+
B.line(`store i64 ${st2}, ptr ${stepSlot}`);
|
|
2358
|
+
B.br(lc);
|
|
2359
|
+
B.startBlock(le);
|
|
2360
|
+
const atF = B.tmp();
|
|
2361
|
+
B.line(`${atF} = load double, ptr ${atSlot}`);
|
|
2362
|
+
const inR = B.tmp();
|
|
2363
|
+
B.line(`${inR} = fcmp olt double ${atF}, ${len}`);
|
|
2364
|
+
const lHit = B.newLabel("din.sh");
|
|
2365
|
+
const lMiss = B.newLabel("din.sm");
|
|
2366
|
+
B.condBr(inR, lHit, lMiss);
|
|
2367
|
+
B.startBlock(lHit);
|
|
2368
|
+
const cp2 = B.tmp();
|
|
2369
|
+
const r = B.tmp();
|
|
2370
|
+
B.line(`${cp2} = call ptr @scr_str_cp_at(ptr ${s}, double ${atF})`);
|
|
2371
|
+
B.line(`${r} = call ptr @scr_dyn_new_str(ptr ${cp2})`);
|
|
2372
|
+
B.line(`call void @scr_str_release(ptr ${cp2})`);
|
|
2373
|
+
B.line(`store ptr ${r}, ptr ${itemSlot}`);
|
|
2374
|
+
B.br(lPush);
|
|
2375
|
+
B.startBlock(lMiss);
|
|
2376
|
+
const u = this.undef(B);
|
|
2377
|
+
const r2 = this.retainDyn(B, u);
|
|
2378
|
+
B.line(`store ptr ${r2}, ptr ${itemSlot}`);
|
|
2379
|
+
B.br(lPush);
|
|
2380
|
+
}
|
|
2381
|
+
B.startBlock(lPush);
|
|
2382
|
+
const item = B.tmp();
|
|
2383
|
+
B.line(`${item} = load ptr, ptr ${itemSlot}`);
|
|
2384
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${item}) ; push takes ownership`);
|
|
2385
|
+
});
|
|
2386
|
+
B.terminate(`ret ptr ${out}`);
|
|
2387
|
+
this.defs.push(`define internal ptr @${name}(ptr %d, i64 %n) ${FN_ATTRS} { ; destructuring GetIterator + N steps`, B.render(), `}`, ``);
|
|
2388
|
+
return name;
|
|
2389
|
+
}
|
|
2390
|
+
/* ── the checked-dynamic function boundary (ported) ────────────────── */
|
|
2391
|
+
/** The dyn argument spelling of one static value: dyn passes through
|
|
2392
|
+
* (+1), funcs box anonymously, everything else rides toDyn. `expr` is
|
|
2393
|
+
* BORROWED in every arm. Emits into the given builder. */
|
|
2394
|
+
toDynExpr(B, t, expr) {
|
|
2395
|
+
if (t.kind === "dyn")
|
|
2396
|
+
return this.retainDyn(B, expr);
|
|
2397
|
+
if (t.kind === "func") {
|
|
2398
|
+
const box = this.dynFuncBoxHelper(t);
|
|
2399
|
+
const r = B.tmp();
|
|
2400
|
+
B.line(`${r} = call ptr @${box}(ptr ${expr}, ptr null)`);
|
|
2401
|
+
return r;
|
|
2402
|
+
}
|
|
2403
|
+
const r = B.tmp();
|
|
2404
|
+
B.line(`${r} = call ptr @${this.toDynHelper(t)}(${this.valTy(t)} ${expr})`);
|
|
2405
|
+
return r;
|
|
2406
|
+
}
|
|
2407
|
+
/** The call thunk for one closure signature: validate each dyn arg
|
|
2408
|
+
* into the declared param type, call through the closure, convert the
|
|
2409
|
+
* result back to a DOM value (+1). */
|
|
2410
|
+
dynFuncThunkHelper(t) {
|
|
2411
|
+
const key = typeKey(t);
|
|
2412
|
+
const existing = this.dynFuncThunks.get(key);
|
|
2413
|
+
if (existing)
|
|
2414
|
+
return existing;
|
|
2415
|
+
const name = `sc_dfk_${this.dynFuncThunks.size}`;
|
|
2416
|
+
this.dynFuncThunks.set(key, name);
|
|
2417
|
+
const host = this.host;
|
|
2418
|
+
const B = new BlockBuilder();
|
|
2419
|
+
const argNames = [];
|
|
2420
|
+
t.params.forEach((p, i) => {
|
|
2421
|
+
// JS arity: a missing argument IS the undefined DOM value.
|
|
2422
|
+
const adSlot = B.slot();
|
|
2423
|
+
B.entryAllocas.push(`${adSlot} = alloca ptr`);
|
|
2424
|
+
const has = B.tmp();
|
|
2425
|
+
B.line(`${has} = icmp ult i64 ${i}, %argc`);
|
|
2426
|
+
const lHas = B.newLabel("dfk.h");
|
|
2427
|
+
const lMiss = B.newLabel("dfk.m");
|
|
2428
|
+
const lj = B.newLabel("dfk.j");
|
|
2429
|
+
B.condBr(has, lHas, lMiss);
|
|
2430
|
+
B.startBlock(lHas);
|
|
2431
|
+
const ap = B.tmp();
|
|
2432
|
+
const av = B.tmp();
|
|
2433
|
+
B.line(`${ap} = getelementptr inbounds ptr, ptr %args, i64 ${i}`);
|
|
2434
|
+
B.line(`${av} = load ptr, ptr ${ap}`);
|
|
2435
|
+
B.line(`store ptr ${av}, ptr ${adSlot}`);
|
|
2436
|
+
B.br(lj);
|
|
2437
|
+
B.startBlock(lMiss);
|
|
2438
|
+
const u = this.undef(B);
|
|
2439
|
+
B.line(`store ptr ${u}, ptr ${adSlot}`);
|
|
2440
|
+
B.br(lj);
|
|
2441
|
+
B.startBlock(lj);
|
|
2442
|
+
const ad = B.tmp();
|
|
2443
|
+
B.line(`${ad} = load ptr, ptr ${adSlot}`);
|
|
2444
|
+
if (p.kind === "dyn") {
|
|
2445
|
+
argNames.push(this.retainDyn(B, ad));
|
|
2446
|
+
}
|
|
2447
|
+
else {
|
|
2448
|
+
const pathSlot = B.slot();
|
|
2449
|
+
B.entryAllocas.push(`${pathSlot} = alloca %ScrDynPath`);
|
|
2450
|
+
const pp = B.tmp();
|
|
2451
|
+
const kp2 = B.tmp();
|
|
2452
|
+
const ip = B.tmp();
|
|
2453
|
+
B.line(`${pp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 0`);
|
|
2454
|
+
B.line(`store ptr null, ptr ${pp}`);
|
|
2455
|
+
B.line(`${kp2} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 1`);
|
|
2456
|
+
B.line(`store ptr null, ptr ${kp2}`);
|
|
2457
|
+
B.line(`${ip} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 2`);
|
|
2458
|
+
B.line(`store i64 ${i}, ptr ${ip}`);
|
|
2459
|
+
const a = B.tmp();
|
|
2460
|
+
B.line(`${a} = call ${this.valTy(p)} @${this.dynCheckHelper(p)}(ptr ${ad}, ptr ${pathSlot})`);
|
|
2461
|
+
this.pendingBail(B, "dfk", () => {
|
|
2462
|
+
t.params.slice(0, i).forEach((q, j) => {
|
|
2463
|
+
if (isRefCounted(q))
|
|
2464
|
+
B.line(`call void ${releaseSym(host, q)}(ptr ${argNames[j]})`);
|
|
2465
|
+
});
|
|
2466
|
+
}, "ptr null");
|
|
2467
|
+
argNames.push(a);
|
|
2468
|
+
}
|
|
2469
|
+
});
|
|
2470
|
+
// VARIADIC (rest-marked) signatures: one extra trailing DOM-array
|
|
2471
|
+
// param carries the call's arguments from index params.length on.
|
|
2472
|
+
let rest = null;
|
|
2473
|
+
if (t.rest) {
|
|
2474
|
+
host.declare(`declare ptr @scr_dyn_new_arr()`);
|
|
2475
|
+
host.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
|
|
2476
|
+
rest = B.tmp();
|
|
2477
|
+
B.line(`${rest} = call ptr @scr_dyn_new_arr()`);
|
|
2478
|
+
const riSlot = B.slot();
|
|
2479
|
+
B.entryAllocas.push(`${riSlot} = alloca i64`);
|
|
2480
|
+
B.line(`store i64 ${t.params.length}, ptr ${riSlot}`);
|
|
2481
|
+
const lc = B.newLabel("dfk.rc");
|
|
2482
|
+
const lb = B.newLabel("dfk.rb");
|
|
2483
|
+
const le = B.newLabel("dfk.re");
|
|
2484
|
+
B.br(lc);
|
|
2485
|
+
B.startBlock(lc);
|
|
2486
|
+
const ri = B.tmp();
|
|
2487
|
+
const cont = B.tmp();
|
|
2488
|
+
B.line(`${ri} = load i64, ptr ${riSlot}`);
|
|
2489
|
+
B.line(`${cont} = icmp ult i64 ${ri}, %argc`);
|
|
2490
|
+
B.condBr(cont, lb, le);
|
|
2491
|
+
B.startBlock(lb);
|
|
2492
|
+
const ap = B.tmp();
|
|
2493
|
+
const av = B.tmp();
|
|
2494
|
+
B.line(`${ap} = getelementptr inbounds ptr, ptr %args, i64 ${ri}`);
|
|
2495
|
+
B.line(`${av} = load ptr, ptr ${ap}`);
|
|
2496
|
+
const rv = this.retainDyn(B, av);
|
|
2497
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${rest}, ptr ${rv})`);
|
|
2498
|
+
const ri2 = B.tmp();
|
|
2499
|
+
B.line(`${ri2} = add i64 ${ri}, 1`);
|
|
2500
|
+
B.line(`store i64 ${ri2}, ptr ${riSlot}`);
|
|
2501
|
+
B.br(lc);
|
|
2502
|
+
B.startBlock(le);
|
|
2503
|
+
}
|
|
2504
|
+
// The closure CONSUMES its params (+1 each moved in).
|
|
2505
|
+
const fnp = B.tmp();
|
|
2506
|
+
const fn = B.tmp();
|
|
2507
|
+
B.line(`${fnp} = getelementptr inbounds %ScrClosure, ptr %c, i64 0, i32 1`);
|
|
2508
|
+
B.line(`${fn} = load ptr, ptr ${fnp}`);
|
|
2509
|
+
const retTy = t.ret.kind === "void" ? "void" : this.valTy(t.ret);
|
|
2510
|
+
const callArgs = [
|
|
2511
|
+
`ptr %c`,
|
|
2512
|
+
...t.params.map((p, i) => `${this.valTy(p)} ${argNames[i]}`),
|
|
2513
|
+
...(rest !== null ? [`ptr ${rest}`] : []),
|
|
2514
|
+
].join(", ");
|
|
2515
|
+
if (t.ret.kind === "void") {
|
|
2516
|
+
B.line(`call void ${fn}(${callArgs})`);
|
|
2517
|
+
this.pendingBail(B, "dfkr", () => { }, "ptr null");
|
|
2518
|
+
const u = this.undef(B);
|
|
2519
|
+
const r = this.retainDyn(B, u);
|
|
2520
|
+
B.terminate(`ret ptr ${r}`);
|
|
2521
|
+
}
|
|
2522
|
+
else if (t.ret.kind === "dyn") {
|
|
2523
|
+
const r = B.tmp();
|
|
2524
|
+
B.line(`${r} = call ptr ${fn}(${callArgs})`);
|
|
2525
|
+
this.pendingBail(B, "dfkr", () => { }, "ptr null");
|
|
2526
|
+
B.terminate(`ret ptr ${r}`);
|
|
2527
|
+
}
|
|
2528
|
+
else {
|
|
2529
|
+
const r = B.tmp();
|
|
2530
|
+
B.line(`${r} = call ${retTy} ${fn}(${callArgs})`);
|
|
2531
|
+
this.pendingBail(B, "dfkr", () => { }, "ptr null");
|
|
2532
|
+
const out = this.toDynExpr(B, t.ret, r);
|
|
2533
|
+
if (isRefCounted(t.ret))
|
|
2534
|
+
B.line(`call void ${releaseSym(host, t.ret)}(ptr ${r})`);
|
|
2535
|
+
B.terminate(`ret ptr ${out}`);
|
|
2536
|
+
}
|
|
2537
|
+
this.defs.push(`define internal ptr @${name}(ptr %c, ptr %args, i64 %argc) ${FN_ATTRS} { ; dyn call thunk for ${key}`, B.render(), `}`, ``);
|
|
2538
|
+
return name;
|
|
2539
|
+
}
|
|
2540
|
+
/** The box builder dynFrom emits for one closure signature. */
|
|
2541
|
+
dynFuncBoxHelper(t) {
|
|
2542
|
+
const key = typeKey(t);
|
|
2543
|
+
const existing = this.dynFuncBoxes.get(key);
|
|
2544
|
+
if (existing)
|
|
2545
|
+
return existing;
|
|
2546
|
+
const name = `sc_dfb_${this.dynFuncBoxes.size}`;
|
|
2547
|
+
this.dynFuncBoxes.set(key, name);
|
|
2548
|
+
const host = this.host;
|
|
2549
|
+
const thunk = this.dynFuncThunkHelper(t);
|
|
2550
|
+
host.declare(`declare ptr @scr_closure_retain_v(ptr)`);
|
|
2551
|
+
host.declare(`declare ptr @scr_dyn_new_func(ptr, ptr, i32, ptr, ptr)`);
|
|
2552
|
+
const sigLit = host.cstr(key);
|
|
2553
|
+
this.defs.push(`define internal ptr @${name}(ptr %v, ptr %fname) ${FN_ATTRS} { ; box ${key} into dyn`, `entry:`, ` %c = call ptr @scr_closure_retain_v(ptr %v)`, ` %r = call ptr @scr_dyn_new_func(ptr %c, ptr @${thunk}, i32 ${t.params.length}, ptr ${sigLit}, ptr %fname)`, ` ret ptr %r`, `}`, ``);
|
|
2554
|
+
return name;
|
|
2555
|
+
}
|
|
2556
|
+
/** The adapter closure body for one TARGET signature: caps[0] is an
|
|
2557
|
+
* untraced obj-box owning the dyn function value. */
|
|
2558
|
+
dynFuncAdapterHelper(t) {
|
|
2559
|
+
const key = typeKey(t);
|
|
2560
|
+
const existing = this.dynFuncAdapters.get(key);
|
|
2561
|
+
if (existing)
|
|
2562
|
+
return existing;
|
|
2563
|
+
const name = `sc_dfa_${this.dynFuncAdapters.size}`;
|
|
2564
|
+
this.dynFuncAdapters.set(key, name);
|
|
2565
|
+
const host = this.host;
|
|
2566
|
+
const B = new BlockBuilder();
|
|
2567
|
+
host.declare(`declare ptr @scr_box_get_ref(ptr)`);
|
|
2568
|
+
host.declare(`declare ptr @scr_dyn_call(ptr, ptr, i64, ptr)`);
|
|
2569
|
+
host.declare(`declare void @scr_dyn_release(ptr)`);
|
|
2570
|
+
const retTy = t.ret.kind === "void" ? "void" : this.valTy(t.ret);
|
|
2571
|
+
const dummy = t.ret.kind === "void" ? "void" : retTy === "double" ? `double ${f64Lit(0)}` : retTy === "i1" ? "i1 false" : "ptr null";
|
|
2572
|
+
const capp = B.tmp();
|
|
2573
|
+
const box = B.tmp();
|
|
2574
|
+
B.line(`${capp} = getelementptr inbounds i8, ptr %sc_env, i64 32 ; caps[0]`);
|
|
2575
|
+
B.line(`${box} = load ptr, ptr ${capp}`);
|
|
2576
|
+
const fnv = B.tmp();
|
|
2577
|
+
B.line(`${fnv} = call ptr @scr_box_get_ref(ptr ${box}) ; +1`);
|
|
2578
|
+
// The adapter OWNS its params (closure ABI); each converts to a dyn
|
|
2579
|
+
// argument (borrowed by the conversion) and releases.
|
|
2580
|
+
let argsPtr = "null";
|
|
2581
|
+
const argVals = [];
|
|
2582
|
+
if (t.params.length > 0) {
|
|
2583
|
+
const arr = B.slot();
|
|
2584
|
+
B.entryAllocas.push(`${arr} = alloca [${t.params.length} x ptr]`);
|
|
2585
|
+
t.params.forEach((p, i) => {
|
|
2586
|
+
const v = this.toDynExpr(B, p, `%a${i}`);
|
|
2587
|
+
argVals.push(v);
|
|
2588
|
+
const slotp = B.tmp();
|
|
2589
|
+
B.line(`${slotp} = getelementptr inbounds [${t.params.length} x ptr], ptr ${arr}, i64 0, i64 ${i}`);
|
|
2590
|
+
B.line(`store ptr ${v}, ptr ${slotp}`);
|
|
2591
|
+
if (isRefCounted(p))
|
|
2592
|
+
B.line(`call void ${releaseSym(host, p)}(ptr %a${i})`);
|
|
2593
|
+
});
|
|
2594
|
+
argsPtr = arr;
|
|
2595
|
+
}
|
|
2596
|
+
// The kind is FUNC by construction; `what` is unreachable — spelled
|
|
2597
|
+
// anyway (the C's "value").
|
|
2598
|
+
const r = B.tmp();
|
|
2599
|
+
B.line(`${r} = call ptr @scr_dyn_call(ptr ${fnv}, ptr ${argsPtr}, i64 ${t.params.length}, ptr ${host.cstr("value")})`);
|
|
2600
|
+
B.line(`call void @scr_dyn_release(ptr ${fnv})`);
|
|
2601
|
+
for (const v of argVals)
|
|
2602
|
+
B.line(`call void @scr_dyn_release(ptr ${v})`);
|
|
2603
|
+
this.pendingBail(B, "dfa", () => { }, dummy === "void" ? "void" : dummy);
|
|
2604
|
+
if (t.ret.kind === "void") {
|
|
2605
|
+
B.line(`call void @scr_dyn_release(ptr ${r})`);
|
|
2606
|
+
B.terminate(`ret void`);
|
|
2607
|
+
}
|
|
2608
|
+
else if (t.ret.kind === "dyn") {
|
|
2609
|
+
B.terminate(`ret ptr ${r}`);
|
|
2610
|
+
}
|
|
2611
|
+
else {
|
|
2612
|
+
// Validate the dyn result into the target's return type — a lying
|
|
2613
|
+
// wrapper throws the catchable TypeError here (path "$").
|
|
2614
|
+
const out = B.tmp();
|
|
2615
|
+
B.line(`${out} = call ${this.valTy(t.ret)} @${this.dynCheckHelper(t.ret)}(ptr ${r}, ptr null)`);
|
|
2616
|
+
B.line(`call void @scr_dyn_release(ptr ${r})`);
|
|
2617
|
+
B.terminate(`ret ${this.valTy(t.ret)} ${out}`);
|
|
2618
|
+
}
|
|
2619
|
+
const params = ["ptr %sc_env", ...t.params.map((p, i) => `${this.valTy(p)} %a${i}`)].join(", ");
|
|
2620
|
+
this.defs.push(`define internal ${retTy === "i1" ? "zeroext i1" : retTy} @${name}(${params}) ${FN_ATTRS} { ; dyn fn adapter to ${key}`, B.render(), `}`, ``);
|
|
2621
|
+
return name;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
//# sourceMappingURL=dyn.js.map
|