jseption 0.0.0-stage → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +191 -2
  3. package/dist/atoms.d.ts +255 -0
  4. package/dist/atoms.js +445 -0
  5. package/dist/atoms.js.map +1 -0
  6. package/dist/builtins/array.d.ts +3 -0
  7. package/dist/builtins/array.js +116 -0
  8. package/dist/builtins/array.js.map +1 -0
  9. package/dist/builtins/bigint.d.ts +3 -0
  10. package/dist/builtins/bigint.js +122 -0
  11. package/dist/builtins/bigint.js.map +1 -0
  12. package/dist/builtins/boolean.d.ts +2 -0
  13. package/dist/builtins/boolean.js +26 -0
  14. package/dist/builtins/boolean.js.map +1 -0
  15. package/dist/builtins/date.d.ts +3 -0
  16. package/dist/builtins/date.js +1046 -0
  17. package/dist/builtins/date.js.map +1 -0
  18. package/dist/builtins/error.d.ts +2 -0
  19. package/dist/builtins/error.js +154 -0
  20. package/dist/builtins/error.js.map +1 -0
  21. package/dist/builtins/function.d.ts +5 -0
  22. package/dist/builtins/function.js +164 -0
  23. package/dist/builtins/function.js.map +1 -0
  24. package/dist/builtins/global.d.ts +2 -0
  25. package/dist/builtins/global.js +29 -0
  26. package/dist/builtins/global.js.map +1 -0
  27. package/dist/builtins/index.d.ts +3 -0
  28. package/dist/builtins/index.js +139 -0
  29. package/dist/builtins/index.js.map +1 -0
  30. package/dist/builtins/json.d.ts +6 -0
  31. package/dist/builtins/json.js +984 -0
  32. package/dist/builtins/json.js.map +1 -0
  33. package/dist/builtins/mapset.d.ts +4 -0
  34. package/dist/builtins/mapset.js +234 -0
  35. package/dist/builtins/mapset.js.map +1 -0
  36. package/dist/builtins/math.d.ts +2 -0
  37. package/dist/builtins/math.js +62 -0
  38. package/dist/builtins/math.js.map +1 -0
  39. package/dist/builtins/native.d.ts +22 -0
  40. package/dist/builtins/native.js +250 -0
  41. package/dist/builtins/native.js.map +1 -0
  42. package/dist/builtins/number.d.ts +4 -0
  43. package/dist/builtins/number.js +76 -0
  44. package/dist/builtins/number.js.map +1 -0
  45. package/dist/builtins/object.d.ts +12 -0
  46. package/dist/builtins/object.js +575 -0
  47. package/dist/builtins/object.js.map +1 -0
  48. package/dist/builtins/promise.d.ts +13 -0
  49. package/dist/builtins/promise.js +582 -0
  50. package/dist/builtins/promise.js.map +1 -0
  51. package/dist/builtins/proxy.d.ts +3 -0
  52. package/dist/builtins/proxy.js +226 -0
  53. package/dist/builtins/proxy.js.map +1 -0
  54. package/dist/builtins/reflect.d.ts +2 -0
  55. package/dist/builtins/reflect.js +109 -0
  56. package/dist/builtins/reflect.js.map +1 -0
  57. package/dist/builtins/regexp.d.ts +2 -0
  58. package/dist/builtins/regexp.js +203 -0
  59. package/dist/builtins/regexp.js.map +1 -0
  60. package/dist/builtins/string.d.ts +4 -0
  61. package/dist/builtins/string.js +135 -0
  62. package/dist/builtins/string.js.map +1 -0
  63. package/dist/builtins/symbol.d.ts +3 -0
  64. package/dist/builtins/symbol.js +98 -0
  65. package/dist/builtins/symbol.js.map +1 -0
  66. package/dist/builtins/typedarray.d.ts +15 -0
  67. package/dist/builtins/typedarray.js +1838 -0
  68. package/dist/builtins/typedarray.js.map +1 -0
  69. package/dist/builtins/weakref.d.ts +18 -0
  70. package/dist/builtins/weakref.js +130 -0
  71. package/dist/builtins/weakref.js.map +1 -0
  72. package/dist/dump.d.ts +2 -0
  73. package/dist/dump.js +124 -0
  74. package/dist/dump.js.map +1 -0
  75. package/dist/embed.d.ts +18 -0
  76. package/dist/embed.js +286 -0
  77. package/dist/embed.js.map +1 -0
  78. package/dist/interpreter.d.ts +81 -0
  79. package/dist/interpreter.js +3768 -0
  80. package/dist/interpreter.js.map +1 -0
  81. package/dist/jit.d.ts +59 -0
  82. package/dist/jit.js +3440 -0
  83. package/dist/jit.js.map +1 -0
  84. package/dist/jit_hook.d.ts +21 -0
  85. package/dist/jit_hook.js +4 -0
  86. package/dist/jit_hook.js.map +1 -0
  87. package/dist/jit_runtime.d.ts +104 -0
  88. package/dist/jit_runtime.js +721 -0
  89. package/dist/jit_runtime.js.map +1 -0
  90. package/dist/jit_stack.d.ts +24 -0
  91. package/dist/jit_stack.js +227 -0
  92. package/dist/jit_stack.js.map +1 -0
  93. package/dist/jseption.d.ts +15 -0
  94. package/dist/jseption.js +61 -0
  95. package/dist/jseption.js.map +1 -0
  96. package/dist/opcodes.d.ts +347 -0
  97. package/dist/opcodes.js +322 -0
  98. package/dist/opcodes.js.map +1 -0
  99. package/dist/parser.d.ts +283 -0
  100. package/dist/parser.js +9752 -0
  101. package/dist/parser.js.map +1 -0
  102. package/dist/qjs.d.ts +2 -0
  103. package/dist/qjs.js +122 -0
  104. package/dist/qjs.js.map +1 -0
  105. package/dist/quickjs.d.ts +460 -0
  106. package/dist/quickjs.js +3408 -0
  107. package/dist/quickjs.js.map +1 -0
  108. package/dist/std.d.ts +4 -0
  109. package/dist/std.js +273 -0
  110. package/dist/std.js.map +1 -0
  111. package/dist/values.d.ts +447 -0
  112. package/dist/values.js +461 -0
  113. package/dist/values.js.map +1 -0
  114. package/package.json +59 -4
package/dist/jit.js ADDED
@@ -0,0 +1,3440 @@
1
+ /*
2
+ * Compiled tier: translates a JSFunctionBytecode (final bytecode, after
3
+ * resolve_labels) into host JavaScript source and evaluates it once with
4
+ * `new Function`. See docs/HOST_VALUES.md ("Compiled tier").
5
+ *
6
+ * Pipeline:
7
+ * 1. decode - split the byte code into instructions
8
+ * 2. analyze - abstract interpretation of the operand stack: the
9
+ * depth and the *kind* of every slot (value, catch
10
+ * offset, for-of iterator marker, gosub return address)
11
+ * are static at every pc
12
+ * 3. blocks - basic blocks + control flow graph; blocks are also
13
+ * split where the set of active exception handlers
14
+ * changes, so every block belongs to exactly one
15
+ * try-region
16
+ * 4. emit blocks - stack slots become locals ($s<depth>); each block is
17
+ * translated with a pending-expression model so that
18
+ * `a[i] = b[i] * 2` becomes one host statement
19
+ * 5. reloop - a relooper turns the CFG into labeled blocks/loops
20
+ * and nests try-regions as host try/catch statements;
21
+ * a `for(;;) switch` fallback exists for debugging
22
+ * (JSEPTION_JIT_CFG=switch)
23
+ *
24
+ * Calling convention of compiled functions (and of everything the runtime
25
+ * adapter exposes): `fn($this, $new_target, ...args)`. `this` and
26
+ * new.target are explicit parameters so that guest-to-guest calls are
27
+ * plain host calls (no Function.prototype.call / Reflect.apply, which are
28
+ * 30-50x slower and would need the guest-visible `call` property).
29
+ * A constructor call passes `$this = undefined` and a defined
30
+ * `$new_target`; the callee creates `this` and returns the object.
31
+ *
32
+ * Generated code never contains free identifiers other than the `$`-prefixed
33
+ * names created here. Guest globals, closures, classes, regexps, spread,
34
+ * iteration and every operation on a primitive base go through the `$RT`
35
+ * object (src/jit_runtime.ts).
36
+ */
37
+ import { JSFunctionBytecode, JS_AtomGetStr, __JS_AtomIsTaggedInt, __JS_AtomToUInt32 } from "./quickjs.js";
38
+ import { JS_MODE_STRICT, JS_CALL_FLAG_CONSTRUCTOR, new_var_ref_cell, JS_IsCatchOffset } from "./values.js";
39
+ import { short_opcode_info, get_u8, get_i8, get_u16, get_i16, get_u32, get_i32 } from "./opcodes.js";
40
+ import { jit_hooks, JIT_NOT_COMPILED } from "./jit_hook.js";
41
+ import { jit_set_osr_threshold } from "./interpreter.js";
42
+ import { make_jit_runtime, jit_runtime_call_compiled, jit_runtime_call_priv, jit_runtime_make_wrapper, jit_runtime_factory_args, JIT_FACTORY_PARAMS } from "./jit_runtime.js";
43
+ import { js_direct, JS_GetFunctionData, js_host_exception_to_guest } from "./quickjs.js";
44
+ import { jit_stack_alloc_id, jit_stack_register, jit_stack_install } from "./jit_stack.js";
45
+ function env(name) {
46
+ return globalThis.process?.env?.[name];
47
+ }
48
+ /* 'hot' is the default (measured with bench/inproc.ts, see docs/JIT.md): compiling every
49
+ function that runs ('always') costs 300-400 us per function in Bun, most of it the host
50
+ compiling the generated source, which made code that runs once (application start-up, the
51
+ cold bundles) 3-5x slower than interpreting it; hot mode leaves such code interpreted, and
52
+ with retargetable wrappers, closures compiled at creation by compiled code and on-stack
53
+ replacement of hot loops its steady state is mostly within 1.15x of 'always'. */
54
+ /* the mode used when JSEPTION_JIT is unset (or `1`) */
55
+ export const jit_default_mode = 'hot';
56
+ function parse_mode(v) {
57
+ if (v === undefined || v === '' || v === '1')
58
+ return jit_default_mode;
59
+ if (v === 'always')
60
+ return 'always';
61
+ if (v === '0' || v === 'off' || v === 'false')
62
+ return 'off';
63
+ if (v === 'hot')
64
+ return 'hot';
65
+ return jit_default_mode;
66
+ }
67
+ export const jit_config = {
68
+ mode: parse_mode(env('JSEPTION_JIT')),
69
+ unavailable: false,
70
+ unavailable_reason: '',
71
+ threshold: 2,
72
+ cfg: env('JSEPTION_JIT_CFG') === 'switch' ? 'switch' : 'reloop',
73
+ dump: env('JSEPTION_JIT_DUMP') === '1',
74
+ conv: env('JSEPTION_JIT_CONV') === 'forwarder' ? 'forwarder' : 'natural',
75
+ osr: env('JSEPTION_JIT_OSR') !== '0',
76
+ osr_threshold: Number(env('JSEPTION_JIT_OSR') ?? '') > 0 ? Number(env('JSEPTION_JIT_OSR')) : 500,
77
+ };
78
+ if (env('JSEPTION_JIT_THRESHOLD') !== undefined && Number(env('JSEPTION_JIT_THRESHOLD')) > 0) {
79
+ jit_config.threshold = Number(env('JSEPTION_JIT_THRESHOLD'));
80
+ }
81
+ /* Does the host allow code generation from strings? Probed once (or again with `force`); when
82
+ it does not, the tier turns itself off and the interpreter runs everything. Never throws. */
83
+ let probed = false;
84
+ export function jit_probe(force = false) {
85
+ if (probed && !force)
86
+ return !jit_config.unavailable;
87
+ probed = true;
88
+ try {
89
+ new Function('return 1');
90
+ jit_config.unavailable = false;
91
+ jit_config.unavailable_reason = '';
92
+ jit_stats.unavailable = null;
93
+ return true;
94
+ }
95
+ catch (e) {
96
+ const reason = String(e?.message ?? e);
97
+ jit_config.unavailable = true;
98
+ jit_config.unavailable_reason = reason;
99
+ jit_config.mode = 'off';
100
+ jit_stats.unavailable = reason;
101
+ jit_stack_install(false);
102
+ return false;
103
+ }
104
+ }
105
+ /* change the configuration at run time; returns it (`mode` is 'off' and `unavailable` is set
106
+ when the host forbids code generation) */
107
+ export function jit_configure(c) {
108
+ Object.assign(jit_config, c);
109
+ if (jit_config.mode !== 'off' && !jit_probe())
110
+ jit_config.mode = 'off';
111
+ jit_stack_install(jit_config.mode !== 'off');
112
+ jit_set_osr_threshold(jit_config.osr_threshold);
113
+ return jit_config;
114
+ }
115
+ /* statistics (for tests and debugging) */
116
+ export const jit_stats = {
117
+ compiled: 0, bailed: 0, bail_reasons: new Map(),
118
+ /* static counts of method call sites: native member call vs direct-entry/slow path */
119
+ method_calls_fused: 0, method_calls_unfused: 0,
120
+ /* on-stack replacement: variants compiled, and frames replaced */
121
+ osr_compiled: 0, osr_entered: 0,
122
+ /* the host's message when code generation from strings is forbidden (the tier is off) */
123
+ unavailable: null,
124
+ };
125
+ export class JitBail extends Error {
126
+ }
127
+ function bail(msg) { throw new JitBail(msg); }
128
+ function decode(b) {
129
+ const buf = b.byte_code_buf;
130
+ const len = b.byte_code_len;
131
+ const insns = [];
132
+ let pos = 0;
133
+ while (pos < len) {
134
+ const op = buf[pos];
135
+ const oi = short_opcode_info(op);
136
+ if (!oi || op === 0 /* OP.invalid */)
137
+ bail(`invalid opcode ${op} at ${pos}`);
138
+ const p = pos + 1;
139
+ const insn = { pc: pos, op, size: oi.size, next: pos + oi.size, name: oi.name, fmt: oi.fmt, a: 0, b: 0, label: -1 };
140
+ switch (oi.fmt) {
141
+ case 0 /* OpFmt.none */: break;
142
+ case 1 /* OpFmt.none_int */:
143
+ insn.a = op - 183 /* OP.push_0 */;
144
+ break;
145
+ case 2 /* OpFmt.none_loc */:
146
+ insn.a = (op - 199 /* OP.get_loc0 */) % 4;
147
+ break;
148
+ case 3 /* OpFmt.none_arg */:
149
+ insn.a = (op - 211 /* OP.get_arg0 */) % 4;
150
+ break;
151
+ case 4 /* OpFmt.none_var_ref */:
152
+ insn.a = (op - 223 /* OP.get_var_ref0 */) % 4;
153
+ break;
154
+ case 5 /* OpFmt.u8 */:
155
+ case 7 /* OpFmt.loc8 */:
156
+ case 8 /* OpFmt.const8 */:
157
+ insn.a = get_u8(buf, p);
158
+ break;
159
+ case 6 /* OpFmt.i8 */:
160
+ insn.a = get_i8(buf, p);
161
+ break;
162
+ case 9 /* OpFmt.label8 */:
163
+ insn.label = p + get_i8(buf, p);
164
+ break;
165
+ case 10 /* OpFmt.u16 */:
166
+ case 13 /* OpFmt.npop */:
167
+ case 16 /* OpFmt.loc */:
168
+ case 17 /* OpFmt.arg */:
169
+ case 18 /* OpFmt.var_ref */:
170
+ insn.a = get_u16(buf, p);
171
+ break;
172
+ case 14 /* OpFmt.npopx */:
173
+ insn.a = op - 240 /* OP.call0 */;
174
+ break;
175
+ case 11 /* OpFmt.i16 */:
176
+ insn.a = get_i16(buf, p);
177
+ break;
178
+ case 12 /* OpFmt.label16 */:
179
+ insn.label = p + get_i16(buf, p);
180
+ break;
181
+ case 15 /* OpFmt.npop_u16 */:
182
+ insn.a = get_u16(buf, p);
183
+ insn.b = get_u16(buf, p + 2);
184
+ break;
185
+ case 19 /* OpFmt.u32 */:
186
+ insn.a = get_u32(buf, p);
187
+ break;
188
+ case 20 /* OpFmt.i32 */:
189
+ insn.a = get_i32(buf, p);
190
+ break;
191
+ case 21 /* OpFmt.const */:
192
+ insn.a = get_u32(buf, p);
193
+ break;
194
+ case 22 /* OpFmt.label */:
195
+ insn.label = p + get_i32(buf, p);
196
+ break;
197
+ case 23 /* OpFmt.atom */:
198
+ insn.a = get_u32(buf, p);
199
+ break;
200
+ case 24 /* OpFmt.atom_u8 */:
201
+ insn.a = get_u32(buf, p);
202
+ insn.b = get_u8(buf, p + 4);
203
+ break;
204
+ case 25 /* OpFmt.atom_u16 */:
205
+ insn.a = get_u32(buf, p);
206
+ insn.b = get_u16(buf, p + 4);
207
+ break;
208
+ case 26 /* OpFmt.atom_label_u8 */:
209
+ insn.a = get_u32(buf, p);
210
+ insn.label = p + 4 + get_i32(buf, p + 4);
211
+ insn.b = get_u8(buf, p + 8);
212
+ break;
213
+ case 27 /* OpFmt.atom_label_u16 */:
214
+ insn.a = get_u32(buf, p);
215
+ insn.label = p + 4 + get_i32(buf, p + 4);
216
+ insn.b = get_u16(buf, p + 8);
217
+ break;
218
+ case 28 /* OpFmt.label_u16 */:
219
+ insn.label = p + get_i32(buf, p);
220
+ insn.a = get_u16(buf, p + 4);
221
+ break;
222
+ default: bail(`unknown operand format for ${oi.name}`);
223
+ }
224
+ insns.push(insn);
225
+ pos += oi.size;
226
+ }
227
+ return insns;
228
+ }
229
+ const KV = { t: 0 /* KT.V */, origin: -1, handler: -1, sites: null };
230
+ function kinds_equal(a, b) {
231
+ if (a.length !== b.length)
232
+ return false;
233
+ for (let i = 0; i < a.length; i++) {
234
+ const x = a[i], y = b[i];
235
+ if (x.t !== y.t)
236
+ return false;
237
+ if (x.t === 1 /* KT.CATCH */ || x.t === 2 /* KT.CATCH0 */) {
238
+ if (x.origin !== y.origin)
239
+ return false;
240
+ }
241
+ if (x.t === 4 /* KT.REF */) {
242
+ if (x.ref_kind !== y.ref_kind || x.ref_idx !== y.ref_idx)
243
+ return false;
244
+ }
245
+ }
246
+ return true;
247
+ }
248
+ /* merge ret sites; returns true if `into` changed */
249
+ function kinds_merge(into, from) {
250
+ let changed = false;
251
+ for (let i = 0; i < into.length; i++) {
252
+ if (into[i].t === 3 /* KT.RET */) {
253
+ const a = into[i].sites, bs = from[i].sites;
254
+ for (const s of bs)
255
+ if (!a.includes(s)) {
256
+ a.push(s);
257
+ changed = true;
258
+ }
259
+ }
260
+ }
261
+ return changed;
262
+ }
263
+ function analyze(b, insns) {
264
+ const len = b.byte_code_len;
265
+ const states = new Array(len).fill(null);
266
+ const insnAt = new Array(len);
267
+ for (const i of insns)
268
+ insnAt[i.pc] = i;
269
+ const sites = [];
270
+ const siteByPc = new Map();
271
+ const A = { states, sites, insnAt, uses_arguments: false, uses_this: false, uses_mapped_arguments: false, is_ctor: false };
272
+ const work = [];
273
+ const visit = (pc, st) => {
274
+ if (pc < 0 || pc >= len)
275
+ bail(`jump out of range: ${pc}`);
276
+ if (insnAt[pc] === undefined)
277
+ bail(`jump into the middle of an instruction: ${pc}`);
278
+ const cur = states[pc];
279
+ if (cur === null) {
280
+ let has_ret = false;
281
+ for (let i = 0; i < st.length; i++)
282
+ if (st[i].t === 3 /* KT.RET */) {
283
+ has_ret = true;
284
+ break;
285
+ }
286
+ states[pc] = has_ret ? st.map(k => k.t === 3 /* KT.RET */ ? { ...k, sites: k.sites.slice() } : k) : st.slice();
287
+ work.push(pc);
288
+ }
289
+ else {
290
+ if (!kinds_equal(cur, st))
291
+ bail(`inconsistent stack at ${pc}`);
292
+ if (kinds_merge(cur, st))
293
+ work.push(pc);
294
+ }
295
+ };
296
+ visit(0, []);
297
+ /* the instruction being analyzed and its (copied) input stack; the helpers are created once */
298
+ let pc = 0;
299
+ let insn = insns[0];
300
+ let st = [];
301
+ const pop = (n) => {
302
+ if (st.length < n)
303
+ bail(`stack underflow at ${pc} (${insn.name})`);
304
+ return st.splice(st.length - n, n);
305
+ };
306
+ const push = (k) => { st.push(k); };
307
+ const pushV = (n) => { for (let i = 0; i < n; i++)
308
+ st.push(KV); };
309
+ const top = (i = 1) => st[st.length - i];
310
+ while (work.length) {
311
+ pc = work.pop();
312
+ insn = insnAt[pc];
313
+ st = states[pc].slice();
314
+ const oi = short_opcode_info(insn.op);
315
+ let n_pop = oi.n_pop;
316
+ if (oi.fmt === 13 /* OpFmt.npop */ || oi.fmt === 15 /* OpFmt.npop_u16 */)
317
+ n_pop += insn.a;
318
+ else if (oi.fmt === 14 /* OpFmt.npopx */)
319
+ n_pop += insn.a;
320
+ switch (insn.op) {
321
+ /* unsupported in the first version */
322
+ case 117 /* OP.with_get_var */:
323
+ case 118 /* OP.with_put_var */:
324
+ case 119 /* OP.with_delete_var */:
325
+ case 120 /* OP.with_make_ref */:
326
+ case 121 /* OP.with_get_ref */:
327
+ bail('with statement');
328
+ case 50 /* OP.eval */:
329
+ case 51 /* OP.apply_eval */:
330
+ bail('direct eval');
331
+ case 141 /* OP.await */:
332
+ case 138 /* OP.yield */:
333
+ case 139 /* OP.yield_star */:
334
+ case 140 /* OP.async_yield_star */:
335
+ case 137 /* OP.initial_yield */:
336
+ case 47 /* OP.return_async */:
337
+ case 128 /* OP.for_await_of_start */:
338
+ case 131 /* OP.for_await_of_next */:
339
+ case 133 /* OP.iterator_get_value_done */:
340
+ bail('generator/async');
341
+ case 54 /* OP.import */:
342
+ bail('dynamic import');
343
+ case 122 /* OP.make_loc_ref */:
344
+ case 123 /* OP.make_arg_ref */:
345
+ case 124 /* OP.make_var_ref_ref */:
346
+ push({ t: 4 /* KT.REF */, origin: pc, handler: -1, sites: null,
347
+ ref_kind: insn.op === 122 /* OP.make_loc_ref */ ? 0 /* RefKind.LOC */ : insn.op === 123 /* OP.make_arg_ref */ ? 1 /* RefKind.ARG */ : 2 /* RefKind.VAR_REF */,
348
+ ref_idx: insn.b });
349
+ pushV(1);
350
+ break;
351
+ /* instructions that keep their lower operands (iterator triples carry catch kinds) */
352
+ case 130 /* OP.for_of_next */:
353
+ case 129 /* OP.for_in_next */:
354
+ pushV(2);
355
+ break;
356
+ case 135 /* OP.iterator_next */:
357
+ pop(1);
358
+ pushV(1);
359
+ break;
360
+ case 136 /* OP.iterator_call */:
361
+ pushV(1);
362
+ break;
363
+ case 132 /* OP.iterator_check_object */: break;
364
+ case 12 /* OP.special_object */:
365
+ if (insn.a === 6 /* OPSpecialObject.IMPORT_META */)
366
+ bail('import.meta');
367
+ if (insn.a === 0 /* OPSpecialObject.ARGUMENTS */)
368
+ A.uses_arguments = true;
369
+ if (insn.a === 1 /* OPSpecialObject.MAPPED_ARGUMENTS */) {
370
+ A.uses_arguments = true;
371
+ A.uses_mapped_arguments = true;
372
+ }
373
+ pushV(1);
374
+ break;
375
+ case 13 /* OP.rest */:
376
+ case 44 /* OP.init_ctor */:
377
+ A.uses_arguments = true;
378
+ if (insn.op === 44 /* OP.init_ctor */)
379
+ A.is_ctor = true;
380
+ pushV(1);
381
+ break;
382
+ case 43 /* OP.check_ctor */:
383
+ A.is_ctor = true;
384
+ break;
385
+ case 8 /* OP.push_this */:
386
+ A.uses_this = true;
387
+ pushV(1);
388
+ break;
389
+ /* terminators */
390
+ case 40 /* OP.return */:
391
+ case 41 /* OP.return_undef */:
392
+ case 48 /* OP.throw */:
393
+ case 49 /* OP.throw_error */:
394
+ case 35 /* OP.tail_call */:
395
+ case 37 /* OP.tail_call_method */:
396
+ continue;
397
+ case 109 /* OP.goto */:
398
+ case 238 /* OP.goto8 */:
399
+ case 239 /* OP.goto16 */:
400
+ visit(insn.label, st);
401
+ continue;
402
+ case 108 /* OP.if_true */:
403
+ case 107 /* OP.if_false */:
404
+ case 237 /* OP.if_true8 */:
405
+ case 236 /* OP.if_false8 */:
406
+ pop(1);
407
+ visit(insn.label, st);
408
+ visit(insn.next, st);
409
+ continue;
410
+ case 110 /* OP.catch */: {
411
+ /* handler: the catch slot is replaced by the exception */
412
+ const hst = st.slice();
413
+ hst.push(KV);
414
+ visit(insn.label, hst);
415
+ push({ t: 1 /* KT.CATCH */, origin: pc, handler: insn.label, sites: null });
416
+ break;
417
+ }
418
+ case 111 /* OP.gosub */: {
419
+ let site = siteByPc.get(pc);
420
+ if (!site) {
421
+ site = { id: sites.length, pc, cont: insn.next };
422
+ sites.push(site);
423
+ siteByPc.set(pc, site);
424
+ }
425
+ push({ t: 3 /* KT.RET */, origin: pc, handler: -1, sites: [site.id] });
426
+ visit(insn.label, st);
427
+ continue;
428
+ }
429
+ case 112 /* OP.ret */: {
430
+ const k = pop(1)[0];
431
+ if (k.t !== 3 /* KT.RET */)
432
+ bail('ret without gosub');
433
+ for (const sid of k.sites)
434
+ visit(sites[sid].cont, st);
435
+ continue;
436
+ }
437
+ case 127 /* OP.for_of_start */:
438
+ pop(1);
439
+ pushV(2);
440
+ push({ t: 2 /* KT.CATCH0 */, origin: pc, handler: 0, sites: null });
441
+ break;
442
+ case 134 /* OP.iterator_close */:
443
+ pop(3);
444
+ break;
445
+ case 113 /* OP.nip_catch */: {
446
+ const rv = pop(1)[0];
447
+ let found = false;
448
+ while (st.length > 0) {
449
+ const k = st.pop();
450
+ if (k.t === 1 /* KT.CATCH */ || k.t === 2 /* KT.CATCH0 */) {
451
+ found = true;
452
+ break;
453
+ }
454
+ }
455
+ if (!found)
456
+ bail('nip_catch without catch');
457
+ push(rv.t === 0 /* KT.V */ ? KV : rv);
458
+ break;
459
+ }
460
+ /* stack manipulation (kinds move with the values) */
461
+ case 14 /* OP.drop */:
462
+ pop(1);
463
+ break;
464
+ case 15 /* OP.nip */: {
465
+ const [, y] = pop(2);
466
+ push(y);
467
+ break;
468
+ }
469
+ case 16 /* OP.nip1 */: {
470
+ const [, y, z] = pop(3);
471
+ push(y);
472
+ push(z);
473
+ break;
474
+ }
475
+ case 17 /* OP.dup */: {
476
+ const x = top();
477
+ push(x);
478
+ break;
479
+ }
480
+ case 18 /* OP.dup1 */: {
481
+ const [x, y] = pop(2);
482
+ push(x);
483
+ push(x);
484
+ push(y);
485
+ break;
486
+ }
487
+ case 19 /* OP.dup2 */: {
488
+ const x = top(2), y = top(1);
489
+ push(x);
490
+ push(y);
491
+ break;
492
+ }
493
+ case 20 /* OP.dup3 */: {
494
+ const x = top(3), y = top(2), z = top(1);
495
+ push(x);
496
+ push(y);
497
+ push(z);
498
+ break;
499
+ }
500
+ case 21 /* OP.insert2 */: {
501
+ const [x, y] = pop(2);
502
+ push(y);
503
+ push(x);
504
+ push(y);
505
+ break;
506
+ }
507
+ case 22 /* OP.insert3 */: {
508
+ const [x, y, z] = pop(3);
509
+ push(z);
510
+ push(x);
511
+ push(y);
512
+ push(z);
513
+ break;
514
+ }
515
+ case 23 /* OP.insert4 */: {
516
+ const [w, x, y, z] = pop(4);
517
+ push(z);
518
+ push(w);
519
+ push(x);
520
+ push(y);
521
+ push(z);
522
+ break;
523
+ }
524
+ case 24 /* OP.perm3 */: {
525
+ const [x, y, z] = pop(3);
526
+ push(y);
527
+ push(x);
528
+ push(z);
529
+ break;
530
+ }
531
+ case 25 /* OP.perm4 */: {
532
+ const [w, x, y, z] = pop(4);
533
+ push(y);
534
+ push(w);
535
+ push(x);
536
+ push(z);
537
+ break;
538
+ }
539
+ case 26 /* OP.perm5 */: {
540
+ const [v, w, x, y, z] = pop(5);
541
+ push(y);
542
+ push(v);
543
+ push(w);
544
+ push(x);
545
+ push(z);
546
+ break;
547
+ }
548
+ case 27 /* OP.swap */: {
549
+ const [x, y] = pop(2);
550
+ push(y);
551
+ push(x);
552
+ break;
553
+ }
554
+ case 28 /* OP.swap2 */: {
555
+ const [w, x, y, z] = pop(4);
556
+ push(y);
557
+ push(z);
558
+ push(w);
559
+ push(x);
560
+ break;
561
+ }
562
+ case 29 /* OP.rot3l */: {
563
+ const [x, y, z] = pop(3);
564
+ push(y);
565
+ push(z);
566
+ push(x);
567
+ break;
568
+ }
569
+ case 30 /* OP.rot3r */: {
570
+ const [x, y, z] = pop(3);
571
+ push(z);
572
+ push(x);
573
+ push(y);
574
+ break;
575
+ }
576
+ case 31 /* OP.rot4l */: {
577
+ const [w, x, y, z] = pop(4);
578
+ push(x);
579
+ push(y);
580
+ push(z);
581
+ push(w);
582
+ break;
583
+ }
584
+ case 32 /* OP.rot5l */: {
585
+ const [v, w, x, y, z] = pop(5);
586
+ push(w);
587
+ push(x);
588
+ push(y);
589
+ push(z);
590
+ push(v);
591
+ break;
592
+ }
593
+ default:
594
+ if (st.length < n_pop)
595
+ bail(`stack underflow at ${pc} (${insn.name})`);
596
+ st.length -= n_pop;
597
+ for (let i = 0; i < oi.n_push; i++)
598
+ st.push(KV);
599
+ break;
600
+ }
601
+ visit(insn.next, st);
602
+ }
603
+ return A;
604
+ }
605
+ class Block {
606
+ id;
607
+ start;
608
+ insns = [];
609
+ stateIn;
610
+ region = null;
611
+ code = ''; /* statements (without the terminator) */
612
+ term = { kind: 'fall' };
613
+ succs = []; /* block ids (deduplicated, in terminator order) */
614
+ dead = false; /* unreachable from an on-stack replacement entry: not emitted */
615
+ constructor(id, start, stateIn) { this.id = id; this.start = start; this.stateIn = stateIn; }
616
+ }
617
+ class Region {
618
+ id;
619
+ origin; /* pc of OP.catch / OP.for_of_start, -1 for the root */
620
+ parent;
621
+ kind; /* CATCH, CATCH0 or V for the root */
622
+ slot; /* stack index of the catch slot */
623
+ handler; /* CATCH: handler pc */
624
+ blocks = [];
625
+ children = [];
626
+ entry = null;
627
+ /* on-stack replacement: the region contains the entry (the loop header), which is its entry
628
+ node (a block id, or the node id of the child region containing it) */
629
+ entry_node = null;
630
+ osr_chain = false;
631
+ exits = new Set(); /* block ids outside this region targeted from inside */
632
+ constructor(id, origin, parent, kind, slot, handler) {
633
+ this.id = id;
634
+ this.origin = origin;
635
+ this.parent = parent;
636
+ this.kind = kind;
637
+ this.slot = slot;
638
+ this.handler = handler;
639
+ }
640
+ }
641
+ function handler_chain(st) {
642
+ const r = [];
643
+ for (let i = 0; i < st.length; i++)
644
+ if (st[i].t === 1 /* KT.CATCH */ || st[i].t === 2 /* KT.CATCH0 */)
645
+ r.push({ kind: st[i], slot: i });
646
+ return r;
647
+ }
648
+ /* same handler chain (catch kinds, in order) without allocating */
649
+ function same_chain(a, b) {
650
+ let i = 0, j = 0;
651
+ for (;;) {
652
+ while (i < a.length && a[i].t !== 1 /* KT.CATCH */ && a[i].t !== 2 /* KT.CATCH0 */)
653
+ i++;
654
+ while (j < b.length && b[j].t !== 1 /* KT.CATCH */ && b[j].t !== 2 /* KT.CATCH0 */)
655
+ j++;
656
+ if (i === a.length || j === b.length)
657
+ return i === a.length && j === b.length;
658
+ if (a[i].origin !== b[j].origin)
659
+ return false;
660
+ i++;
661
+ j++;
662
+ }
663
+ }
664
+ function is_terminator(op) {
665
+ switch (op) {
666
+ case 40 /* OP.return */:
667
+ case 41 /* OP.return_undef */:
668
+ case 48 /* OP.throw */:
669
+ case 49 /* OP.throw_error */:
670
+ case 35 /* OP.tail_call */:
671
+ case 37 /* OP.tail_call_method */:
672
+ case 109 /* OP.goto */:
673
+ case 238 /* OP.goto8 */:
674
+ case 239 /* OP.goto16 */:
675
+ case 108 /* OP.if_true */:
676
+ case 107 /* OP.if_false */:
677
+ case 237 /* OP.if_true8 */:
678
+ case 236 /* OP.if_false8 */:
679
+ case 111 /* OP.gosub */:
680
+ case 112 /* OP.ret */:
681
+ return true;
682
+ }
683
+ return false;
684
+ }
685
+ function build_blocks(insns, A) {
686
+ const leaders = new Set([0]);
687
+ for (const insn of insns) {
688
+ if (A.states[insn.pc] === null)
689
+ continue;
690
+ if (insn.label >= 0)
691
+ leaders.add(insn.label);
692
+ if (is_terminator(insn.op)) {
693
+ if (insn.next < insns[insns.length - 1].next)
694
+ leaders.add(insn.next);
695
+ }
696
+ /* handler chain change */
697
+ const nextState = A.states[insn.next];
698
+ if (nextState !== null && nextState !== undefined && !same_chain(A.states[insn.pc], nextState))
699
+ leaders.add(insn.next);
700
+ if (insn.op === 110 /* OP.catch */ || insn.op === 127 /* OP.for_of_start */)
701
+ leaders.add(insn.next);
702
+ }
703
+ for (const s of A.sites)
704
+ leaders.add(s.cont);
705
+ const blocks = [];
706
+ const byPc = new Map();
707
+ let cur = null;
708
+ for (const insn of insns) {
709
+ if (A.states[insn.pc] === null) {
710
+ cur = null;
711
+ continue;
712
+ }
713
+ if (leaders.has(insn.pc)) {
714
+ cur = new Block(blocks.length, insn.pc, A.states[insn.pc]);
715
+ blocks.push(cur);
716
+ byPc.set(insn.pc, cur);
717
+ }
718
+ if (cur === null)
719
+ bail(`reachable code without a leader at ${insn.pc}`);
720
+ cur.insns.push(insn);
721
+ }
722
+ /* regions */
723
+ const root = new Region(0, -1, null, 0 /* KT.V */, -1, -1);
724
+ const regions = [root];
725
+ const byOrigin = new Map();
726
+ for (const blk of blocks) {
727
+ let parent = root;
728
+ for (const { kind, slot } of handler_chain(blk.stateIn)) {
729
+ let r = byOrigin.get(kind.origin);
730
+ if (!r) {
731
+ r = new Region(regions.length, kind.origin, parent, kind.t, slot, kind.handler);
732
+ regions.push(r);
733
+ byOrigin.set(kind.origin, r);
734
+ parent.children.push(r);
735
+ }
736
+ else if (r.parent !== parent || r.slot !== slot) {
737
+ bail('inconsistent try-region nesting');
738
+ }
739
+ parent = r;
740
+ }
741
+ blk.region = parent;
742
+ parent.blocks.push(blk);
743
+ }
744
+ for (const r of regions) {
745
+ if (r.origin < 0) {
746
+ r.entry = blocks[0];
747
+ continue;
748
+ }
749
+ const originInsn = A.insnAt[r.origin];
750
+ const e = byPc.get(originInsn.next);
751
+ if (!e || e.region !== r)
752
+ bail('try-region without entry');
753
+ r.entry = e;
754
+ }
755
+ return { blocks, byPc, root, regions };
756
+ }
757
+ /* On-stack replacement: make the loop header at `H` the entry of the function. Blocks that are
758
+ unreachable from it are dropped, try-regions without live blocks disappear, and every region
759
+ containing the header takes it (or the child region containing it) as its entry node. A
760
+ region containing the header that can also be entered normally (e.g. a try statement inside
761
+ an outer loop) is not supported (Relooper.build bails). */
762
+ function osr_prepare(cfg, A, H) {
763
+ const hb = cfg.byPc.get(H);
764
+ if (!hb)
765
+ bail('osr: the loop header is not a block');
766
+ for (const k of hb.stateIn) {
767
+ if (k.t !== 0 /* KT.V */ && k.t !== 1 /* KT.CATCH */ && k.t !== 2 /* KT.CATCH0 */)
768
+ bail('osr: unsupported stack at the loop header');
769
+ }
770
+ const live = new Set([hb]);
771
+ const work = [hb];
772
+ const add = (pc) => {
773
+ const t = cfg.byPc.get(pc);
774
+ if (t !== undefined && !live.has(t)) {
775
+ live.add(t);
776
+ work.push(t);
777
+ }
778
+ };
779
+ while (work.length) {
780
+ const blk = work.pop();
781
+ const last = blk.insns[blk.insns.length - 1];
782
+ switch (last.op) {
783
+ case 40 /* OP.return */:
784
+ case 41 /* OP.return_undef */:
785
+ case 48 /* OP.throw */:
786
+ case 49 /* OP.throw_error */:
787
+ case 35 /* OP.tail_call */:
788
+ case 37 /* OP.tail_call_method */:
789
+ case 112 /* OP.ret */: /* its continuations are added at the gosub sites */
790
+ break;
791
+ case 109 /* OP.goto */:
792
+ case 238 /* OP.goto8 */:
793
+ case 239 /* OP.goto16 */:
794
+ add(last.label);
795
+ break;
796
+ case 108 /* OP.if_true */:
797
+ case 107 /* OP.if_false */:
798
+ case 237 /* OP.if_true8 */:
799
+ case 236 /* OP.if_false8 */:
800
+ add(last.label);
801
+ add(last.next);
802
+ break;
803
+ case 111 /* OP.gosub */: {
804
+ add(last.label);
805
+ const site = A.sites.find(st => st.pc === last.pc);
806
+ if (site)
807
+ add(site.cont);
808
+ break;
809
+ }
810
+ default:
811
+ add(last.next);
812
+ if (last.op === 110 /* OP.catch */)
813
+ add(last.label);
814
+ break;
815
+ }
816
+ /* exception edges */
817
+ for (let r = blk.region; r !== null; r = r.parent)
818
+ if (r.kind === 1 /* KT.CATCH */)
819
+ add(r.handler);
820
+ }
821
+ for (const blk of cfg.blocks)
822
+ if (!live.has(blk))
823
+ blk.dead = true;
824
+ for (const r of cfg.regions)
825
+ r.blocks = r.blocks.filter(blk => !blk.dead);
826
+ const kept = new Set();
827
+ const keep = (r) => {
828
+ r.children = r.children.filter(keep);
829
+ const k = r.origin < 0 || r.blocks.length > 0 || r.children.length > 0;
830
+ if (k)
831
+ kept.add(r);
832
+ return k;
833
+ };
834
+ keep(cfg.root);
835
+ cfg.regions = cfg.regions.filter(r => kept.has(r));
836
+ let child = null;
837
+ for (let r = hb.region; r !== null; r = r.parent) {
838
+ r.entry_node = child === null ? hb.id : -child.id - 1;
839
+ r.osr_chain = true;
840
+ child = r;
841
+ }
842
+ /* the arguments object (and rest parameters) are created from the call's `arguments`, which
843
+ the replacement entry does not have */
844
+ for (const blk of live)
845
+ for (const insn of blk.insns) {
846
+ if (insn.op === 13 /* OP.rest */ || insn.op === 44 /* OP.init_ctor */ ||
847
+ (insn.op === 12 /* OP.special_object */ && (insn.a === 0 /* OPSpecialObject.ARGUMENTS */ || insn.a === 1 /* OPSpecialObject.MAPPED_ARGUMENTS */))) {
848
+ bail('osr: arguments object created after the loop header');
849
+ }
850
+ }
851
+ }
852
+ /* stack temps referenced by a pending expression (cached: push() asks for every live slot) */
853
+ function temps_of(p) {
854
+ if (p.trefs_code === p.code)
855
+ return p.trefs;
856
+ const code = p.code;
857
+ const r = [];
858
+ let i = code.indexOf('$s');
859
+ while (i >= 0) {
860
+ let j = i + 2, n = 0, c = code.charCodeAt(j);
861
+ if (c >= 48 && c <= 57) {
862
+ do {
863
+ n = n * 10 + (c - 48);
864
+ c = code.charCodeAt(++j);
865
+ } while (c >= 48 && c <= 57);
866
+ r.push(n);
867
+ }
868
+ i = code.indexOf('$s', j);
869
+ }
870
+ p.trefs_code = code;
871
+ p.trefs = r;
872
+ return r;
873
+ }
874
+ function P(code, kind, extra) {
875
+ /* explicit fields (an object spread here was a measurable part of code generation) */
876
+ const p = { code, kind, cpool: -1, local: '', method: undefined, fresh: undefined, mkeys: undefined };
877
+ if (extra !== undefined) {
878
+ if (extra.cpool !== undefined)
879
+ p.cpool = extra.cpool;
880
+ if (extra.local !== undefined)
881
+ p.local = extra.local;
882
+ if (extra.method !== undefined)
883
+ p.method = extra.method;
884
+ if (extra.fresh !== undefined)
885
+ p.fresh = extra.fresh;
886
+ if (extra.mkeys !== undefined)
887
+ p.mkeys = extra.mkeys;
888
+ }
889
+ return p;
890
+ }
891
+ /* the interrupt poll emitted at every function entry and loop head: the interpreter's
892
+ counter, decremented in place (`$C` is the context); the handler is asked when it runs out */
893
+ const POLL = 'if (--$C.interrupt_counter <= 0) $RT.poll_handler();\n';
894
+ const IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
895
+ const RESERVED = new Set(['__proto__']);
896
+ function fmt_number(n) {
897
+ if (Number.isNaN(n))
898
+ return '(0/0)';
899
+ if (n === Infinity)
900
+ return '(1/0)';
901
+ if (n === -Infinity)
902
+ return '(-1/0)';
903
+ if (n === 0 && 1 / n < 0)
904
+ return '(-0)';
905
+ const s = String(n);
906
+ return n < 0 ? `(${s})` : s;
907
+ }
908
+ function fmt_const(v, cpool_index) {
909
+ switch (typeof v) {
910
+ case 'number': return P(fmt_number(v), 0 /* PK.LIT */, { cpool: cpool_index });
911
+ case 'string': return P(JSON.stringify(v), 0 /* PK.LIT */, { cpool: cpool_index });
912
+ case 'boolean': return P(v ? 'true' : 'false', 0 /* PK.LIT */, { cpool: cpool_index });
913
+ case 'bigint': return P(v < 0n ? `(${v}n)` : `${v}n`, 0 /* PK.LIT */, { cpool: cpool_index });
914
+ case 'undefined': return P('undefined', 0 /* PK.LIT */, { cpool: cpool_index });
915
+ default:
916
+ if (v === null)
917
+ return P('null', 0 /* PK.LIT */, { cpool: cpool_index });
918
+ return null;
919
+ }
920
+ }
921
+ /* property key text for a static atom: `.name` or `[..]` */
922
+ function key_access(fi, atom) {
923
+ if (__JS_AtomIsTaggedInt(atom))
924
+ return `[${__JS_AtomToUInt32(atom)}]`;
925
+ if (fi.atom_is_symbol(atom))
926
+ bail('symbol atom in static property access');
927
+ const name = fi.atom_name(atom);
928
+ if (IDENT_RE.test(name) && !RESERVED.has(name))
929
+ return `.${name}`;
930
+ return `[${JSON.stringify(name)}]`;
931
+ }
932
+ /* property key value for a static atom (string/number literal) */
933
+ function key_value(fi, atom) {
934
+ if (__JS_AtomIsTaggedInt(atom))
935
+ return String(__JS_AtomToUInt32(atom));
936
+ if (fi.atom_is_symbol(atom))
937
+ bail('symbol atom as property key');
938
+ return JSON.stringify(fi.atom_name(atom));
939
+ }
940
+ /* references to stack temps `$s<n>` in generated code (manual scans: these run for every push) */
941
+ function refs_temp_above(code, idx) {
942
+ let i = code.indexOf('$s');
943
+ while (i >= 0) {
944
+ let j = i + 2, n = 0, c = code.charCodeAt(j);
945
+ if (c >= 48 && c <= 57) {
946
+ do {
947
+ n = n * 10 + (c - 48);
948
+ c = code.charCodeAt(++j);
949
+ } while (c >= 48 && c <= 57);
950
+ if (n > idx)
951
+ return true;
952
+ }
953
+ i = code.indexOf('$s', j);
954
+ }
955
+ return false;
956
+ }
957
+ function refs_temp(code, idx) {
958
+ let i = code.indexOf('$s');
959
+ while (i >= 0) {
960
+ let j = i + 2, n = 0, c = code.charCodeAt(j);
961
+ if (c >= 48 && c <= 57) {
962
+ do {
963
+ n = n * 10 + (c - 48);
964
+ c = code.charCodeAt(++j);
965
+ } while (c >= 48 && c <= 57);
966
+ if (n === idx)
967
+ return true;
968
+ }
969
+ i = code.indexOf('$s', j);
970
+ }
971
+ return false;
972
+ }
973
+ const TEMP_NAMES = [];
974
+ function temp_name(i) {
975
+ let n = TEMP_NAMES[i];
976
+ if (n === undefined) {
977
+ n = `$s${i}`;
978
+ TEMP_NAMES[i] = n;
979
+ }
980
+ return n;
981
+ }
982
+ /* position markers are emitted as comments `/*@<pc>*\/` (see src/jit_stack.ts) */
983
+ function starts_with_marker(code) {
984
+ return code.charCodeAt(0) === 47 && code.charCodeAt(1) === 42 && code.charCodeAt(2) === 64;
985
+ }
986
+ /* a statement `$name = /*@<pc>*\/...`: the pc of the expression it assigns, else -1 */
987
+ function stmt_expr_pc(stmt) {
988
+ if (stmt.charCodeAt(0) !== 36 /* $ */)
989
+ return -1;
990
+ let i = 1, c = stmt.charCodeAt(1);
991
+ if (!(c === 36 || c === 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122)))
992
+ return -1;
993
+ while (c === 36 || c === 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))
994
+ c = stmt.charCodeAt(++i);
995
+ if (c !== 32 || stmt.charCodeAt(i + 1) !== 61 || stmt.charCodeAt(i + 2) !== 32 ||
996
+ stmt.charCodeAt(i + 3) !== 47 || stmt.charCodeAt(i + 4) !== 42 || stmt.charCodeAt(i + 5) !== 64)
997
+ return -1;
998
+ let j = i + 6, pc = 0;
999
+ c = stmt.charCodeAt(j);
1000
+ if (!(c >= 48 && c <= 57))
1001
+ return -1;
1002
+ while (c >= 48 && c <= 57) {
1003
+ pc = pc * 10 + (c - 48);
1004
+ c = stmt.charCodeAt(++j);
1005
+ }
1006
+ return c === 42 ? pc : -1;
1007
+ }
1008
+ /* a primitive literal (never an object): property operations go straight to the runtime */
1009
+ const PRIM_LIT_RE = /^(?:-?\d|\(|"|'|true$|false$|null$|undefined$|\d)/;
1010
+ function is_prim_literal(code) {
1011
+ return PRIM_LIT_RE.test(code) && !code.startsWith('$');
1012
+ }
1013
+ function is_obj_guard(x) {
1014
+ return `(typeof ${x} === 'object' ? ${x} !== null : typeof ${x} === 'function')`;
1015
+ }
1016
+ class BlockEmitter {
1017
+ fi;
1018
+ blk;
1019
+ slots = [];
1020
+ out = [];
1021
+ scratch = 0;
1022
+ /* temps `$s<k>` that may occur in pending code of this block: those of the input stack and
1023
+ every temp slot pushed or materialized since (a pending expression can only mention a temp
1024
+ that existed as a slot). Lets push() skip scanning expression strings, which would make the
1025
+ host flatten every nested expression. */
1026
+ temp_seen = [];
1027
+ max_seen = -1;
1028
+ see_temp(i) {
1029
+ if (this.temp_seen[i] !== true) {
1030
+ this.temp_seen[i] = true;
1031
+ if (i > this.max_seen)
1032
+ this.max_seen = i;
1033
+ }
1034
+ }
1035
+ constructor(fi, blk) {
1036
+ this.fi = fi;
1037
+ this.blk = blk;
1038
+ for (let i = 0; i < blk.stateIn.length; i++) {
1039
+ this.slots.push(P(temp_name(i), 1 /* PK.TEMP */));
1040
+ this.see_temp(i);
1041
+ }
1042
+ }
1043
+ get depth() { return this.slots.length; }
1044
+ /* every statement starts with a position marker (see M): that of the expression it assigns,
1045
+ else of the current instruction */
1046
+ emit(stmt) {
1047
+ if (starts_with_marker(stmt)) {
1048
+ this.out.push(stmt);
1049
+ return;
1050
+ }
1051
+ const pc = stmt_expr_pc(stmt);
1052
+ this.out.push((pc >= 0 ? `/*@${pc}*/` : this.M()) + stmt);
1053
+ }
1054
+ /* Position marker: a comment "/*@<pc>*\/" that the host parses as whitespace; the table
1055
+ (line, column) -> pc is built from the source when a backtrace needs it (src/jit_stack.ts),
1056
+ so that a host stack frame of the compiled function maps back to the guest operation. Every pushed expression starts with
1057
+ one; property accesses and calls carry one before their `.`/`[`/`(` and one at their end,
1058
+ binary operators one at the operator: the hosts report one of those points. */
1059
+ M(pc = this.cur_pc) { return `/*@${pc}*/`; }
1060
+ /* a fresh scratch variable (declared at function level as $x0, $x1, ...) */
1061
+ tmp() { return `$x${this.scratch++}`; }
1062
+ /* materialize slot i into its canonical temp. Pending expressions below i are evaluated
1063
+ first so that the evaluation order of the original code is preserved. */
1064
+ materialize(i) {
1065
+ const p = this.slots[i];
1066
+ if (p.kind === 1 /* PK.TEMP */ && p.code === `$s${i}`)
1067
+ return;
1068
+ if (p.kind === 3 /* PK.EXPR */) {
1069
+ for (let j = 0; j < i; j++)
1070
+ if (this.slots[j].kind === 3 /* PK.EXPR */)
1071
+ this.materialize(j);
1072
+ }
1073
+ this.emit(`$s${i} = ${p.code};`);
1074
+ this.slots[i] = P(temp_name(i), 1 /* PK.TEMP */, { fresh: p.fresh, mkeys: p.mkeys });
1075
+ this.see_temp(i);
1076
+ }
1077
+ /* materialize every pending expression that is not trivially duplicable, in order */
1078
+ flush_exprs(upto = this.depth) {
1079
+ for (let i = 0; i < upto; i++)
1080
+ if (this.slots[i].kind === 3 /* PK.EXPR */)
1081
+ this.materialize(i);
1082
+ }
1083
+ /* materialize all pending reads of a local before writing it */
1084
+ flush_local(name) {
1085
+ for (let i = 0; i < this.depth; i++)
1086
+ if (this.slots[i].kind === 2 /* PK.LOCAL */ && this.slots[i].local === name)
1087
+ this.materialize(i);
1088
+ }
1089
+ /* materialize everything so that slot i holds `$s<i>` */
1090
+ finalize() {
1091
+ /* 1. expressions and literals that are not in canonical form */
1092
+ const moves = [];
1093
+ for (let i = 0; i < this.depth; i++) {
1094
+ const p = this.slots[i];
1095
+ if (p.kind === 1 /* PK.TEMP */) {
1096
+ if (p.code !== `$s${i}`)
1097
+ moves.push({ i, from: p.code });
1098
+ }
1099
+ else if (p.kind === 3 /* PK.EXPR */)
1100
+ this.materialize(i);
1101
+ }
1102
+ /* 2. literal/local assignments: the sources are not temps, no ordering issue, but they must
1103
+ happen after the temp moves if a temp move reads a slot they overwrite... simplest: do
1104
+ temp moves first via a parallel-move resolver, then the others. */
1105
+ this.parallel_moves(moves);
1106
+ for (let i = 0; i < this.depth; i++) {
1107
+ const p = this.slots[i];
1108
+ if (p.kind === 0 /* PK.LIT */ || p.kind === 2 /* PK.LOCAL */)
1109
+ this.materialize(i);
1110
+ }
1111
+ }
1112
+ parallel_moves(moves) {
1113
+ /* moves: $s<i> = from, where from is some $s<j>. Resolve cycles with $x scratch. */
1114
+ const pending = new Map(); /* dest -> src */
1115
+ for (const m of moves)
1116
+ pending.set(m.i, m.from);
1117
+ const isRead = (name) => { for (const s of pending.values())
1118
+ if (s === name)
1119
+ return true; return false; };
1120
+ while (pending.size) {
1121
+ let progress = false;
1122
+ for (const [dest, src] of [...pending]) {
1123
+ if (!isRead(`$s${dest}`)) {
1124
+ this.emit(`$s${dest} = ${src};`);
1125
+ this.slots[dest] = P(temp_name(dest), 1 /* PK.TEMP */);
1126
+ this.see_temp(dest);
1127
+ pending.delete(dest);
1128
+ progress = true;
1129
+ }
1130
+ }
1131
+ if (!progress) {
1132
+ /* break a cycle */
1133
+ const [dest, src] = pending.entries().next().value;
1134
+ const t = this.tmp();
1135
+ this.emit(`${t} = ${src};`);
1136
+ for (const [d2, s2] of pending)
1137
+ if (s2 === src)
1138
+ pending.set(d2, t);
1139
+ void dest;
1140
+ }
1141
+ }
1142
+ }
1143
+ push(p) {
1144
+ const idx = this.depth;
1145
+ /* a live slot below may still hold the temp `$s<idx>` (after dup/permutations): save it first */
1146
+ if (this.temp_seen[idx] === true) {
1147
+ const mine = temp_name(idx);
1148
+ for (let j = 0; j < idx; j++) {
1149
+ const q = this.slots[j];
1150
+ if ((q.kind === 1 /* PK.TEMP */ && q.code === mine) || (q.kind === 3 /* PK.EXPR */ && temps_of(q).includes(idx)))
1151
+ this.materialize(j);
1152
+ }
1153
+ }
1154
+ if (p.kind === 3 /* PK.EXPR */ && p.marked !== true) {
1155
+ p.code = this.M() + p.code;
1156
+ p.marked = true;
1157
+ }
1158
+ else if (p.kind === 1 /* PK.TEMP */) {
1159
+ /* a temp slot: `$s<k>` (k may differ from idx after permutations) */
1160
+ const code = p.code;
1161
+ if (code.length > 2 && code.charCodeAt(0) === 36 && code.charCodeAt(1) === 115) {
1162
+ const k = Number(code.slice(2));
1163
+ if (Number.isInteger(k))
1164
+ this.see_temp(k);
1165
+ }
1166
+ }
1167
+ this.slots.push(p);
1168
+ /* an expression that reads temps above its own slot must be evaluated now, before those
1169
+ temps are reused */
1170
+ if (p.kind === 3 /* PK.EXPR */ && this.max_seen > idx) {
1171
+ const t = temps_of(p);
1172
+ for (let k = 0; k < t.length; k++)
1173
+ if (t[k] > idx) {
1174
+ this.materialize(idx);
1175
+ break;
1176
+ }
1177
+ }
1178
+ }
1179
+ /* make every slot a canonical temp (used after permutations) */
1180
+ canonicalize() { this.finalize(); }
1181
+ pop() {
1182
+ if (!this.slots.length)
1183
+ bail('emit: stack underflow');
1184
+ return this.slots.pop();
1185
+ }
1186
+ top(i = 1) { return this.slots[this.slots.length - i]; }
1187
+ /* drop a slot: its side effects must still happen */
1188
+ drop_value(p) {
1189
+ if (p.kind === 3 /* PK.EXPR */)
1190
+ this.emit(`${p.code};`);
1191
+ }
1192
+ /* make the slot duplicable (temp/local/lit) and return its code */
1193
+ dup_code(i) {
1194
+ const p = this.slots[i];
1195
+ if (p.kind === 3 /* PK.EXPR */)
1196
+ this.materialize(i);
1197
+ return this.slots[i].code;
1198
+ }
1199
+ /* the value of a store that is written into two branches (fast native store / runtime
1200
+ fallback): an expression is evaluated once into `$w` first. Order-preserving: the statement
1201
+ follows immediately and the guard between them evaluates nothing. */
1202
+ store_value(v) {
1203
+ if (v.kind !== 3 /* PK.EXPR */)
1204
+ return v.code;
1205
+ this.fi.uses_w = true;
1206
+ this.emit(`$w = ${v.code};`);
1207
+ return '$w';
1208
+ }
1209
+ /* code of an operand for use inside a larger expression */
1210
+ par(p) {
1211
+ if (p.kind === 3 /* PK.EXPR */)
1212
+ return `(${p.code})`;
1213
+ return p.code;
1214
+ }
1215
+ /* before emitting a statement with side effects */
1216
+ before_stmt() { this.flush_exprs(); }
1217
+ /* --- variable access --- */
1218
+ arg_read(i) {
1219
+ const fi = this.fi;
1220
+ if (i >= fi.argName.length)
1221
+ return P('undefined', 0 /* PK.LIT */);
1222
+ return fi.argCell[i] ? P(`${fi.argName[i]}.get()`, 3 /* PK.EXPR */) : P(fi.argName[i], 2 /* PK.LOCAL */, { local: fi.argName[i] });
1223
+ }
1224
+ var_read(i) {
1225
+ const fi = this.fi;
1226
+ if (i >= fi.varName.length)
1227
+ bail(`bad local index ${i}`);
1228
+ return fi.varCell[i] ? P(`${fi.varName[i]}.get()`, 3 /* PK.EXPR */) : P(fi.varName[i], 2 /* PK.LOCAL */, { local: fi.varName[i] });
1229
+ }
1230
+ write_local(is_arg, i, value) {
1231
+ const fi = this.fi;
1232
+ const name = is_arg ? fi.argName[i] : fi.varName[i];
1233
+ const cell = is_arg ? fi.argCell[i] : fi.varCell[i];
1234
+ if (name === undefined)
1235
+ bail(`bad variable index ${i}`);
1236
+ if (cell) {
1237
+ this.before_stmt();
1238
+ this.emit(`${name}.set(${value});`);
1239
+ }
1240
+ else {
1241
+ this.flush_local(name);
1242
+ this.emit(`${name} = ${value};`);
1243
+ }
1244
+ }
1245
+ /* current value expression of a local (duplicable) */
1246
+ local_value(is_arg, i) {
1247
+ const fi = this.fi;
1248
+ const name = is_arg ? fi.argName[i] : fi.varName[i];
1249
+ const cell = is_arg ? fi.argCell[i] : fi.varCell[i];
1250
+ return cell ? `${name}.get()` : name;
1251
+ }
1252
+ /* --- property access and calls --- */
1253
+ guard_obj(x) { return is_obj_guard(x); }
1254
+ get_field(obj, atom) {
1255
+ if (is_prim_literal(obj))
1256
+ return `$RT.get_field(${obj}, ${atom})`;
1257
+ const M = this.M();
1258
+ return `(${this.guard_obj(obj)} ? ${obj}${M}${key_access(this.fi, atom)}${M} : $RT.get_field(${obj}, ${atom}))`;
1259
+ }
1260
+ get_el(obj, key) {
1261
+ if (is_prim_literal(obj))
1262
+ return `$RT.get_el(${obj}, ${key})`;
1263
+ const M = this.M();
1264
+ return `(${this.guard_obj(obj)} ? ${obj}${M}[${key}]${M} : $RT.get_el(${obj}, ${key}))`;
1265
+ }
1266
+ put_field_stmt(obj, atom, val) {
1267
+ const fi = this.fi;
1268
+ const M = this.M();
1269
+ if (is_prim_literal(obj))
1270
+ return `$RT.put_field(${obj}, ${atom}, ${val}, ${fi.strict});`;
1271
+ if (fi.strict)
1272
+ return `if (${this.guard_obj(obj)}) ${obj}${M}${key_access(fi, atom)} = ${val}; else $RT.put_field${M}(${obj}, ${atom}, ${val}, true);`;
1273
+ /* sloppy assignment semantics (no throw) come from a non-strict helper in the factory scope */
1274
+ let idx = fi.sloppy_props.get(atom);
1275
+ if (idx === undefined) {
1276
+ idx = fi.sloppy_props.size;
1277
+ fi.sloppy_props.set(atom, idx);
1278
+ }
1279
+ return `if (${this.guard_obj(obj)}) $sp${idx}${M}(${obj}, ${val}); else $RT.put_field${M}(${obj}, ${atom}, ${val}, false);`;
1280
+ }
1281
+ put_el_stmt(obj, key, val) {
1282
+ const fi = this.fi;
1283
+ const M = this.M();
1284
+ if (is_prim_literal(obj))
1285
+ return `$RT.put_el(${obj}, ${key}, ${val}, ${fi.strict});`;
1286
+ if (fi.strict)
1287
+ return `if (${this.guard_obj(obj)}) ${obj}${M}[${key}] = ${val}; else $RT.put_el${M}(${obj}, ${key}, ${val}, true);`;
1288
+ fi.uses_spe = true;
1289
+ return `if (${this.guard_obj(obj)}) $spe${M}(${obj}, ${key}, ${val}); else $RT.put_el${M}(${obj}, ${key}, ${val}, false);`;
1290
+ }
1291
+ /* call with an explicit `this`: the direct entry of guest functions, else the engine */
1292
+ call_with_this(fn, thisv, args) {
1293
+ this.fi.uses_direct = true;
1294
+ const a = args.join(', ');
1295
+ const M = this.M();
1296
+ return `(($d = $DIRECT.get(${fn})) !== undefined ? $d${M}(${thisv}, undefined${a ? ', ' + a : ''})${M} : $RT.call_slow${M}(${fn}, ${thisv}, [${a}])${M})`;
1297
+ }
1298
+ construct_with_target(fn, nt, args) {
1299
+ this.fi.uses_direct = true;
1300
+ const a = args.join(', ');
1301
+ const M = this.M();
1302
+ return `(($d = $DIRECT.get(${fn})) !== undefined ? $d${M}(undefined, ${nt}${a ? ', ' + a : ''})${M} : $RT.construct_slow${M}(${fn}, ${nt}, [${a}])${M})`;
1303
+ }
1304
+ /* returns true if the instruction terminated the block */
1305
+ translate(insn, last) {
1306
+ const fi = this.fi;
1307
+ const op = insn.op;
1308
+ switch (op) {
1309
+ /* --- constants --- */
1310
+ case 1 /* OP.push_i32 */:
1311
+ case 191 /* OP.push_i8 */:
1312
+ case 192 /* OP.push_i16 */:
1313
+ case 182 /* OP.push_minus1 */:
1314
+ case 183 /* OP.push_0 */:
1315
+ case 184 /* OP.push_1 */:
1316
+ case 185 /* OP.push_2 */:
1317
+ case 186 /* OP.push_3 */:
1318
+ case 187 /* OP.push_4 */:
1319
+ case 188 /* OP.push_5 */:
1320
+ case 189 /* OP.push_6 */:
1321
+ case 190 /* OP.push_7 */:
1322
+ this.push(P(fmt_number(insn.a), 0 /* PK.LIT */));
1323
+ return false;
1324
+ case 180 /* OP.push_bigint_i32 */:
1325
+ /* a negative literal is parenthesized: `-1n ** 2n` is a syntax error */
1326
+ this.push(P(insn.a < 0 ? `(${insn.a}n)` : `${insn.a}n`, 0 /* PK.LIT */));
1327
+ return false;
1328
+ case 2 /* OP.push_const */:
1329
+ case 193 /* OP.push_const8 */: {
1330
+ const v = fi.b.cpool[insn.a];
1331
+ const lit = v instanceof JSFunctionBytecode ? null : fmt_const(v, insn.a);
1332
+ this.push(lit ?? P(`$cpool[${insn.a}]`, 0 /* PK.LIT */, { cpool: insn.a }));
1333
+ return false;
1334
+ }
1335
+ case 195 /* OP.push_empty_string */:
1336
+ this.push(P('""', 0 /* PK.LIT */));
1337
+ return false;
1338
+ case 6 /* OP.undefined */:
1339
+ this.push(P('undefined', 0 /* PK.LIT */));
1340
+ return false;
1341
+ case 7 /* OP.null */:
1342
+ this.push(P('null', 0 /* PK.LIT */));
1343
+ return false;
1344
+ case 9 /* OP.push_false */:
1345
+ this.push(P('false', 0 /* PK.LIT */));
1346
+ return false;
1347
+ case 10 /* OP.push_true */:
1348
+ this.push(P('true', 0 /* PK.LIT */));
1349
+ return false;
1350
+ case 4 /* OP.push_atom_value */:
1351
+ if (fi.atom_is_symbol(insn.a))
1352
+ this.push(P(`$RT.atom_value(${insn.a})`, 3 /* PK.EXPR */));
1353
+ else if (__JS_AtomIsTaggedInt(insn.a))
1354
+ this.push(P(String(__JS_AtomToUInt32(insn.a)), 0 /* PK.LIT */));
1355
+ else
1356
+ this.push(P(JSON.stringify(fi.atom_name(insn.a)), 0 /* PK.LIT */));
1357
+ return false;
1358
+ case 5 /* OP.private_symbol */:
1359
+ this.push(P(`$RT.private_symbol(${insn.a})`, 3 /* PK.EXPR */));
1360
+ return false;
1361
+ case 8 /* OP.push_this */:
1362
+ if (fi.strict)
1363
+ this.push(P('$t', 2 /* PK.LOCAL */, { local: '$t' }));
1364
+ else
1365
+ this.push(P(`(($t === undefined || $t === null) ? $G : ((typeof $t === 'object' || typeof $t === 'function') ? $t : $RT.to_object($t)))`, 3 /* PK.EXPR */));
1366
+ return false;
1367
+ case 11 /* OP.object */:
1368
+ this.push(P('new $Obj()', 3 /* PK.EXPR */, { fresh: true, mkeys: null }));
1369
+ return false;
1370
+ case 12 /* OP.special_object */:
1371
+ switch (insn.a) {
1372
+ case 0 /* OPSpecialObject.ARGUMENTS */:
1373
+ this.push(P(`$RT.arguments(arguments, ${fi.arg_off})`, 3 /* PK.EXPR */));
1374
+ break;
1375
+ case 1 /* OPSpecialObject.MAPPED_ARGUMENTS */:
1376
+ this.push(P(`$RT.mapped_arguments(arguments, ${fi.arg_off}, [${fi.argName.join(', ')}], $fo)`, 3 /* PK.EXPR */));
1377
+ break;
1378
+ case 2 /* OPSpecialObject.THIS_FUNC */:
1379
+ this.push(P('$fo', 2 /* PK.LOCAL */, { local: '$fo' }));
1380
+ break;
1381
+ case 3 /* OPSpecialObject.NEW_TARGET */:
1382
+ /* a non constructible function (method, arrow body) is never constructed */
1383
+ if (fi.constructible)
1384
+ this.push(P('$nt', 2 /* PK.LOCAL */, { local: '$nt' }));
1385
+ else
1386
+ this.push(P('undefined', 0 /* PK.LIT */));
1387
+ break;
1388
+ case 4 /* OPSpecialObject.HOME_OBJECT */:
1389
+ this.push(P('$RT.home_object($fo)', 3 /* PK.EXPR */));
1390
+ break;
1391
+ case 5 /* OPSpecialObject.VAR_OBJECT */:
1392
+ this.push(P('$RT.new_object_null()', 3 /* PK.EXPR */));
1393
+ break;
1394
+ default: bail('special object ' + insn.a);
1395
+ }
1396
+ return false;
1397
+ case 13 /* OP.rest */:
1398
+ this.push(P(`$RT.rest(arguments, ${fi.arg_off}, ${insn.a})`, 3 /* PK.EXPR */));
1399
+ return false;
1400
+ /* --- stack manipulation --- */
1401
+ case 14 /* OP.drop */:
1402
+ this.drop_value(this.pop());
1403
+ return false;
1404
+ case 15 /* OP.nip */: {
1405
+ const y = this.pop();
1406
+ const x = this.pop();
1407
+ this.drop_value(x);
1408
+ this.push(y);
1409
+ return false;
1410
+ }
1411
+ case 16 /* OP.nip1 */: {
1412
+ const z = this.pop();
1413
+ const y = this.pop();
1414
+ const x = this.pop();
1415
+ this.drop_value(x);
1416
+ this.push(y);
1417
+ this.push(z);
1418
+ return false;
1419
+ }
1420
+ case 17 /* OP.dup */: {
1421
+ const c = this.dup_code(this.depth - 1);
1422
+ this.push({ ...this.top(), code: c });
1423
+ return false;
1424
+ }
1425
+ case 18 /* OP.dup1 */: { /* a b -> a a b */
1426
+ this.dup_code(this.depth - 2);
1427
+ this.dup_code(this.depth - 1);
1428
+ const b = this.pop();
1429
+ const a = this.pop();
1430
+ this.push(a);
1431
+ this.push({ ...a });
1432
+ this.push(b);
1433
+ return false;
1434
+ }
1435
+ case 19 /* OP.dup2 */: {
1436
+ this.dup_code(this.depth - 2);
1437
+ this.dup_code(this.depth - 1);
1438
+ const a = this.top(2), b = this.top(1);
1439
+ this.push({ ...a });
1440
+ this.push({ ...b });
1441
+ return false;
1442
+ }
1443
+ case 20 /* OP.dup3 */: {
1444
+ this.dup_code(this.depth - 3);
1445
+ this.dup_code(this.depth - 2);
1446
+ this.dup_code(this.depth - 1);
1447
+ const a = this.top(3), b = this.top(2), c = this.top(1);
1448
+ this.push({ ...a });
1449
+ this.push({ ...b });
1450
+ this.push({ ...c });
1451
+ return false;
1452
+ }
1453
+ case 21 /* OP.insert2 */:
1454
+ case 22 /* OP.insert3 */:
1455
+ case 23 /* OP.insert4 */:
1456
+ case 24 /* OP.perm3 */:
1457
+ case 25 /* OP.perm4 */:
1458
+ case 26 /* OP.perm5 */:
1459
+ case 27 /* OP.swap */:
1460
+ case 28 /* OP.swap2 */:
1461
+ case 29 /* OP.rot3l */:
1462
+ case 30 /* OP.rot3r */:
1463
+ case 31 /* OP.rot4l */:
1464
+ case 32 /* OP.rot5l */: {
1465
+ const oi = short_opcode_info(op);
1466
+ const n = oi.n_pop;
1467
+ for (let i = this.depth - n; i < this.depth; i++)
1468
+ this.dup_code(i);
1469
+ const v = this.slots.splice(this.depth - n, n);
1470
+ let r;
1471
+ switch (op) {
1472
+ case 21 /* OP.insert2 */:
1473
+ r = [v[1], v[0], v[1]];
1474
+ break;
1475
+ case 22 /* OP.insert3 */:
1476
+ r = [v[2], v[0], v[1], v[2]];
1477
+ break;
1478
+ case 23 /* OP.insert4 */:
1479
+ r = [v[3], v[0], v[1], v[2], v[3]];
1480
+ break;
1481
+ case 24 /* OP.perm3 */:
1482
+ r = [v[1], v[0], v[2]];
1483
+ break;
1484
+ case 25 /* OP.perm4 */:
1485
+ r = [v[2], v[0], v[1], v[3]];
1486
+ break;
1487
+ case 26 /* OP.perm5 */:
1488
+ r = [v[3], v[0], v[1], v[2], v[4]];
1489
+ break;
1490
+ case 27 /* OP.swap */:
1491
+ r = [v[1], v[0]];
1492
+ break;
1493
+ case 28 /* OP.swap2 */:
1494
+ r = [v[2], v[3], v[0], v[1]];
1495
+ break;
1496
+ case 29 /* OP.rot3l */:
1497
+ r = [v[1], v[2], v[0]];
1498
+ break;
1499
+ case 30 /* OP.rot3r */:
1500
+ r = [v[2], v[0], v[1]];
1501
+ break;
1502
+ case 31 /* OP.rot4l */:
1503
+ r = [v[1], v[2], v[3], v[0]];
1504
+ break;
1505
+ default:
1506
+ r = [v[1], v[2], v[3], v[4], v[0]];
1507
+ break;
1508
+ }
1509
+ for (const p of r)
1510
+ this.slots.push({ ...p });
1511
+ this.canonicalize();
1512
+ return false;
1513
+ }
1514
+ /* --- closures --- */
1515
+ case 3 /* OP.fclosure */:
1516
+ case 194 /* OP.fclosure8 */: {
1517
+ const child = fi.b.cpool[insn.a];
1518
+ if (!(child instanceof JSFunctionBytecode))
1519
+ bail('fclosure without bytecode');
1520
+ this.push(P(`$RT.closure($cpool[${insn.a}], ${this.closure_refs(child)}, $fo)`, 3 /* PK.EXPR */));
1521
+ return false;
1522
+ }
1523
+ /* --- calls --- */
1524
+ case 240 /* OP.call0 */:
1525
+ case 241 /* OP.call1 */:
1526
+ case 242 /* OP.call2 */:
1527
+ case 243 /* OP.call3 */:
1528
+ case 34 /* OP.call */:
1529
+ case 35 /* OP.tail_call */: {
1530
+ /* plain call: native (this = undefined, strict callees) */
1531
+ const argc = insn.a;
1532
+ const args = this.pop_args(argc);
1533
+ const fn = this.pop();
1534
+ const M = this.M();
1535
+ const e = `${this.par(fn)}${M}(${args.join(', ')})${M}`;
1536
+ if (op === 35 /* OP.tail_call */) {
1537
+ this.before_stmt();
1538
+ this.emit_return(e);
1539
+ return true;
1540
+ }
1541
+ this.push(P(e, 3 /* PK.EXPR */));
1542
+ return false;
1543
+ }
1544
+ case 36 /* OP.call_method */:
1545
+ case 37 /* OP.tail_call_method */: {
1546
+ const argc = insn.a;
1547
+ const args = this.pop_args(argc);
1548
+ const fn = this.top(1), thisv = this.top(2);
1549
+ let e;
1550
+ const a = args.join(', ');
1551
+ if (fn.method && fn.method.recv === thisv.code && a.length <= 400) {
1552
+ /* fused member call: evaluation order (receiver, property, arguments) is the
1553
+ bytecode's since the arguments are still pending expressions */
1554
+ this.pop();
1555
+ this.pop();
1556
+ const r = fn.method.recv;
1557
+ const M = this.M();
1558
+ e = `(${this.guard_obj(r)} ? ${r}${this.M(fn.method.pc)}${fn.method.access}${M}(${a})${M} : ${fn.method.prim}${M}(${fn.method.prim_args}, [${a}])${M})`;
1559
+ jit_stats.method_calls_fused++;
1560
+ }
1561
+ else {
1562
+ const f = this.dup_code(this.depth - 1);
1563
+ const t = this.dup_code(this.depth - 2);
1564
+ this.pop();
1565
+ this.pop();
1566
+ e = this.call_with_this(f, t, args);
1567
+ jit_stats.method_calls_unfused++;
1568
+ }
1569
+ if (op === 37 /* OP.tail_call_method */) {
1570
+ this.before_stmt();
1571
+ this.emit_return(e);
1572
+ return true;
1573
+ }
1574
+ this.push(P(e, 3 /* PK.EXPR */));
1575
+ return false;
1576
+ }
1577
+ case 33 /* OP.call_constructor */: {
1578
+ const argc = insn.a;
1579
+ const args = this.pop_args(argc);
1580
+ const nt = this.top(1), fn = this.top(2);
1581
+ let e;
1582
+ if ((nt.kind === 1 /* PK.TEMP */ || nt.kind === 2 /* PK.LOCAL */) && nt.kind === fn.kind && nt.code === fn.code) {
1583
+ /* `new C(args)`: new.target is the constructor itself */
1584
+ this.pop();
1585
+ this.pop();
1586
+ e = `new ${fn.code}${this.M()}(${args.join(', ')})${this.M()}`;
1587
+ }
1588
+ else {
1589
+ const t = this.dup_code(this.depth - 1);
1590
+ const f = this.dup_code(this.depth - 2);
1591
+ this.pop();
1592
+ this.pop();
1593
+ e = this.construct_with_target(f, t, args);
1594
+ }
1595
+ this.push(P(e, 3 /* PK.EXPR */));
1596
+ return false;
1597
+ }
1598
+ case 38 /* OP.array_from */: {
1599
+ const args = this.pop_args(insn.a);
1600
+ this.push(P(`$RT.array([${args.join(', ')}])`, 3 /* PK.EXPR */));
1601
+ return false;
1602
+ }
1603
+ case 39 /* OP.apply */: {
1604
+ const arr = this.pop();
1605
+ const thisv = this.pop();
1606
+ const fn = this.pop();
1607
+ this.push(P(`$RT.apply(${this.par(fn)}, ${this.par(thisv)}, ${this.par(arr)}, ${insn.a})`, 3 /* PK.EXPR */));
1608
+ return false;
1609
+ }
1610
+ case 40 /* OP.return */: {
1611
+ const v = this.pop();
1612
+ this.before_stmt();
1613
+ this.emit_return(v.code);
1614
+ return true;
1615
+ }
1616
+ case 41 /* OP.return_undef */:
1617
+ this.before_stmt();
1618
+ this.emit_return('undefined');
1619
+ return true;
1620
+ case 42 /* OP.check_ctor_return */: {
1621
+ const r = this.dup_code(this.depth - 1);
1622
+ this.before_stmt();
1623
+ this.emit(`if (!${this.guard_obj(r)} && ${r} !== undefined) $RT.throw_type_error("derived class constructor must return an object or undefined");`);
1624
+ this.push(P(`!${this.guard_obj(r)}`, 3 /* PK.EXPR */));
1625
+ return false;
1626
+ }
1627
+ case 43 /* OP.check_ctor */:
1628
+ this.before_stmt();
1629
+ this.emit(`if ($nt === undefined) $RT.throw_type_error("class constructors must be invoked with 'new'");`);
1630
+ return false;
1631
+ case 44 /* OP.init_ctor */:
1632
+ this.push(P(`$RT.init_ctor($fo, $nt, arguments, ${fi.arg_off})`, 3 /* PK.EXPR */));
1633
+ return false;
1634
+ case 45 /* OP.check_brand */: {
1635
+ const f = this.dup_code(this.depth - 1), o = this.dup_code(this.depth - 2);
1636
+ this.before_stmt();
1637
+ this.emit(`$RT.check_brand(${o}, ${f});`);
1638
+ return false;
1639
+ }
1640
+ case 46 /* OP.add_brand */: {
1641
+ const f = this.pop(), o = this.pop();
1642
+ this.before_stmt();
1643
+ this.emit(`$RT.add_brand(${this.par(o)}, ${this.par(f)});`);
1644
+ return false;
1645
+ }
1646
+ case 48 /* OP.throw */: {
1647
+ const v = this.pop();
1648
+ this.before_stmt();
1649
+ this.emit(`throw ${v.code};`);
1650
+ this.blk.term = { kind: 'none' };
1651
+ return true;
1652
+ }
1653
+ case 49 /* OP.throw_error */:
1654
+ this.before_stmt();
1655
+ this.emit(`$RT.throw_error(${insn.a}, ${insn.b});`);
1656
+ this.blk.term = { kind: 'none' };
1657
+ return true;
1658
+ case 52 /* OP.regexp */: {
1659
+ const flags = this.pop(), pat = this.pop();
1660
+ this.push(P(`$RT.regexp(${this.par(pat)}, ${this.par(flags)})`, 3 /* PK.EXPR */));
1661
+ return false;
1662
+ }
1663
+ case 53 /* OP.get_super */: {
1664
+ const o = this.dup_code(this.depth - 1);
1665
+ this.pop();
1666
+ this.push(P(`(${this.guard_obj(o)} ? $getproto(${o}) : $RT.get_prototype(${o}))`, 3 /* PK.EXPR */));
1667
+ return false;
1668
+ }
1669
+ /* --- globals --- */
1670
+ case 55 /* OP.check_var */:
1671
+ this.push(P(`$RT.check_var(${insn.a})`, 3 /* PK.EXPR */));
1672
+ return false;
1673
+ case 56 /* OP.get_var_undef */:
1674
+ case 57 /* OP.get_var */: {
1675
+ /* inline fast path: the global variable table ($GV, lexical globals, TDZ sentinel $U)
1676
+ then the global object ($G); the runtime handles the slow cases (undefined values,
1677
+ missing variables, errors) */
1678
+ const acc = this.global_access(insn.a);
1679
+ if (acc === null) {
1680
+ this.push(P(`$RT.get_global(${insn.a}, ${op === 57 /* OP.get_var */})`, 3 /* PK.EXPR */));
1681
+ return false;
1682
+ }
1683
+ fi.uses_global = true;
1684
+ const slow = `$RT.get_global(${insn.a}, ${op === 57 /* OP.get_var */})`;
1685
+ const g = op === 57 /* OP.get_var */ ? `(($g = $G${acc}) !== undefined ? $g : ${slow})` : `$G${acc}`;
1686
+ this.push(P(`(($g = $GV${acc}) !== undefined ? ($g === $U ? ${slow} : $g) : ${g})`, 3 /* PK.EXPR */));
1687
+ return false;
1688
+ }
1689
+ case 58 /* OP.put_var */:
1690
+ case 59 /* OP.put_var_init */: {
1691
+ const acc = this.global_access(insn.a);
1692
+ if (op === 59 /* OP.put_var_init */ || acc === null) {
1693
+ const v = this.pop();
1694
+ this.before_stmt();
1695
+ this.emit(`$RT.put_global(${insn.a}, ${v.code}, ${op - 58 /* OP.put_var */}, ${fi.strict});`);
1696
+ return false;
1697
+ }
1698
+ const v = this.dup_code(this.depth - 1);
1699
+ this.pop();
1700
+ this.before_stmt();
1701
+ const key = key_value(fi, insn.a);
1702
+ if (fi.strict) {
1703
+ this.emit(`if (${key} in $GV || !(${key} in $G)) $RT.put_global(${insn.a}, ${v}, 0, true); else $G${acc} = ${v};`);
1704
+ }
1705
+ else {
1706
+ /* sloppy assignment semantics (no throw) come from a non-strict helper in the factory scope */
1707
+ let idx = fi.sloppy_sets.get(insn.a);
1708
+ if (idx === undefined) {
1709
+ idx = fi.sloppy_sets.size;
1710
+ fi.sloppy_sets.set(insn.a, idx);
1711
+ }
1712
+ this.emit(`$ss${idx}(${v});`);
1713
+ }
1714
+ return false;
1715
+ }
1716
+ case 60 /* OP.put_var_strict */: {
1717
+ const v = this.pop();
1718
+ const flag = this.pop();
1719
+ this.before_stmt();
1720
+ this.emit(`$RT.put_global_strict(${insn.a}, ${this.par(flag)}, ${v.code});`);
1721
+ return false;
1722
+ }
1723
+ case 64 /* OP.check_define_var */:
1724
+ this.before_stmt();
1725
+ this.emit(`$RT.check_define_var(${insn.a}, ${insn.b});`);
1726
+ return false;
1727
+ case 63 /* OP.define_var */:
1728
+ this.before_stmt();
1729
+ this.emit(`$RT.define_var(${insn.a}, ${insn.b});`);
1730
+ return false;
1731
+ case 65 /* OP.define_func */: {
1732
+ const v = this.pop();
1733
+ this.before_stmt();
1734
+ this.emit(`$RT.define_func(${insn.a}, ${v.code}, ${insn.b});`);
1735
+ return false;
1736
+ }
1737
+ case 122 /* OP.make_loc_ref */:
1738
+ case 123 /* OP.make_arg_ref */:
1739
+ case 124 /* OP.make_var_ref_ref */: {
1740
+ /* the reference object is static (see KT.REF); only the property name is a value */
1741
+ this.push(P('undefined', 0 /* PK.LIT */));
1742
+ if (fi.atom_is_symbol(insn.a))
1743
+ this.push(P(`$RT.atom_value(${insn.a})`, 3 /* PK.EXPR */));
1744
+ else
1745
+ this.push(P(JSON.stringify(fi.atom_name(insn.a)), 0 /* PK.LIT */));
1746
+ return false;
1747
+ }
1748
+ case 125 /* OP.make_var_ref */: {
1749
+ const d = this.depth;
1750
+ this.before_stmt();
1751
+ this.emit(`$s${d} = $RT.get_global_ref(${insn.a}); $s${d + 1} = $s${d}[1]; $s${d} = $s${d}[0];`);
1752
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1753
+ this.push(P(`$s${d + 1}`, 1 /* PK.TEMP */));
1754
+ return false;
1755
+ }
1756
+ case 155 /* OP.delete_var */:
1757
+ this.push(P(`$RT.delete_global(${insn.a})`, 3 /* PK.EXPR */));
1758
+ return false;
1759
+ /* --- locals --- */
1760
+ case 89 /* OP.get_loc */:
1761
+ case 196 /* OP.get_loc8 */:
1762
+ case 199 /* OP.get_loc0 */:
1763
+ case 200 /* OP.get_loc1 */:
1764
+ case 201 /* OP.get_loc2 */:
1765
+ case 202 /* OP.get_loc3 */:
1766
+ this.push(this.var_read(insn.a));
1767
+ return false;
1768
+ case 90 /* OP.put_loc */:
1769
+ case 197 /* OP.put_loc8 */:
1770
+ case 203 /* OP.put_loc0 */:
1771
+ case 204 /* OP.put_loc1 */:
1772
+ case 205 /* OP.put_loc2 */:
1773
+ case 206 /* OP.put_loc3 */: {
1774
+ const v = this.pop();
1775
+ this.write_local(false, insn.a, v.code);
1776
+ return false;
1777
+ }
1778
+ case 91 /* OP.set_loc */:
1779
+ case 198 /* OP.set_loc8 */:
1780
+ case 207 /* OP.set_loc0 */:
1781
+ case 208 /* OP.set_loc1 */:
1782
+ case 209 /* OP.set_loc2 */:
1783
+ case 210 /* OP.set_loc3 */: {
1784
+ const c = this.dup_code(this.depth - 1);
1785
+ this.write_local(false, insn.a, c);
1786
+ return false;
1787
+ }
1788
+ case 92 /* OP.get_arg */:
1789
+ case 211 /* OP.get_arg0 */:
1790
+ case 212 /* OP.get_arg1 */:
1791
+ case 213 /* OP.get_arg2 */:
1792
+ case 214 /* OP.get_arg3 */:
1793
+ this.push(this.arg_read(insn.a));
1794
+ return false;
1795
+ case 93 /* OP.put_arg */:
1796
+ case 215 /* OP.put_arg0 */:
1797
+ case 216 /* OP.put_arg1 */:
1798
+ case 217 /* OP.put_arg2 */:
1799
+ case 218 /* OP.put_arg3 */: {
1800
+ const v = this.pop();
1801
+ this.write_local(true, insn.a, v.code);
1802
+ return false;
1803
+ }
1804
+ case 94 /* OP.set_arg */:
1805
+ case 219 /* OP.set_arg0 */:
1806
+ case 220 /* OP.set_arg1 */:
1807
+ case 221 /* OP.set_arg2 */:
1808
+ case 222 /* OP.set_arg3 */: {
1809
+ const c = this.dup_code(this.depth - 1);
1810
+ this.write_local(true, insn.a, c);
1811
+ return false;
1812
+ }
1813
+ case 95 /* OP.get_var_ref */:
1814
+ case 223 /* OP.get_var_ref0 */:
1815
+ case 224 /* OP.get_var_ref1 */:
1816
+ case 225 /* OP.get_var_ref2 */:
1817
+ case 226 /* OP.get_var_ref3 */:
1818
+ this.push(P(`$vr[${insn.a}].get()`, 3 /* PK.EXPR */));
1819
+ return false;
1820
+ case 96 /* OP.put_var_ref */:
1821
+ case 227 /* OP.put_var_ref0 */:
1822
+ case 228 /* OP.put_var_ref1 */:
1823
+ case 229 /* OP.put_var_ref2 */:
1824
+ case 230 /* OP.put_var_ref3 */: {
1825
+ const v = this.pop();
1826
+ this.before_stmt();
1827
+ this.emit(`$vr[${insn.a}].set(${v.code});`);
1828
+ return false;
1829
+ }
1830
+ case 97 /* OP.set_var_ref */:
1831
+ case 231 /* OP.set_var_ref0 */:
1832
+ case 232 /* OP.set_var_ref1 */:
1833
+ case 233 /* OP.set_var_ref2 */:
1834
+ case 234 /* OP.set_var_ref3 */: {
1835
+ const c = this.dup_code(this.depth - 1);
1836
+ this.before_stmt();
1837
+ this.emit(`$vr[${insn.a}].set(${c});`);
1838
+ return false;
1839
+ }
1840
+ case 103 /* OP.get_var_ref_check */: {
1841
+ const d = this.depth;
1842
+ this.before_stmt();
1843
+ this.emit(`$s${d} = $vr[${insn.a}].get(); if ($s${d} === $U) $RT.throw_uninitialized($b, ${insn.a}, true);`);
1844
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1845
+ return false;
1846
+ }
1847
+ case 104 /* OP.put_var_ref_check */: {
1848
+ const v = this.pop();
1849
+ this.before_stmt();
1850
+ this.emit(`if ($vr[${insn.a}].get() === $U) $RT.throw_uninitialized($b, ${insn.a}, true); $vr[${insn.a}].set(${v.code});`);
1851
+ return false;
1852
+ }
1853
+ case 105 /* OP.put_var_ref_check_init */: {
1854
+ const v = this.pop();
1855
+ this.before_stmt();
1856
+ this.emit(`if ($vr[${insn.a}].get() !== $U) $RT.throw_uninitialized($b, ${insn.a}, true); $vr[${insn.a}].set(${v.code});`);
1857
+ return false;
1858
+ }
1859
+ case 98 /* OP.set_loc_uninitialized */:
1860
+ this.write_local(false, insn.a, '$U');
1861
+ return false;
1862
+ case 99 /* OP.get_loc_check */:
1863
+ case 102 /* OP.get_loc_checkthis */: {
1864
+ const d = this.depth;
1865
+ this.before_stmt();
1866
+ this.emit(`$s${d} = ${this.local_value(false, insn.a)}; if ($s${d} === $U) $RT.throw_uninitialized($b, ${insn.a}, false);`);
1867
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1868
+ return false;
1869
+ }
1870
+ case 100 /* OP.put_loc_check */: {
1871
+ const v = this.pop();
1872
+ this.before_stmt();
1873
+ this.emit(`if (${this.local_value(false, insn.a)} === $U) $RT.throw_uninitialized($b, ${insn.a}, false);`);
1874
+ this.write_local(false, insn.a, v.code);
1875
+ return false;
1876
+ }
1877
+ case 101 /* OP.put_loc_check_init */: {
1878
+ const v = this.pop();
1879
+ this.before_stmt();
1880
+ this.emit(`if (${this.local_value(false, insn.a)} !== $U) $RT.throw_reference_error("'this' can be initialized only once");`);
1881
+ this.write_local(false, insn.a, v.code);
1882
+ return false;
1883
+ }
1884
+ case 106 /* OP.close_loc */:
1885
+ if (fi.varCell[insn.a]) {
1886
+ this.before_stmt();
1887
+ this.emit(`${fi.varName[insn.a]} = $RT.new_cell(${fi.varName[insn.a]}.get());`);
1888
+ }
1889
+ return false;
1890
+ /* --- control flow --- */
1891
+ case 109 /* OP.goto */:
1892
+ case 238 /* OP.goto8 */:
1893
+ case 239 /* OP.goto16 */:
1894
+ this.finalize();
1895
+ this.blk.term = { kind: 'jump', target: insn.label };
1896
+ return true;
1897
+ case 108 /* OP.if_true */:
1898
+ case 107 /* OP.if_false */:
1899
+ case 237 /* OP.if_true8 */:
1900
+ case 236 /* OP.if_false8 */: {
1901
+ let cond = this.pop();
1902
+ if (this.needs_moves() && cond.kind !== 0 /* PK.LIT */) {
1903
+ const t = this.tmp();
1904
+ this.flush_exprs();
1905
+ this.emit(`${t} = ${cond.code};`);
1906
+ cond = P(t, 1 /* PK.TEMP */);
1907
+ }
1908
+ this.finalize();
1909
+ const isTrue = (op === 108 /* OP.if_true */ || op === 237 /* OP.if_true8 */);
1910
+ this.blk.term = { kind: 'cond', cond: cond.code, tTrue: isTrue ? insn.label : insn.next, tFalse: isTrue ? insn.next : insn.label };
1911
+ return true;
1912
+ }
1913
+ case 110 /* OP.catch */:
1914
+ this.push(P('undefined', 0 /* PK.LIT */));
1915
+ return false;
1916
+ case 111 /* OP.gosub */: {
1917
+ const d = this.depth;
1918
+ const site = fi.A.sites.find(s => s.pc === insn.pc);
1919
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1920
+ this.finalize();
1921
+ this.emit(`$s${d} = ${site.id};`);
1922
+ this.blk.term = { kind: 'jump', target: insn.label };
1923
+ return true;
1924
+ }
1925
+ case 112 /* OP.ret */: {
1926
+ const k = fi.A.states[insn.pc][this.depth - 1];
1927
+ if (!k || k.t !== 3 /* KT.RET */)
1928
+ bail('ret without return address');
1929
+ this.pop();
1930
+ this.finalize();
1931
+ const d = this.depth;
1932
+ this.blk.term = { kind: 'multi', multi: { expr: `$s${d}`, cases: k.sites.map(id => ({ value: id, target: fi.A.sites[id].cont })) } };
1933
+ return true;
1934
+ }
1935
+ case 113 /* OP.nip_catch */: {
1936
+ const rv = this.pop();
1937
+ let i = this.depth - 1;
1938
+ while (i >= 0 && this.blk_kind(i).t !== 1 /* KT.CATCH */ && this.blk_kind(i).t !== 2 /* KT.CATCH0 */)
1939
+ i--;
1940
+ if (i < 0)
1941
+ bail('nip_catch');
1942
+ /* drop everything above the catch slot (their effects, if pending) and the catch slot */
1943
+ const dropped = this.slots.splice(i, this.depth - i);
1944
+ this.flush_exprs();
1945
+ for (const p of dropped)
1946
+ this.drop_value(p);
1947
+ this.push(rv);
1948
+ return false;
1949
+ }
1950
+ /* --- iteration --- */
1951
+ case 126 /* OP.for_in_start */: {
1952
+ const o = this.pop();
1953
+ this.push(P(`$RT.for_in_start(${this.par(o)})`, 3 /* PK.EXPR */));
1954
+ return false;
1955
+ }
1956
+ case 129 /* OP.for_in_next */: {
1957
+ const it = this.dup_code(this.depth - 1);
1958
+ const d = this.depth;
1959
+ this.before_stmt();
1960
+ this.emit(`$s${d} = $RT.for_in_next(${it}); if ($s${d} === $D) { $s${d} = undefined; $s${d + 1} = true; } else $s${d + 1} = false;`);
1961
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1962
+ this.push(P(`$s${d + 1}`, 1 /* PK.TEMP */));
1963
+ return false;
1964
+ }
1965
+ case 127 /* OP.for_of_start */: {
1966
+ const o = this.pop();
1967
+ const d = this.depth;
1968
+ this.before_stmt();
1969
+ this.emit(`$s${d} = $RT.get_iterator(${this.par(o)}); $s${d + 1} = ${this.get_field_atom(`$s${d}`, 'next')};`);
1970
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1971
+ this.push(P(`$s${d + 1}`, 1 /* PK.TEMP */));
1972
+ this.push(P('undefined', 0 /* PK.LIT */));
1973
+ return false;
1974
+ }
1975
+ case 130 /* OP.for_of_next */: {
1976
+ const offset = -3 - insn.a;
1977
+ const iter = this.depth + offset;
1978
+ if (iter < 0)
1979
+ bail('for_of_next offset');
1980
+ this.materialize(iter);
1981
+ this.materialize(iter + 1);
1982
+ const d = this.depth;
1983
+ this.before_stmt();
1984
+ this.emit(`$s${d} = $RT.for_of_next($s${iter}, $s${iter + 1}); if ($s${d} === $D) { $s${iter} = undefined; $s${d} = undefined; $s${d + 1} = true; } else $s${d + 1} = false;`);
1985
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
1986
+ this.push(P(`$s${d + 1}`, 1 /* PK.TEMP */));
1987
+ return false;
1988
+ }
1989
+ case 132 /* OP.iterator_check_object */: {
1990
+ const v = this.dup_code(this.depth - 1);
1991
+ this.before_stmt();
1992
+ this.emit(`if (!${this.guard_obj(v)}) $RT.throw_type_error("iterator must return an object");`);
1993
+ return false;
1994
+ }
1995
+ case 134 /* OP.iterator_close */: {
1996
+ const c = this.pop();
1997
+ const nx = this.pop();
1998
+ const it = this.pop();
1999
+ this.before_stmt();
2000
+ this.drop_value(c);
2001
+ this.drop_value(nx);
2002
+ const itc = it.kind === 3 /* PK.EXPR */ ? (() => { const t = this.tmp(); this.emit(`${t} = ${it.code};`); return t; })() : it.code;
2003
+ this.emit(`if (${itc} !== undefined) $RT.iterator_close(${itc});`);
2004
+ return false;
2005
+ }
2006
+ case 135 /* OP.iterator_next */: {
2007
+ /* iter next catch val -> iter next catch ret */
2008
+ const val = this.pop();
2009
+ const it = this.dup_code(this.depth - 3), nx = this.dup_code(this.depth - 2);
2010
+ this.push(P(this.call_with_this(nx, it, [this.par(val)]), 3 /* PK.EXPR */));
2011
+ return false;
2012
+ }
2013
+ case 136 /* OP.iterator_call */: {
2014
+ const val = this.dup_code(this.depth - 1);
2015
+ const it = this.dup_code(this.depth - 4);
2016
+ const d = this.depth;
2017
+ this.before_stmt();
2018
+ const t = this.tmp();
2019
+ this.emit(`${t} = $RT.iterator_call(${it}, ${val}, ${insn.a}); if (${t} === $D) $s${d} = true; else { $s${d - 1} = ${t}; $s${d} = false; }`);
2020
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
2021
+ return false;
2022
+ }
2023
+ /* --- properties --- */
2024
+ case 66 /* OP.get_field */: {
2025
+ const o = this.dup_code(this.depth - 1);
2026
+ this.pop();
2027
+ this.push(P(this.get_field(o, insn.a), 3 /* PK.EXPR */));
2028
+ return false;
2029
+ }
2030
+ case 67 /* OP.get_field2 */: {
2031
+ const o = this.dup_code(this.depth - 1);
2032
+ const method = is_prim_literal(o) ? null
2033
+ : { recv: o, access: key_access(fi, insn.a), prim: '$RT.call_method_prim', prim_args: `${o}, ${insn.a}`, pc: this.cur_pc };
2034
+ this.push(P(this.get_field(o, insn.a), 3 /* PK.EXPR */, { method }));
2035
+ return false;
2036
+ }
2037
+ case 235 /* OP.get_length */: {
2038
+ const o = this.dup_code(this.depth - 1);
2039
+ this.pop();
2040
+ this.push(P(is_prim_literal(o) ? `$RT.get_length(${o})` : `(${this.guard_obj(o)} ? ${o}${this.M()}.length${this.M()} : $RT.get_length(${o}))`, 3 /* PK.EXPR */));
2041
+ return false;
2042
+ }
2043
+ case 68 /* OP.put_field */: {
2044
+ const v = this.pop();
2045
+ const o = this.dup_code(this.depth - 1);
2046
+ this.pop();
2047
+ this.before_stmt();
2048
+ this.emit(this.put_field_stmt(o, insn.a, is_prim_literal(o) ? v.code : this.store_value(v)));
2049
+ return false;
2050
+ }
2051
+ case 72 /* OP.get_array_el */: {
2052
+ const k = this.dup_code(this.depth - 1);
2053
+ const o = this.dup_code(this.depth - 2);
2054
+ this.pop();
2055
+ this.pop();
2056
+ this.push(P(this.get_el(o, k), 3 /* PK.EXPR */));
2057
+ return false;
2058
+ }
2059
+ case 73 /* OP.get_array_el2 */: {
2060
+ const k = this.dup_code(this.depth - 1);
2061
+ const o = this.dup_code(this.depth - 2);
2062
+ this.pop();
2063
+ const method = is_prim_literal(o) ? null
2064
+ : { recv: o, access: `[${k}]`, prim: '$RT.call_method_prim_key', prim_args: `${o}, ${k}`, pc: this.cur_pc };
2065
+ this.push(P(this.get_el(o, k), 3 /* PK.EXPR */, { method }));
2066
+ return false;
2067
+ }
2068
+ case 74 /* OP.put_array_el */: {
2069
+ const v = this.pop();
2070
+ const k = this.dup_code(this.depth - 1);
2071
+ const o = this.dup_code(this.depth - 2);
2072
+ this.pop();
2073
+ this.pop();
2074
+ this.before_stmt();
2075
+ this.emit(this.put_el_stmt(o, k, is_prim_literal(o) ? v.code : this.store_value(v)));
2076
+ return false;
2077
+ }
2078
+ case 61 /* OP.get_ref_value */: {
2079
+ const rk = this.kind_at(this.depth - 2);
2080
+ if (rk.t === 4 /* KT.REF */) {
2081
+ this.push(this.ref_read(rk));
2082
+ return false;
2083
+ }
2084
+ const k = this.dup_code(this.depth - 1);
2085
+ const o = this.dup_code(this.depth - 2);
2086
+ this.push(P(`$RT.get_ref_value(${o}, ${k}, ${fi.strict})`, 3 /* PK.EXPR */));
2087
+ return false;
2088
+ }
2089
+ case 62 /* OP.put_ref_value */: {
2090
+ const rk = this.kind_at(this.depth - 3);
2091
+ const v = this.pop();
2092
+ const k = this.pop();
2093
+ const o = this.pop();
2094
+ if (rk.t === 4 /* KT.REF */) {
2095
+ this.drop_value(k);
2096
+ this.ref_write(rk, v.code);
2097
+ return false;
2098
+ }
2099
+ this.before_stmt();
2100
+ this.emit(`$RT.put_ref_value(${this.par(o)}, ${this.par(k)}, ${v.code}, ${fi.strict});`);
2101
+ return false;
2102
+ }
2103
+ case 75 /* OP.get_super_value */: {
2104
+ const k = this.pop();
2105
+ const o = this.pop();
2106
+ const t = this.pop();
2107
+ this.push(P(`$RT.get_super_value(${this.par(t)}, ${this.par(o)}, ${this.par(k)})`, 3 /* PK.EXPR */));
2108
+ return false;
2109
+ }
2110
+ case 76 /* OP.put_super_value */: {
2111
+ const v = this.pop();
2112
+ const k = this.pop();
2113
+ const o = this.pop();
2114
+ const t = this.pop();
2115
+ this.before_stmt();
2116
+ this.emit(`$RT.put_super_value(${this.par(t)}, ${this.par(o)}, ${this.par(k)}, ${v.code});`);
2117
+ return false;
2118
+ }
2119
+ case 77 /* OP.define_field */: {
2120
+ const v = this.pop();
2121
+ const o = this.dup_code(this.depth - 1);
2122
+ const slot = this.top();
2123
+ this.before_stmt();
2124
+ const name = __JS_AtomIsTaggedInt(insn.a) ? null : fi.atom_name(insn.a);
2125
+ if (slot.fresh && name !== null && name !== '__proto__' && !(slot.mkeys && slot.mkeys.has(name))) {
2126
+ /* fresh object literal: a plain assignment while Object.prototype has no accessors */
2127
+ const vc = this.store_value(v);
2128
+ this.emit(`if ($P.ok) ${o}${key_access(fi, insn.a)} = ${vc}; else $RT.define_field(${o}, ${insn.a}, ${vc});`);
2129
+ }
2130
+ else {
2131
+ this.emit(`$RT.define_field(${o}, ${insn.a}, ${v.code});`);
2132
+ }
2133
+ return false;
2134
+ }
2135
+ case 82 /* OP.define_array_el */: {
2136
+ const v = this.pop();
2137
+ const pos = this.dup_code(this.depth - 1);
2138
+ const arr = this.dup_code(this.depth - 2);
2139
+ this.before_stmt();
2140
+ this.emit(`$RT.define_array_el(${arr}, ${pos}, ${v.code});`);
2141
+ return false;
2142
+ }
2143
+ case 83 /* OP.append */: {
2144
+ const src = this.pop();
2145
+ const pos = this.depth - 1;
2146
+ this.materialize(pos);
2147
+ const arr = this.dup_code(this.depth - 2);
2148
+ this.before_stmt();
2149
+ this.emit(`$s${pos} = $RT.append(${arr}, $s${pos}, ${src.code});`);
2150
+ return false;
2151
+ }
2152
+ case 84 /* OP.copy_data_properties */: {
2153
+ const mask = insn.a;
2154
+ const d = this.depth;
2155
+ const tgt = this.dup_code(d - 1 - (mask & 3)), src = this.dup_code(d - 1 - ((mask >> 2) & 7)), excl = this.dup_code(d - 1 - ((mask >> 5) & 7));
2156
+ this.before_stmt();
2157
+ this.emit(`$RT.copy_data_properties(${tgt}, ${src}, ${excl});`);
2158
+ return false;
2159
+ }
2160
+ case 69 /* OP.get_private_field */: {
2161
+ const name = this.pop();
2162
+ const o = this.pop();
2163
+ this.push(P(`$RT.get_private_field(${this.par(o)}, ${this.par(name)})`, 3 /* PK.EXPR */));
2164
+ return false;
2165
+ }
2166
+ case 70 /* OP.put_private_field */: { /* obj value prop -> */
2167
+ const name = this.pop();
2168
+ const v = this.pop();
2169
+ const o = this.pop();
2170
+ this.before_stmt();
2171
+ this.emit(`$RT.put_private_field(${this.par(o)}, ${this.par(name)}, ${this.par(v)});`);
2172
+ return false;
2173
+ }
2174
+ case 71 /* OP.define_private_field */: { /* obj prop value -> obj */
2175
+ const v = this.pop();
2176
+ const name = this.pop();
2177
+ const o = this.dup_code(this.depth - 1);
2178
+ this.before_stmt();
2179
+ this.emit(`$RT.define_private_field(${o}, ${this.par(name)}, ${v.code});`);
2180
+ return false;
2181
+ }
2182
+ case 179 /* OP.private_in */: {
2183
+ const name = this.pop();
2184
+ const o = this.pop();
2185
+ this.push(P(`$RT.private_in(${this.par(o)}, ${this.par(name)})`, 3 /* PK.EXPR */));
2186
+ return false;
2187
+ }
2188
+ case 78 /* OP.set_name */: {
2189
+ const f = this.dup_code(this.depth - 1);
2190
+ this.before_stmt();
2191
+ this.emit(`$RT.set_name(${f}, ${insn.a});`);
2192
+ return false;
2193
+ }
2194
+ case 79 /* OP.set_name_computed */: {
2195
+ const f = this.dup_code(this.depth - 1), name = this.dup_code(this.depth - 2);
2196
+ this.before_stmt();
2197
+ this.emit(`$RT.set_name_computed(${f}, ${name});`);
2198
+ return false;
2199
+ }
2200
+ case 80 /* OP.set_proto */: {
2201
+ const p = this.pop();
2202
+ const o = this.dup_code(this.depth - 1);
2203
+ this.top().fresh = false;
2204
+ this.before_stmt();
2205
+ this.emit(`$RT.set_proto(${o}, ${p.code});`);
2206
+ return false;
2207
+ }
2208
+ case 81 /* OP.set_home_object */: {
2209
+ const f = this.dup_code(this.depth - 1), home = this.dup_code(this.depth - 2);
2210
+ this.before_stmt();
2211
+ this.emit(`$RT.set_home_object(${f}, ${home});`);
2212
+ return false;
2213
+ }
2214
+ case 85 /* OP.define_method */: {
2215
+ const f = this.pop();
2216
+ const o = this.dup_code(this.depth - 1);
2217
+ const slot = this.top();
2218
+ if (slot.fresh) {
2219
+ if (!slot.mkeys)
2220
+ slot.mkeys = new Set();
2221
+ slot.mkeys.add(fi.atom_name(insn.a));
2222
+ }
2223
+ this.before_stmt();
2224
+ this.emit(`$RT.define_method(${o}, ${insn.a}, ${f.code}, ${insn.b}, false);`);
2225
+ return false;
2226
+ }
2227
+ case 86 /* OP.define_method_computed */: {
2228
+ const f = this.pop();
2229
+ const name = this.pop();
2230
+ const o = this.dup_code(this.depth - 1);
2231
+ this.top().fresh = false;
2232
+ this.before_stmt();
2233
+ this.emit(`$RT.define_method(${o}, ${this.par(name)}, ${f.code}, ${insn.a}, true);`);
2234
+ return false;
2235
+ }
2236
+ case 87 /* OP.define_class */:
2237
+ case 88 /* OP.define_class_computed */: {
2238
+ /* [name] parent ctor_bytecode -> [name] ctor proto */
2239
+ const bf = this.pop();
2240
+ const parent = this.pop();
2241
+ if (bf.cpool < 0 || !(fi.b.cpool[bf.cpool] instanceof JSFunctionBytecode))
2242
+ bail('define_class without constructor bytecode');
2243
+ const child = fi.b.cpool[bf.cpool];
2244
+ const computed = op === 88 /* OP.define_class_computed */;
2245
+ const name = computed ? this.dup_code(this.depth - 1) : String(insn.a);
2246
+ const d = this.depth;
2247
+ this.before_stmt();
2248
+ this.emit(`$s${d} = $RT.define_class(${name}, ${this.par(parent)}, $cpool[${bf.cpool}], ${insn.b}, ${this.closure_refs(child)}, ${computed}); $s${d + 1} = $s${d}[1]; $s${d} = $s${d}[0];`);
2249
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
2250
+ this.push(P(`$s${d + 1}`, 1 /* PK.TEMP */));
2251
+ return false;
2252
+ }
2253
+ case 114 /* OP.to_object */: {
2254
+ const v = this.dup_code(this.depth - 1);
2255
+ this.pop();
2256
+ this.push(P(is_prim_literal(v) ? `$RT.to_object(${v})` : `(${this.guard_obj(v)} ? ${v} : $RT.to_object(${v}))`, 3 /* PK.EXPR */));
2257
+ return false;
2258
+ }
2259
+ case 115 /* OP.to_propkey */: {
2260
+ const v = this.dup_code(this.depth - 1);
2261
+ this.pop();
2262
+ this.push(P(`(typeof ${v} === 'string' || typeof ${v} === 'number' || typeof ${v} === 'symbol' ? ${v} : $RT.to_propkey(${v}))`, 3 /* PK.EXPR */));
2263
+ return false;
2264
+ }
2265
+ case 116 /* OP.to_propkey2 */: {
2266
+ const k = this.dup_code(this.depth - 1);
2267
+ const o = this.dup_code(this.depth - 2);
2268
+ this.pop();
2269
+ this.before_stmt();
2270
+ this.emit(`if (${o} === undefined || ${o} === null) $RT.throw_type_error("value has no property");`);
2271
+ this.push(P(`(typeof ${k} === 'string' || typeof ${k} === 'number' || typeof ${k} === 'symbol' ? ${k} : $RT.to_propkey(${k}))`, 3 /* PK.EXPR */));
2272
+ return false;
2273
+ }
2274
+ case 154 /* OP.delete */: {
2275
+ const k = this.dup_code(this.depth - 1);
2276
+ const o = this.dup_code(this.depth - 2);
2277
+ this.pop();
2278
+ this.pop();
2279
+ if (is_prim_literal(o))
2280
+ this.push(P(`$RT.delete(${o}, ${k}, ${fi.strict})`, 3 /* PK.EXPR */));
2281
+ else if (fi.strict)
2282
+ this.push(P(`(${this.guard_obj(o)} ? delete ${o}[${k}] : $RT.delete(${o}, ${k}, true))`, 3 /* PK.EXPR */));
2283
+ else {
2284
+ fi.uses_sdel = true;
2285
+ this.push(P(`(${this.guard_obj(o)} ? $sdel(${o}, ${k}) : $RT.delete(${o}, ${k}, false))`, 3 /* PK.EXPR */));
2286
+ }
2287
+ return false;
2288
+ }
2289
+ /* --- operators --- */
2290
+ case 159 /* OP.add */: return this.binop('+');
2291
+ case 160 /* OP.sub */: return this.binop('-');
2292
+ case 156 /* OP.mul */: return this.binop('*');
2293
+ case 157 /* OP.div */: return this.binop('/');
2294
+ case 158 /* OP.mod */: return this.binop('%');
2295
+ case 161 /* OP.pow */: return this.binop('**');
2296
+ case 162 /* OP.shl */: return this.binop('<<');
2297
+ case 163 /* OP.sar */: return this.binop('>>');
2298
+ case 164 /* OP.shr */: return this.binop('>>>');
2299
+ case 175 /* OP.and */: return this.binop('&');
2300
+ case 177 /* OP.or */: return this.binop('|');
2301
+ case 176 /* OP.xor */: return this.binop('^');
2302
+ case 165 /* OP.lt */: return this.binop('<');
2303
+ case 166 /* OP.lte */: return this.binop('<=');
2304
+ case 167 /* OP.gt */: return this.binop('>');
2305
+ case 168 /* OP.gte */: return this.binop('>=');
2306
+ case 171 /* OP.eq */: return this.binop('==');
2307
+ case 172 /* OP.neq */: return this.binop('!=');
2308
+ case 173 /* OP.strict_eq */: return this.binop('===');
2309
+ case 174 /* OP.strict_neq */: return this.binop('!==');
2310
+ case 150 /* OP.add_loc */: {
2311
+ const v = this.pop();
2312
+ const cur = this.local_value(false, insn.a);
2313
+ this.write_local(false, insn.a, `${cur} + ${this.par(v)}`);
2314
+ return false;
2315
+ }
2316
+ case 149 /* OP.inc_loc */:
2317
+ case 148 /* OP.dec_loc */: {
2318
+ const cur = this.local_value(false, insn.a);
2319
+ const o = op === 149 /* OP.inc_loc */ ? '+' : '-';
2320
+ this.write_local(false, insn.a, `(typeof ${cur} === 'number' ? ${cur} ${o} 1 : $RT.${op === 149 /* OP.inc_loc */ ? 'inc' : 'dec'}(${cur}))`);
2321
+ return false;
2322
+ }
2323
+ case 145 /* OP.inc */:
2324
+ case 144 /* OP.dec */: {
2325
+ const x = this.dup_code(this.depth - 1);
2326
+ this.pop();
2327
+ const o = op === 145 /* OP.inc */ ? '+' : '-';
2328
+ this.push(P(`(typeof ${x} === 'number' ? ${x} ${o} 1 : $RT.${op === 145 /* OP.inc */ ? 'inc' : 'dec'}(${x}))`, 3 /* PK.EXPR */));
2329
+ return false;
2330
+ }
2331
+ case 147 /* OP.post_inc */:
2332
+ case 146 /* OP.post_dec */: {
2333
+ const x = this.dup_code(this.depth - 1);
2334
+ this.pop();
2335
+ const d = this.depth;
2336
+ this.before_stmt();
2337
+ this.emit(`$s${d} = typeof ${x} === 'number' ? ${x} : $RT.to_numeric(${x});`);
2338
+ this.push(P(`$s${d}`, 1 /* PK.TEMP */));
2339
+ const o = op === 147 /* OP.post_inc */ ? '+' : '-';
2340
+ this.push(P(`(typeof $s${d} === 'number' ? $s${d} ${o} 1 : $RT.${op === 147 /* OP.post_inc */ ? 'inc' : 'dec'}($s${d}))`, 3 /* PK.EXPR */));
2341
+ return false;
2342
+ }
2343
+ case 142 /* OP.neg */:
2344
+ case 143 /* OP.plus */:
2345
+ case 151 /* OP.not */: {
2346
+ const x = this.pop();
2347
+ const o = op === 142 /* OP.neg */ ? '-' : op === 143 /* OP.plus */ ? '+' : '~';
2348
+ this.push(P(`${o}${this.par(x)}`, 3 /* PK.EXPR */));
2349
+ return false;
2350
+ }
2351
+ case 152 /* OP.lnot */: {
2352
+ const x = this.pop();
2353
+ this.push(P(`!${this.par(x)}`, 3 /* PK.EXPR */));
2354
+ return false;
2355
+ }
2356
+ case 153 /* OP.typeof */: {
2357
+ const x = this.pop();
2358
+ this.push(P(`typeof ${this.par(x)}`, 3 /* PK.EXPR */));
2359
+ return false;
2360
+ }
2361
+ case 246 /* OP.typeof_is_undefined */: {
2362
+ const x = this.pop();
2363
+ this.push(P(`(typeof ${this.par(x)} === 'undefined')`, 3 /* PK.EXPR */));
2364
+ return false;
2365
+ }
2366
+ case 247 /* OP.typeof_is_function */: {
2367
+ const x = this.pop();
2368
+ this.push(P(`(typeof ${this.par(x)} === 'function')`, 3 /* PK.EXPR */));
2369
+ return false;
2370
+ }
2371
+ case 244 /* OP.is_undefined */: {
2372
+ const x = this.pop();
2373
+ this.push(P(`(${this.par(x)} === undefined)`, 3 /* PK.EXPR */));
2374
+ return false;
2375
+ }
2376
+ case 245 /* OP.is_null */: {
2377
+ const x = this.pop();
2378
+ this.push(P(`(${this.par(x)} === null)`, 3 /* PK.EXPR */));
2379
+ return false;
2380
+ }
2381
+ case 178 /* OP.is_undefined_or_null */: {
2382
+ const x = this.dup_code(this.depth - 1);
2383
+ this.pop();
2384
+ this.push(P(`(${x} === undefined || ${x} === null)`, 3 /* PK.EXPR */));
2385
+ return false;
2386
+ }
2387
+ case 170 /* OP.in */: {
2388
+ const o = this.dup_code(this.depth - 1);
2389
+ const k = this.dup_code(this.depth - 2);
2390
+ this.pop();
2391
+ this.pop();
2392
+ if (is_prim_literal(o))
2393
+ this.push(P(`$RT.in(${k}, ${o})`, 3 /* PK.EXPR */));
2394
+ else
2395
+ this.push(P(`(${this.guard_obj(o)} ? (${k} in ${o}) : $RT.in(${k}, ${o}))`, 3 /* PK.EXPR */));
2396
+ return false;
2397
+ }
2398
+ case 169 /* OP.instanceof */: {
2399
+ const c = this.dup_code(this.depth - 1);
2400
+ const v = this.dup_code(this.depth - 2);
2401
+ this.pop();
2402
+ this.pop();
2403
+ if (is_prim_literal(c))
2404
+ this.push(P(`$RT.instanceof(${v}, ${c})`, 3 /* PK.EXPR */));
2405
+ else
2406
+ this.push(P(`(${this.guard_obj(c)} ? (${v} instanceof ${c}) : $RT.instanceof(${v}, ${c}))`, 3 /* PK.EXPR */));
2407
+ return false;
2408
+ }
2409
+ case 181 /* OP.nop */: return false;
2410
+ default:
2411
+ bail(`unsupported opcode ${insn.name}`);
2412
+ }
2413
+ }
2414
+ binop(o) {
2415
+ const b = this.pop();
2416
+ const a = this.pop();
2417
+ /* JSC reports a binary expression at its start (the marker push() adds), V8 at the operator */
2418
+ this.push(P(`${this.par(a)} ${this.M()}${o} ${this.par(b)}`, 3 /* PK.EXPR */));
2419
+ return false;
2420
+ }
2421
+ /* kind of slot i before the current instruction */
2422
+ kind_at(i) {
2423
+ const st = this.fi.A.states[this.cur_pc];
2424
+ return st[i] ?? KV;
2425
+ }
2426
+ blk_kind(i) { return this.kind_at(i); }
2427
+ cur_pc = 0;
2428
+ ref_read(k) {
2429
+ switch (k.ref_kind) {
2430
+ case 0 /* RefKind.LOC */: return this.var_read(k.ref_idx);
2431
+ case 1 /* RefKind.ARG */: return this.arg_read(k.ref_idx);
2432
+ default: return P(`$vr[${k.ref_idx}].get()`, 3 /* PK.EXPR */);
2433
+ }
2434
+ }
2435
+ ref_write(k, value) {
2436
+ switch (k.ref_kind) {
2437
+ case 0 /* RefKind.LOC */:
2438
+ this.write_local(false, k.ref_idx, value);
2439
+ break;
2440
+ case 1 /* RefKind.ARG */:
2441
+ this.write_local(true, k.ref_idx, value);
2442
+ break;
2443
+ default:
2444
+ this.before_stmt();
2445
+ this.emit(`$vr[${k.ref_idx}].set(${value});`);
2446
+ break;
2447
+ }
2448
+ }
2449
+ needs_moves() {
2450
+ for (let i = 0; i < this.depth; i++) {
2451
+ const p = this.slots[i];
2452
+ if (!(p.kind === 1 /* PK.TEMP */ && p.code === `$s${i}`))
2453
+ return true;
2454
+ }
2455
+ return false;
2456
+ }
2457
+ pop_args(argc) {
2458
+ const args = [];
2459
+ for (let i = 0; i < argc; i++)
2460
+ args.push(this.pop().code);
2461
+ return args.reverse();
2462
+ }
2463
+ get_field_atom(obj, name) {
2464
+ if (is_prim_literal(obj))
2465
+ return `$RT.get_field_str(${obj}, ${JSON.stringify(name)})`;
2466
+ return `(${this.guard_obj(obj)} ? ${obj}${this.M()}.${name}${this.M()} : $RT.get_field_str(${obj}, ${JSON.stringify(name)}))`;
2467
+ }
2468
+ emit_return(value) {
2469
+ const fi = this.fi;
2470
+ if (fi.constructible && !fi.derived) {
2471
+ const t = this.tmp();
2472
+ this.emit(`${t} = ${value}; return $nt !== undefined ? (${this.guard_obj(t)} ? ${t} : $t) : ${t};`);
2473
+ }
2474
+ else if (value.indexOf('(') >= 0) {
2475
+ /* never `return f()`: JavaScriptCore elides the frame of a strict-mode tail call, which
2476
+ would hide the function from backtraces and turn tail recursion into an endless loop
2477
+ instead of a stack overflow */
2478
+ this.emit(`$r = ${value}; return $r;`);
2479
+ }
2480
+ else {
2481
+ this.emit(`return ${value};`);
2482
+ }
2483
+ this.blk.term = { kind: 'none' };
2484
+ }
2485
+ /* `.name` / `["name"]` access text for a global variable atom, or null for a symbol */
2486
+ global_access(atom) {
2487
+ if (__JS_AtomIsTaggedInt(atom) || this.fi.atom_is_symbol(atom))
2488
+ return null;
2489
+ return key_access(this.fi, atom);
2490
+ }
2491
+ /* the var_refs array expression for a child closure */
2492
+ closure_refs(child) {
2493
+ const fi = this.fi;
2494
+ const refs = [];
2495
+ for (const cv of child.closure_var) {
2496
+ if (cv.is_local) {
2497
+ if (cv.is_arg) {
2498
+ if (!fi.argCell[cv.var_idx])
2499
+ bail('closure captures an argument that is not a cell');
2500
+ refs.push(fi.argName[cv.var_idx]);
2501
+ }
2502
+ else {
2503
+ if (!fi.varCell[cv.var_idx])
2504
+ bail('closure captures a local that is not a cell');
2505
+ refs.push(fi.varName[cv.var_idx]);
2506
+ }
2507
+ }
2508
+ else {
2509
+ refs.push(`$vr[${cv.var_idx}]`);
2510
+ }
2511
+ }
2512
+ return `[${refs.join(', ')}]`;
2513
+ }
2514
+ }
2515
+ class Relooper {
2516
+ fi;
2517
+ cfg;
2518
+ labelCount = 0;
2519
+ nodeMap = new Map(); /* all nodes of all regions, by node id */
2520
+ blockNode = new Map(); /* block id -> node id representing it in its parent's graph (block or region) */
2521
+ constructor(fi, cfg) {
2522
+ this.fi = fi;
2523
+ this.cfg = cfg;
2524
+ }
2525
+ label(prefix) { return `${prefix}${this.labelCount++}`; }
2526
+ /* region whose graph is being emitted */
2527
+ curRegion = null;
2528
+ /* node id representing the block at `pc` inside the current region graph: the block itself,
2529
+ or the child region that contains it */
2530
+ target_node(pc) {
2531
+ const tb = this.cfg.byPc.get(pc);
2532
+ if (!tb)
2533
+ bail(`branch to unknown block ${pc}`);
2534
+ const r = this.curRegion;
2535
+ if (tb.region === r)
2536
+ return tb.id;
2537
+ let d = tb.region;
2538
+ while (d && d.parent !== r)
2539
+ d = d.parent;
2540
+ if (d)
2541
+ return this.region_node_id(d);
2542
+ /* exit to an enclosing region: the block id (resolved through the context stack) */
2543
+ return tb.id;
2544
+ }
2545
+ region_node_id(r) { return -r.id - 1; }
2546
+ /* build the node graph of every region */
2547
+ build() {
2548
+ const { blocks, byPc } = this.cfg;
2549
+ /* block successors */
2550
+ for (const blk of blocks) {
2551
+ if (blk.dead)
2552
+ continue;
2553
+ const t = blk.term;
2554
+ const targets = [];
2555
+ const add = (pc) => {
2556
+ if (pc === undefined)
2557
+ return;
2558
+ const tb = byPc.get(pc);
2559
+ if (!tb)
2560
+ bail(`branch to unknown block ${pc}`);
2561
+ targets.push(tb.id);
2562
+ };
2563
+ switch (t.kind) {
2564
+ case 'fall':
2565
+ case 'jump':
2566
+ add(t.target);
2567
+ break;
2568
+ case 'cond':
2569
+ add(t.tTrue);
2570
+ add(t.tFalse);
2571
+ break;
2572
+ case 'multi':
2573
+ for (const c of t.multi.cases)
2574
+ add(c.target);
2575
+ break;
2576
+ case 'none': break;
2577
+ }
2578
+ blk.succs = [...new Set(targets)];
2579
+ }
2580
+ /* region exits and node graphs */
2581
+ for (const r of this.cfg.regions) {
2582
+ for (const blk of r.blocks) {
2583
+ for (const s of blk.succs) {
2584
+ const tb = blocks[s];
2585
+ if (tb.region === r)
2586
+ continue;
2587
+ /* descendant? */
2588
+ let d = tb.region;
2589
+ while (d && d.parent !== r)
2590
+ d = d.parent;
2591
+ if (d) {
2592
+ if (d.osr_chain)
2593
+ bail('osr: a try-region containing the loop header is re-entered');
2594
+ if (d.entry !== tb)
2595
+ bail('jump into the middle of a try-region');
2596
+ continue;
2597
+ }
2598
+ /* exit: must be in the parent region */
2599
+ if (!r.parent || tb.region !== r.parent)
2600
+ bail('try-region exit skips a level');
2601
+ r.exits.add(tb.id);
2602
+ }
2603
+ }
2604
+ }
2605
+ for (const r of this.cfg.regions) {
2606
+ for (const blk of r.blocks) {
2607
+ const succs = [];
2608
+ for (const s of blk.succs) {
2609
+ const tb = blocks[s];
2610
+ if (tb.region === r)
2611
+ succs.push(tb.id);
2612
+ else {
2613
+ let d = tb.region;
2614
+ while (d && d.parent !== r)
2615
+ d = d.parent;
2616
+ if (d)
2617
+ succs.push(this.region_node_id(d));
2618
+ /* else: exit, not an internal edge */
2619
+ }
2620
+ }
2621
+ this.nodeMap.set(blk.id, { id: blk.id, block: blk, region: null, succs });
2622
+ }
2623
+ for (const c of r.children) {
2624
+ const succs = [...c.exits];
2625
+ if (c.kind === 1 /* KT.CATCH */) {
2626
+ const hb = byPc.get(c.handler);
2627
+ if (!hb || hb.region !== r)
2628
+ bail('catch handler outside the parent region');
2629
+ if (!succs.includes(hb.id))
2630
+ succs.push(hb.id);
2631
+ }
2632
+ this.nodeMap.set(this.region_node_id(c), { id: this.region_node_id(c), block: null, region: c, succs });
2633
+ }
2634
+ }
2635
+ }
2636
+ /* node successors restricted to `nodes` and excluding loop back-edges */
2637
+ succs_in(n, nodes, loopEntries) {
2638
+ const r = [];
2639
+ for (const s of n.succs)
2640
+ if (nodes.has(s) && !(loopEntries && loopEntries.has(s)))
2641
+ r.push(s);
2642
+ return r;
2643
+ }
2644
+ reach(from, nodes, loopEntries) {
2645
+ const seen = new Set();
2646
+ const stack = [from];
2647
+ while (stack.length) {
2648
+ const succs = this.nodeMap.get(stack.pop()).succs;
2649
+ for (let i = 0; i < succs.length; i++) {
2650
+ const s = succs[i];
2651
+ if (nodes.has(s) && !(loopEntries !== null && loopEntries.has(s)) && !seen.has(s)) {
2652
+ seen.add(s);
2653
+ stack.push(s);
2654
+ }
2655
+ }
2656
+ }
2657
+ return seen;
2658
+ }
2659
+ /* nodes of `nodes` from which an entry is reachable (the entries included): one backward
2660
+ search instead of a forward search from every node. Same edges as reach(). */
2661
+ reaching(entries, nodes, loopEntries) {
2662
+ const preds = new Map();
2663
+ for (const id of nodes) {
2664
+ const succs = this.nodeMap.get(id).succs;
2665
+ for (let i = 0; i < succs.length; i++) {
2666
+ const s = succs[i];
2667
+ if (!nodes.has(s) || (loopEntries !== null && loopEntries.has(s)))
2668
+ continue;
2669
+ let l = preds.get(s);
2670
+ if (l === undefined) {
2671
+ l = [];
2672
+ preds.set(s, l);
2673
+ }
2674
+ l.push(id);
2675
+ }
2676
+ }
2677
+ const found = new Set(entries);
2678
+ const stack = [...entries];
2679
+ while (stack.length) {
2680
+ const l = preds.get(stack.pop());
2681
+ if (l === undefined)
2682
+ continue;
2683
+ for (let i = 0; i < l.length; i++)
2684
+ if (!found.has(l[i])) {
2685
+ found.add(l[i]);
2686
+ stack.push(l[i]);
2687
+ }
2688
+ }
2689
+ /* entries first, then the other nodes in the order of `nodes` (emission follows set order) */
2690
+ const inner = new Set(entries);
2691
+ for (const id of nodes)
2692
+ if (found.has(id))
2693
+ inner.add(id);
2694
+ return inner;
2695
+ }
2696
+ /* --- branch emission --- */
2697
+ branch_to(target, ctx, fallthroughOk) {
2698
+ /* prefer continue to an enclosing loop */
2699
+ for (let i = ctx.length - 1; i >= 0; i--) {
2700
+ const s = ctx[i];
2701
+ if (s.kind === 'loop' && s.entries.has(target)) {
2702
+ return (s.entries.size > 1 ? `$l = ${target}; ` : '') + `continue ${s.label};`;
2703
+ }
2704
+ }
2705
+ for (let i = ctx.length - 1; i >= 0; i--) {
2706
+ const s = ctx[i];
2707
+ if (s.kind === 'switch') {
2708
+ if (s.nodes.has(target))
2709
+ return `$l = ${target}; continue ${s.label};`;
2710
+ continue;
2711
+ }
2712
+ if (s.kind === 'tryswitch') {
2713
+ if (s.nextEntries.has(target))
2714
+ return `$l = ${target}; break ${s.label};`;
2715
+ continue;
2716
+ }
2717
+ if (s.nextEntries.has(target)) {
2718
+ if (fallthroughOk && fallthroughOk.has(target) && i === ctx.length - 1)
2719
+ return s.nextIsMulti ? `$l = ${target};` : '';
2720
+ return (s.nextIsMulti ? `$l = ${target}; ` : '') + `break ${s.label};`;
2721
+ }
2722
+ }
2723
+ bail(`unstructured branch to ${target}`);
2724
+ }
2725
+ /* the terminator of a block, given how each target is reached */
2726
+ emit_term(blk, ctx, fallthroughOk) {
2727
+ const t = blk.term;
2728
+ const idOf = (pc) => this.target_node(pc);
2729
+ switch (t.kind) {
2730
+ case 'none': return '';
2731
+ case 'fall':
2732
+ case 'jump': return this.branch_to(idOf(t.target), ctx, fallthroughOk);
2733
+ case 'cond': {
2734
+ const a = idOf(t.tTrue), b = idOf(t.tFalse);
2735
+ if (a === b)
2736
+ return this.branch_to(a, ctx, fallthroughOk);
2737
+ const ba = this.branch_to(a, ctx, null), bb = this.branch_to(b, ctx, fallthroughOk);
2738
+ return `if (${t.cond}) { ${ba} } ${bb}`;
2739
+ }
2740
+ case 'multi': {
2741
+ const parts = [];
2742
+ const cases = t.multi.cases;
2743
+ for (let i = 0; i < cases.length; i++) {
2744
+ const c = cases[i];
2745
+ const br = this.branch_to(idOf(c.target), ctx, i === cases.length - 1 ? fallthroughOk : null);
2746
+ if (i === cases.length - 1)
2747
+ parts.push(`{ ${br} }`);
2748
+ else
2749
+ parts.push(`if (${t.multi.expr} === ${c.value}) { ${br} } else `);
2750
+ }
2751
+ return parts.join('');
2752
+ }
2753
+ }
2754
+ }
2755
+ /* --- structured emission of a region graph --- */
2756
+ emit_region(r, ctx) {
2757
+ const nodes = new Set();
2758
+ for (const b of r.blocks)
2759
+ nodes.add(b.id);
2760
+ for (const c of r.children)
2761
+ nodes.add(this.region_node_id(c));
2762
+ const saved = this.curRegion;
2763
+ this.curRegion = r;
2764
+ try {
2765
+ if (jit_config.cfg === 'switch')
2766
+ return this.emit_switch(r, nodes, ctx);
2767
+ return this.reloop(nodes, [r.entry_node ?? r.entry.id], ctx, null);
2768
+ }
2769
+ finally {
2770
+ this.curRegion = saved;
2771
+ }
2772
+ }
2773
+ reloop(nodes, entries, ctx, loopEntries) {
2774
+ if (entries.length === 0 || nodes.size === 0)
2775
+ return '';
2776
+ if (entries.length === 1) {
2777
+ const e = entries[0];
2778
+ let hasPred = false;
2779
+ for (const id of nodes) {
2780
+ if (this.succs_in(this.nodeMap.get(id), nodes, loopEntries).includes(e)) {
2781
+ hasPred = true;
2782
+ break;
2783
+ }
2784
+ }
2785
+ if (!hasPred)
2786
+ return this.emit_simple(e, nodes, ctx, loopEntries);
2787
+ }
2788
+ /* loop: can some entry reach itself? */
2789
+ let isLoop = false;
2790
+ for (const e of entries)
2791
+ if (this.reach(e, nodes, loopEntries).has(e)) {
2792
+ isLoop = true;
2793
+ break;
2794
+ }
2795
+ if (isLoop) {
2796
+ const entrySet = new Set(entries);
2797
+ /* inner: nodes that can reach an entry */
2798
+ const inner = this.reaching(entries, nodes, loopEntries);
2799
+ const outer = new Set();
2800
+ for (const id of nodes)
2801
+ if (!inner.has(id))
2802
+ outer.add(id);
2803
+ const nextEntries = new Set();
2804
+ for (const id of inner)
2805
+ for (const s of this.succs_in(this.nodeMap.get(id), nodes, loopEntries))
2806
+ if (outer.has(s))
2807
+ nextEntries.add(s);
2808
+ const label = this.label('$L');
2809
+ const st = { kind: 'loop', label, entries: entrySet, nextEntries, nextIsMulti: nextEntries.size > 1 };
2810
+ const body = this.reloop(inner, entries, [...ctx, st], entrySet);
2811
+ return `${label}: for (;;) {\n${POLL}${body}\n}\n` + this.reloop(outer, [...nextEntries], ctx, loopEntries);
2812
+ }
2813
+ /* multiple */
2814
+ return this.emit_multiple(nodes, entries.map(e => ({ cond: `$l === ${e}`, entry: e })), ctx, loopEntries, '');
2815
+ }
2816
+ /* groups: nodes reachable from exactly one entry */
2817
+ groups(nodes, entries, loopEntries) {
2818
+ const reachFrom = new Map();
2819
+ for (const e of entries) {
2820
+ const s = this.reach(e, nodes, loopEntries);
2821
+ s.add(e);
2822
+ reachFrom.set(e, s);
2823
+ }
2824
+ const g = new Map();
2825
+ for (const e of entries) {
2826
+ const set = new Set();
2827
+ for (const id of reachFrom.get(e)) {
2828
+ let only = true;
2829
+ for (const e2 of entries)
2830
+ if (e2 !== e && reachFrom.get(e2).has(id)) {
2831
+ only = false;
2832
+ break;
2833
+ }
2834
+ if (only)
2835
+ set.add(id);
2836
+ }
2837
+ /* an entry that is reachable from another entry has no group of its own */
2838
+ if (!set.has(e))
2839
+ set.clear();
2840
+ g.set(e, set);
2841
+ }
2842
+ return g;
2843
+ }
2844
+ /* emit a dispatch on `conds` (if/else chain), each arm containing the independent group of
2845
+ its entry; `prefix` is code emitted before the dispatch (the preceding block) */
2846
+ emit_multiple(nodes, arms, ctx, loopEntries, prefix) {
2847
+ const isInternal = (e) => nodes.has(e) && !(loopEntries && loopEntries.has(e));
2848
+ const entries = [...new Set(arms.map(a => a.entry))].filter(isInternal);
2849
+ const g = this.groups(nodes, entries, loopEntries);
2850
+ const handled = new Set();
2851
+ for (const s of g.values())
2852
+ for (const id of s)
2853
+ handled.add(id);
2854
+ const remaining = new Set();
2855
+ for (const id of nodes)
2856
+ if (!handled.has(id))
2857
+ remaining.add(id);
2858
+ const remEntries = new Set();
2859
+ for (const e of entries)
2860
+ if (g.get(e).size === 0)
2861
+ remEntries.add(e);
2862
+ for (const id of handled)
2863
+ for (const s of this.succs_in(this.nodeMap.get(id), nodes, loopEntries))
2864
+ if (remaining.has(s))
2865
+ remEntries.add(s);
2866
+ const label = this.label('$M');
2867
+ const st = { kind: 'block', label, entries: new Set(), nextEntries: remEntries, nextIsMulti: remEntries.size > 1 };
2868
+ const innerCtx = [...ctx, st];
2869
+ let code = '';
2870
+ for (let i = 0; i < arms.length; i++) {
2871
+ const arm = arms[i];
2872
+ const grp = isInternal(arm.entry) ? g.get(arm.entry) : null;
2873
+ let body;
2874
+ if (grp === null)
2875
+ body = this.branch_to(arm.entry, innerCtx, null);
2876
+ else if (grp.size === 0)
2877
+ body = this.branch_to(arm.entry, innerCtx, remEntries);
2878
+ else
2879
+ body = this.reloop(grp, [arm.entry], innerCtx, loopEntries);
2880
+ if (arm.cond === null) {
2881
+ code += i > 0 ? `else {\n${body}\n}\n` : `{\n${body}\n}\n`;
2882
+ }
2883
+ else {
2884
+ code += `${i > 0 ? 'else ' : ''}if (${arm.cond}) {\n${body}\n}\n`;
2885
+ }
2886
+ }
2887
+ const out = `${prefix}${label}: {\n${code}\n}\n` + this.reloop(remaining, [...remEntries], ctx, loopEntries);
2888
+ return out;
2889
+ }
2890
+ emit_simple(e, nodes, ctx, loopEntries) {
2891
+ const node = this.nodeMap.get(e);
2892
+ const rest = new Set(nodes);
2893
+ rest.delete(e);
2894
+ const internal = this.succs_in(node, rest, loopEntries);
2895
+ const nextEntries = new Set(internal);
2896
+ if (node.region) {
2897
+ /* nested try-region */
2898
+ const r = node.region;
2899
+ const label = this.label('$T');
2900
+ const st = { kind: 'try', label, entries: new Set(), nextEntries, nextIsMulti: nextEntries.size > 1 };
2901
+ const innerCtx = [...ctx, st];
2902
+ const body = this.emit_region(r, innerCtx);
2903
+ let handler;
2904
+ if (r.kind === 1 /* KT.CATCH */) {
2905
+ const hb = this.cfg.byPc.get(r.handler);
2906
+ handler = `$s${r.slot} = $e; ${this.branch_to(hb.id, innerCtx, null)}`;
2907
+ }
2908
+ else {
2909
+ handler = `$RT.iterator_close_exc($s${r.slot - 2}); throw $e;`;
2910
+ }
2911
+ const code = `${label}: try {\n${body}\n} catch ($e) {\n$e = $RT.catchable($e); ${handler}\n}\n`;
2912
+ return code + this.reloop(rest, [...nextEntries], ctx, loopEntries);
2913
+ }
2914
+ const blk = node.block;
2915
+ const t = blk.term;
2916
+ const idOf = (pc) => this.target_node(pc);
2917
+ let code = blk.code;
2918
+ switch (t.kind) {
2919
+ case 'none':
2920
+ return code;
2921
+ case 'fall':
2922
+ case 'jump': {
2923
+ const target = idOf(t.target);
2924
+ if (rest.has(target) && !(loopEntries && loopEntries.has(target))) {
2925
+ /* fallthrough into the next structure */
2926
+ return code + this.reloop(rest, [target], ctx, loopEntries);
2927
+ }
2928
+ return code + this.branch_to(target, ctx, null) + '\n';
2929
+ }
2930
+ case 'cond': {
2931
+ const a = idOf(t.tTrue), b = idOf(t.tFalse);
2932
+ const aIn = rest.has(a) && !(loopEntries && loopEntries.has(a));
2933
+ const bIn = rest.has(b) && !(loopEntries && loopEntries.has(b));
2934
+ if (aIn && bIn && a !== b) {
2935
+ return this.emit_multiple(rest, [{ cond: t.cond, entry: a }, { cond: null, entry: b }], ctx, loopEntries, code);
2936
+ }
2937
+ if (aIn && !bIn) {
2938
+ return code + `if (!(${t.cond})) { ${this.branch_to(b, ctx, null)} }\n` + this.reloop(rest, [a], ctx, loopEntries);
2939
+ }
2940
+ if (bIn && !aIn) {
2941
+ return code + `if (${t.cond}) { ${this.branch_to(a, ctx, null)} }\n` + this.reloop(rest, [b], ctx, loopEntries);
2942
+ }
2943
+ if (a === b) {
2944
+ if (aIn)
2945
+ return code + this.reloop(rest, [a], ctx, loopEntries);
2946
+ return code + this.branch_to(a, ctx, null) + '\n';
2947
+ }
2948
+ return code + `if (${t.cond}) { ${this.branch_to(a, ctx, null)} }\n${this.branch_to(b, ctx, null)}\n`;
2949
+ }
2950
+ case 'multi': {
2951
+ const cases = t.multi.cases;
2952
+ const arms = cases.map((c, i) => ({ cond: i === cases.length - 1 ? null : `${t.multi.expr} === ${c.value}`, entry: idOf(c.target) }));
2953
+ const anyIn = arms.some(a => rest.has(a.entry) && !(loopEntries && loopEntries.has(a.entry)));
2954
+ if (anyIn && new Set(arms.map(a => a.entry)).size === arms.length) {
2955
+ return this.emit_multiple(rest, arms, ctx, loopEntries, code);
2956
+ }
2957
+ return code + this.emit_term(blk, ctx, null) + '\n';
2958
+ }
2959
+ }
2960
+ }
2961
+ /* --- state machine fallback --- */
2962
+ emit_switch(r, nodes, ctx) {
2963
+ const label = this.label('$S');
2964
+ const st = { kind: 'switch', label, entries: new Set(), nextEntries: new Set(), nextIsMulti: true, nodes };
2965
+ const innerCtx = [...ctx, st];
2966
+ let cases = '';
2967
+ for (const id of nodes) {
2968
+ const node = this.nodeMap.get(id);
2969
+ if (node.region) {
2970
+ const c = node.region;
2971
+ const tl = this.label('$T');
2972
+ const tst = { kind: 'tryswitch', label: tl, entries: new Set(), nextEntries: new Set(node.succs), nextIsMulti: true };
2973
+ const body = this.emit_region(c, [...innerCtx, tst]);
2974
+ let handler;
2975
+ if (c.kind === 1 /* KT.CATCH */) {
2976
+ const hb = this.cfg.byPc.get(c.handler);
2977
+ handler = `$s${c.slot} = $e; $l = ${hb.id}; break ${tl};`;
2978
+ }
2979
+ else {
2980
+ handler = `$RT.iterator_close_exc($s${c.slot - 2}); throw $e;`;
2981
+ }
2982
+ cases += `case ${id}: ${tl}: try {\n${body}\n} catch ($e) {\n$e = $RT.catchable($e); ${handler}\n}\ncontinue ${label};\n`;
2983
+ }
2984
+ else {
2985
+ const blk = node.block;
2986
+ cases += `case ${blk.id}: {\n${blk.code}${this.emit_term(blk, innerCtx, null)}\n}\n`;
2987
+ }
2988
+ }
2989
+ return `$l = ${r.entry_node ?? r.entry.id};\n${label}: for (;;) {\n${POLL}switch ($l) {\n${cases}default: $RT.throw_internal_error("jit: bad state " + $l);\n}\n}\n`;
2990
+ }
2991
+ }
2992
+ /* used by the runtime adapter when compiled code creates a closure: the compiled form if the
2993
+ function is (or should now be) compiled, else null (interpreter wrapper, tiered up later) */
2994
+ function jit_closure_compile(ctx, b) {
2995
+ if (b.compiled)
2996
+ return b.compiled;
2997
+ if (b.no_jit || jit_config.mode === 'off')
2998
+ return null;
2999
+ /* closures created by compiled code are compiled when created in hot mode too: the creating
3000
+ code is hot, so its closures are likely to run, and a compiled function object is cheaper
3001
+ to call than a wrapper (closures created by the interpreter stay lazy) */
3002
+ return jit_compile(ctx, b);
3003
+ }
3004
+ function func_name(ctx, b) {
3005
+ return b.func_name ? JS_AtomGetStr(ctx, b.func_name) : '';
3006
+ }
3007
+ export function jit_generate_source(ctx, b, id = 0, opts = {}) {
3008
+ if (b.func_kind !== 0 /* JSFunctionKind.NORMAL */)
3009
+ bail('generator or async function');
3010
+ const insns = decode(b);
3011
+ const A = analyze(b, insns);
3012
+ const cfg = build_blocks(insns, A);
3013
+ const osr = opts.osr;
3014
+ if (osr !== undefined) {
3015
+ if ((opts.conv ?? jit_config.conv) !== 'forwarder')
3016
+ bail('osr: needs the explicit convention');
3017
+ osr_prepare(cfg, A, osr);
3018
+ }
3019
+ const strict = (b.js_mode & JS_MODE_STRICT) !== 0;
3020
+ const constructible = b.has_prototype || b.is_derived_class_constructor || A.is_ctor;
3021
+ const fi = {
3022
+ ctx, b, A, strict, constructible, derived: b.is_derived_class_constructor, uses_direct: false, uses_global: false, uses_w: false,
3023
+ natural: (opts.conv ?? jit_config.conv) === 'natural', arg_off: (opts.conv ?? jit_config.conv) === 'natural' ? 0 : 2,
3024
+ sloppy_sets: new Map(), sloppy_props: new Map(), uses_spe: false, uses_sdel: false,
3025
+ argName: [], argCell: [], varName: [], varCell: [],
3026
+ atom_name: (atom) => JS_AtomGetStr(ctx, atom),
3027
+ atom_is_symbol: (atom) => ctx.rt.atoms.isSymbol(atom),
3028
+ };
3029
+ for (let i = 0; i < b.arg_count; i++) {
3030
+ const vd = b.vardefs[i];
3031
+ const cell = (vd?.is_captured ?? false) || A.uses_mapped_arguments;
3032
+ fi.argCell.push(cell);
3033
+ fi.argName.push(cell ? `$c${i}` : `$a${i}`);
3034
+ }
3035
+ for (let i = 0; i < b.var_count; i++) {
3036
+ const vd = b.vardefs[b.arg_count + i];
3037
+ const cell = vd?.is_captured ?? false;
3038
+ fi.varCell.push(cell);
3039
+ fi.varName.push(`$v${i}`);
3040
+ }
3041
+ /* variables referenced by child closures must be cells even if the parser did not flag them */
3042
+ for (const c of b.cpool) {
3043
+ if (!(c instanceof JSFunctionBytecode))
3044
+ continue;
3045
+ for (const cv of c.closure_var) {
3046
+ if (!cv.is_local)
3047
+ continue;
3048
+ if (cv.is_arg) {
3049
+ if (cv.var_idx < fi.argCell.length) {
3050
+ fi.argCell[cv.var_idx] = true;
3051
+ fi.argName[cv.var_idx] = `$c${cv.var_idx}`;
3052
+ }
3053
+ }
3054
+ else if (cv.var_idx < fi.varCell.length)
3055
+ fi.varCell[cv.var_idx] = true;
3056
+ }
3057
+ }
3058
+ /* blocks */
3059
+ let maxDepth = 0;
3060
+ let scratch = 0;
3061
+ for (const blk of cfg.blocks) {
3062
+ if (blk.dead)
3063
+ continue;
3064
+ const em = new BlockEmitter(fi, blk);
3065
+ /* translate instruction by instruction, tracking the pc for kind lookups */
3066
+ const n = blk.insns.length;
3067
+ let terminated = false;
3068
+ for (let idx = 0; idx < n; idx++) {
3069
+ const insn = blk.insns[idx];
3070
+ em.cur_pc = insn.pc;
3071
+ maxDepth = Math.max(maxDepth, em.depth + 4);
3072
+ if (em.translate(insn, idx === n - 1)) {
3073
+ if (idx !== n - 1)
3074
+ bail('terminator in the middle of a block');
3075
+ terminated = true;
3076
+ break;
3077
+ }
3078
+ maxDepth = Math.max(maxDepth, em.depth + 2);
3079
+ }
3080
+ if (!terminated) {
3081
+ em.finalize();
3082
+ blk.term = { kind: 'fall', target: blk.insns[n - 1].next };
3083
+ }
3084
+ blk.code = em.out.length ? em.out.join('\n') + '\n' : '';
3085
+ scratch = Math.max(scratch, em.scratch);
3086
+ }
3087
+ maxDepth = Math.max(maxDepth, b.stack_size + 2);
3088
+ const rl = new Relooper(fi, cfg);
3089
+ rl.build();
3090
+ const body = rl.emit_region(cfg.root, []);
3091
+ /* assemble */
3092
+ const name = func_name(ctx, b);
3093
+ const decls = [];
3094
+ for (let i = 0; i < maxDepth; i++)
3095
+ decls.push(`$s${i}`);
3096
+ for (let i = 0; i < scratch; i++)
3097
+ decls.push(`$x${i}`);
3098
+ decls.push('$l', '$e', '$r');
3099
+ if (fi.uses_direct)
3100
+ decls.push('$d');
3101
+ if (fi.uses_global)
3102
+ decls.push('$g');
3103
+ if (fi.uses_w)
3104
+ decls.push('$w');
3105
+ const params = ['$t', '$nt'];
3106
+ const fwparams = [];
3107
+ for (let i = 0; i < b.arg_count; i++) {
3108
+ params.push(`$a${i}`);
3109
+ fwparams.push(`$a${i}`);
3110
+ }
3111
+ /* every entry polls, as every loop head does: recursion can run unbounded without a back edge */
3112
+ let prologue = POLL;
3113
+ const natural = fi.natural;
3114
+ if (osr !== undefined) {
3115
+ /* replacement entry: the interpreter frame's state (cells for captured variables) */
3116
+ params.length = 2;
3117
+ params.push('$S');
3118
+ for (let i = 0; i < b.arg_count; i++)
3119
+ prologue += `let ${fi.argName[i]} = $S.a[${i}];\n`;
3120
+ for (let i = 0; i < b.var_count; i++)
3121
+ prologue += `let $v${i} = $S.v[${i}];\n`;
3122
+ const depth = cfg.byPc.get(osr).stateIn.length;
3123
+ for (let d = 0; d < depth; d++)
3124
+ prologue += `$s${d} = $S.s[${d}];\n`;
3125
+ }
3126
+ else {
3127
+ for (let i = 0; i < b.arg_count; i++)
3128
+ if (fi.argCell[i])
3129
+ prologue += `let $c${i} = $RT.new_cell($a${i});\n`;
3130
+ for (let i = 0; i < b.var_count; i++)
3131
+ prologue += fi.varCell[i] ? `let $v${i} = $RT.new_cell(undefined);\n` : `let $v${i};\n`;
3132
+ if (constructible) {
3133
+ if (!b.is_derived_class_constructor) {
3134
+ /* construct call: create `this` unless the caller already did (the host, for `new fw()`
3135
+ with an object prototype, or the interpreter's legacy constructor path) */
3136
+ if (natural)
3137
+ prologue += `if ($nt !== undefined && (typeof ($xp = $nt.prototype) === 'object' ? $xp === null : typeof $xp !== 'function')) $t = $RT.create_this($nt);\n`;
3138
+ else
3139
+ prologue += `if ($nt !== undefined && $t === undefined) $t = $RT.create_this($nt);\n`;
3140
+ }
3141
+ }
3142
+ else if (!natural) {
3143
+ prologue += `if ($nt !== undefined) $RT.throw_not_a_constructor($fo);\n`;
3144
+ }
3145
+ }
3146
+ const safe = name && IDENT_RE.test(name) ? name : '';
3147
+ const fname = safe ? `$f_${safe}` : '$f';
3148
+ const fwname = safe ? `$fw_${safe}` : '$fw';
3149
+ /* the forwarder is the guest-visible function object (natural host convention); it passes
3150
+ the declared parameters, or every argument when the body looks at `arguments`/rest */
3151
+ /* a rest parameter would forbid the "use strict" directive: forward `arguments` instead */
3152
+ const fwargs = A.uses_arguments ? [] : fwparams;
3153
+ const fwpass = A.uses_arguments ? ['...arguments'] : fwparams;
3154
+ const call = (t, nt) => `$direct(${[t, nt, ...fwpass].join(', ')})`;
3155
+ let forwarder;
3156
+ if (constructible) {
3157
+ /* `new fw(...)`: the host already created `this` from new.target.prototype; keep it when
3158
+ that prototype is an object (the direct entry creates `this` itself otherwise) */
3159
+ const host_this = `((typeof ($xp = new.target.prototype) === 'object' ? $xp !== null : typeof $xp === 'function') ? this : undefined)`;
3160
+ forwarder = `function ${fwname}(${fwargs.join(', ')}) { "use strict"; let $xp; return new.target === undefined ? ${call('this', 'undefined')} : ${call(host_this, 'new.target')}; }`;
3161
+ }
3162
+ else {
3163
+ forwarder = `({ ${fwname}(${fwargs.join(', ')}) { "use strict"; return ${call('this', 'undefined')}; } }).${fwname}`;
3164
+ }
3165
+ /* non-strict helpers for sloppy global assignments (the factory body itself is not strict) */
3166
+ let helpers = '';
3167
+ for (const [atom, idx] of fi.sloppy_sets) {
3168
+ helpers += `function $ss${idx}($v) { if (${key_value(fi, atom)} in $GV) $RT.put_global(${atom}, $v, 0, false); else $G${key_access(fi, atom)} = $v; }\n`;
3169
+ }
3170
+ for (const [atom, idx] of fi.sloppy_props)
3171
+ helpers += `function $sp${idx}($o, $v) { $o${key_access(fi, atom)} = $v; }\n`;
3172
+ if (fi.uses_spe)
3173
+ helpers += `function $spe($o, $k, $v) { $o[$k] = $v; }\n`;
3174
+ if (fi.uses_sdel)
3175
+ helpers += `function $sdel($o, $k) { return delete $o[$k]; }\n`;
3176
+ let source;
3177
+ if (!natural) {
3178
+ source =
3179
+ `${helpers}return function $make($vr, $fo) {
3180
+ const $direct = function ${fname}(${params.join(', ')}) {
3181
+ "use strict";
3182
+ let ${decls.join(', ')};
3183
+ ${prologue}${body}};
3184
+ if ($fo === undefined) $fo = ${forwarder};
3185
+ return [$fo, $direct, null];
3186
+ };`;
3187
+ }
3188
+ else {
3189
+ /* natural convention: the body is the guest function object itself; the direct entry is
3190
+ an adapter used by the engine and by calls with an explicit this/new.target */
3191
+ const nparams = params.slice(2);
3192
+ const head = `"use strict";\nlet ${decls.join(', ')}, $xp;\nlet $t = this;\n${constructible ? 'const $nt = new.target;\n' : ''}`;
3193
+ const fn = constructible
3194
+ ? `function ${fname}(${nparams.join(', ')}) {\n${head}${prologue}${body}}`
3195
+ : `({ ${fname}(${nparams.join(', ')}) {\n${head}${prologue}${body}} }).${fname}`;
3196
+ /* the private copy (`$mkpriv`, created on demand from the same literal, so it shares the
3197
+ host's code and type feedback) keeps the host Function.prototype and is never reachable
3198
+ from guest code: `priv.apply(t, args)` is the host intrinsic, unlike `$CALL.call` on the
3199
+ guest-visible object (~35 ns). With an existing function object (`$fo`, the tier-up of an
3200
+ interpreter wrapper) `$fn` itself is private. */
3201
+ const direct = constructible
3202
+ ? `function ($t, $nt, ...$a) { return $nt === undefined ? $mkpriv().apply($t, $a) : $CONSTRUCT($fn, $a, $nt); }`
3203
+ : `function ($t, $nt, ...$a) { if ($nt !== undefined) $RT.throw_not_a_constructor($fo); return $mkpriv().apply($t, $a); }`;
3204
+ source =
3205
+ `${helpers}return function $make($vr, $fo) {
3206
+ const $lit = function () { return ${fn}; };
3207
+ const $fn = $lit();
3208
+ if ($fo === undefined) $fo = $fn;
3209
+ let $priv = $fn === $fo ? null : $fn;
3210
+ const $mkpriv = function () { return $priv !== null ? $priv : ($priv = $lit()); };
3211
+ const $direct = ${direct};
3212
+ return [$fo, $direct, $mkpriv];
3213
+ };`;
3214
+ }
3215
+ /* the url names the function for host stack traces; its id leads to the position table */
3216
+ source += `\n//# sourceURL=jseption:${id}:${url_part(jit_file_name(ctx, b))}:${url_part(name || '<anonymous>')}`;
3217
+ const res = { source, name };
3218
+ if (osr !== undefined)
3219
+ res.osr = { arg_cell: fi.argCell.slice(), var_cell: fi.varCell.slice(), depth: cfg.byPc.get(osr).stateIn.length };
3220
+ return res;
3221
+ }
3222
+ /* a file or function name as it goes into the generated code's `sourceURL` comment: the file
3223
+ name is the embedder's and the function name the guest's, so a line terminator in either
3224
+ would end the comment and make the rest host code (a function declaration there is hoisted
3225
+ into the compiled code's scope). Control characters and line terminators become `_`. */
3226
+ function url_part(s) {
3227
+ return s.replace(/[\u0000-\u001f\u007f\u2028\u2029]/g, '_');
3228
+ }
3229
+ function jit_file_name(ctx, b) {
3230
+ return (b.has_debug && JS_AtomGetStr(ctx, b.debug.filename)) || '<eval>';
3231
+ }
3232
+ /* variant: 'main' = the function's own convention (jit_config.conv), stored in b.compiled;
3233
+ 'ctor' = explicit convention `(this | undefined, new_target, ...args)` for construct calls
3234
+ through retargetable wrappers, stored in b.compiled_ctor */
3235
+ export function jit_compile(ctx, b, variant = 'main') {
3236
+ if (variant === 'main' ? b.compiled : b.compiled_ctor)
3237
+ return (variant === 'main' ? b.compiled : b.compiled_ctor);
3238
+ if (b.no_jit || (variant === 'ctor' && b.compiled_ctor === false))
3239
+ return null;
3240
+ const c = compile_variant(ctx, b, variant === 'ctor' ? { conv: 'forwarder' } : {});
3241
+ if (c === null) {
3242
+ /* a bail of the function's own variant keeps it interpreted (a failed constructor variant
3243
+ only disables that variant); a host that refuses code generation leaves no trace */
3244
+ if (!jit_config.unavailable) {
3245
+ if (variant === 'main')
3246
+ b.no_jit = true;
3247
+ else
3248
+ b.compiled_ctor = false;
3249
+ }
3250
+ return null;
3251
+ }
3252
+ if (variant === 'main')
3253
+ b.compiled = c;
3254
+ else
3255
+ b.compiled_ctor = c;
3256
+ return c;
3257
+ }
3258
+ /* the on-stack replacement variant of `b` entered at the loop header `pc` (cached; null when
3259
+ the compiler bails for that header) */
3260
+ export function jit_compile_osr(ctx, b, pc) {
3261
+ if (b.osr === null)
3262
+ b.osr = new Map();
3263
+ let c = b.osr.get(pc);
3264
+ if (c !== undefined)
3265
+ return c;
3266
+ c = b.no_jit ? null : compile_variant(ctx, b, { conv: 'forwarder', osr: pc });
3267
+ b.osr.set(pc, c);
3268
+ return c;
3269
+ }
3270
+ function compile_variant(ctx, b, opts) {
3271
+ if (!jit_probe())
3272
+ return null;
3273
+ try {
3274
+ const id = jit_stack_alloc_id();
3275
+ const gen = jit_generate_source(ctx, b, id, opts);
3276
+ const { source, name } = gen;
3277
+ if (jit_config.dump)
3278
+ console.log(`--- jit${opts.osr !== undefined ? ` (osr at ${opts.osr})` : opts.conv === 'forwarder' && jit_config.conv !== 'forwarder' ? ' (ctor)' : ''}: ${name || '<anonymous>'} ---\n${source}`);
3279
+ const RT = make_jit_runtime(ctx, (bb) => jit_closure_compile(ctx, bb));
3280
+ let factory;
3281
+ try {
3282
+ factory = new Function(...JIT_FACTORY_PARAMS, source);
3283
+ }
3284
+ catch (e) {
3285
+ /* code generation from strings forbidden by the host: the tier turns itself off and the
3286
+ interpreter takes over (a SyntaxError is a compiler bug: reported below) */
3287
+ if (!(e instanceof SyntaxError) && !jit_probe(true))
3288
+ return null;
3289
+ throw e;
3290
+ }
3291
+ const make = factory(...jit_runtime_factory_args(RT, b));
3292
+ const stack = { b, name, file: jit_file_name(ctx, b), source, lines: null };
3293
+ jit_stack_register(id, stack);
3294
+ const compiled = { make, source, name, stack, osr: gen.osr };
3295
+ jit_stats.compiled++;
3296
+ if (opts.osr !== undefined)
3297
+ jit_stats.osr_compiled++;
3298
+ return compiled;
3299
+ }
3300
+ catch (e) {
3301
+ jit_stats.bailed++;
3302
+ const msg = e instanceof JitBail ? e.message : `internal: ${e?.message ?? e}`;
3303
+ jit_stats.bail_reasons.set(msg, (jit_stats.bail_reasons.get(msg) ?? 0) + 1);
3304
+ if (jit_config.dump || !(e instanceof JitBail))
3305
+ console.log(`--- jit bail: ${func_name(ctx, b) || '<anonymous>'}: ${msg}`);
3306
+ if (!(e instanceof JitBail) && jit_config.dump)
3307
+ console.log(e.stack);
3308
+ return null;
3309
+ }
3310
+ }
3311
+ export function jit_should_compile(b) {
3312
+ if (jit_config.mode === 'off' || b.no_jit)
3313
+ return false;
3314
+ if (b.compiled)
3315
+ return true;
3316
+ if (jit_config.mode === 'always')
3317
+ return true;
3318
+ return ++b.call_count >= jit_config.threshold;
3319
+ }
3320
+ /* backtraces of compiled frames (src/jit_stack.ts) while the tier is on */
3321
+ jit_stack_install(jit_config.mode !== 'off');
3322
+ /* JS_CallInternal entry hook (see src/jit_hook.ts) */
3323
+ jit_hooks.try_call = (ctx, func_obj, func_data, b, this_obj, new_target, argv, flags) => {
3324
+ let direct = func_data.compiled;
3325
+ if (direct === null) {
3326
+ if (jit_config.mode === 'off')
3327
+ return JIT_NOT_COMPILED;
3328
+ if (!jit_should_compile(b))
3329
+ return JIT_NOT_COMPILED;
3330
+ const compiled = jit_compile(ctx, b);
3331
+ if (!compiled)
3332
+ return JIT_NOT_COMPILED;
3333
+ /* tier-up of an existing function object (an interpreter wrapper): give the closure a
3334
+ compiled direct entry and its private copy; a retargetable wrapper calls the copy from
3335
+ now on, and a generic one reaches it through here */
3336
+ const [, d, mkpriv] = compiled.make(func_data.var_refs, func_obj);
3337
+ direct = d;
3338
+ func_data.compiled = d;
3339
+ func_data.mkpriv = mkpriv;
3340
+ func_data.priv = mkpriv !== null ? mkpriv() : null;
3341
+ if (!jit_is_wrapper(func_obj))
3342
+ js_direct.set(func_obj, d);
3343
+ }
3344
+ if ((flags & JS_CALL_FLAG_CONSTRUCTOR) !== 0 || new_target !== undefined) {
3345
+ /* construct call: the explicit-convention body (shared with the wrapper's construct path) */
3346
+ let c = func_data.ctor;
3347
+ if (c === null)
3348
+ c = jit_closure_ctor(ctx, func_obj, func_data, b);
3349
+ return jit_runtime_call_compiled(ctx, c ?? direct, this_obj, new_target, argv, flags);
3350
+ }
3351
+ let p = func_data.priv;
3352
+ if (p === null && func_data.mkpriv !== null)
3353
+ p = func_data.priv = func_data.mkpriv();
3354
+ if (p !== null)
3355
+ return jit_runtime_call_priv(ctx, p, this_obj, argv);
3356
+ return jit_runtime_call_compiled(ctx, direct, this_obj, new_target, argv, flags);
3357
+ };
3358
+ /* the explicit-convention constructor body of a closure (compiled on its first construct call
3359
+ through the engine or a wrapper), or null when the compiler bails */
3360
+ function jit_closure_ctor(ctx, func_obj, fd, b) {
3361
+ if (jit_config.conv === 'forwarder')
3362
+ return fd.compiled; /* the direct entry is the body */
3363
+ const c = jit_compile(ctx, b, 'ctor');
3364
+ if (c === null)
3365
+ return null;
3366
+ fd.ctor = c.make(fd.var_refs, func_obj)[1];
3367
+ return fd.ctor;
3368
+ }
3369
+ /* On-stack replacement (see JitHooks.osr): the interpreter frame `sf` of `func_obj` has taken
3370
+ enough backward jumps; its loop header is `pc` and its stack depth `sp`. Runs the rest of the
3371
+ frame in the OSR variant and returns its result (JS_EXCEPTION with the exception pending), or
3372
+ JIT_NOT_COMPILED to continue interpreting. */
3373
+ jit_set_osr_threshold(jit_config.osr_threshold);
3374
+ jit_hooks.osr = (ctx, func_obj, b, sf, pc, sp) => {
3375
+ if (jit_config.mode === 'off' || !jit_config.osr || b.no_jit) {
3376
+ b.loop_count = 0;
3377
+ return JIT_NOT_COMPILED;
3378
+ }
3379
+ const c = jit_compile_osr(ctx, b, pc);
3380
+ if (c === null || c.osr === undefined || c.osr.depth !== sp) {
3381
+ /* the compiler bailed for this header: back to counting (other headers may work) */
3382
+ b.loop_count = 0;
3383
+ return JIT_NOT_COMPILED;
3384
+ }
3385
+ const fd = JS_GetFunctionData(func_obj);
3386
+ const { arg_cell, var_cell, depth } = c.osr;
3387
+ /* captured variables: the frame's own variable references, detached (their values move into
3388
+ them) so that closures created so far stay connected to the compiled code */
3389
+ const cell = (is_arg, idx, v) => {
3390
+ const refs = sf.var_refs;
3391
+ for (let i = 0; i < refs.length; i++) {
3392
+ const vr = refs[i];
3393
+ if (!vr.is_detached && vr.is_arg === is_arg && vr.var_idx === idx) {
3394
+ vr.detach();
3395
+ return vr;
3396
+ }
3397
+ }
3398
+ return new_var_ref_cell(v);
3399
+ };
3400
+ const a = new Array(b.arg_count);
3401
+ for (let i = 0; i < b.arg_count; i++) {
3402
+ const v = i < sf.arg_buf.length ? sf.arg_buf[i] : undefined;
3403
+ a[i] = arg_cell[i] ? cell(true, i, v) : v;
3404
+ }
3405
+ const v = new Array(b.var_count);
3406
+ for (let i = 0; i < b.var_count; i++)
3407
+ v[i] = var_cell[i] ? cell(false, i, sf.var_buf[i]) : sf.var_buf[i];
3408
+ /* catch slots hold catch offsets in the interpreter and nothing in compiled code */
3409
+ const st = new Array(depth);
3410
+ for (let d = 0; d < depth; d++) {
3411
+ const x = sf.stack_buf[d];
3412
+ st[d] = JS_IsCatchOffset(x) ? undefined : x;
3413
+ }
3414
+ const direct = c.make(fd.var_refs, func_obj)[1];
3415
+ sf.osr = true;
3416
+ jit_stats.osr_entered++;
3417
+ const rt = ctx.rt;
3418
+ try {
3419
+ return direct(sf.this_obj, sf.new_target, { a, v, s: st });
3420
+ }
3421
+ catch (e) {
3422
+ return js_host_exception_to_guest(ctx, e);
3423
+ }
3424
+ finally {
3425
+ rt.current_stack_frame = sf;
3426
+ }
3427
+ };
3428
+ /* function objects created by jit_runtime_make_wrapper */
3429
+ const wrappers = new WeakSet();
3430
+ export function jit_is_wrapper(f) { return wrappers.has(f); }
3431
+ /* interpreted closures of normal functions are retargetable wrappers while the tier is on */
3432
+ jit_hooks.make_wrapper = (ctx, b, var_refs, proto, is_ctor) => {
3433
+ if (jit_config.mode === 'off')
3434
+ return null;
3435
+ const w = jit_runtime_make_wrapper(ctx, b, var_refs, proto, is_ctor);
3436
+ if (w !== null)
3437
+ wrappers.add(w);
3438
+ return w;
3439
+ };
3440
+ //# sourceMappingURL=jit.js.map