@tsvm/ir 0.1.0

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 (3) hide show
  1. package/dist/index.d.ts +238 -0
  2. package/dist/index.js +3593 -0
  3. package/package.json +33 -0
package/dist/index.js ADDED
@@ -0,0 +1,3593 @@
1
+ // src/ir.ts
2
+ var IRModuleBuilder = class {
3
+ constructor(sourceFile) {
4
+ this.sourceFile = sourceFile;
5
+ }
6
+ sourceFile;
7
+ functions = [];
8
+ globals = [];
9
+ imports = [];
10
+ exports = [];
11
+ constantPool = [];
12
+ constantMap = /* @__PURE__ */ new Map();
13
+ nextFunctionId = 1;
14
+ addFunction(fn) {
15
+ this.functions.push(fn);
16
+ }
17
+ addGlobal(g) {
18
+ this.globals.push(g);
19
+ }
20
+ addImport(imp) {
21
+ this.imports.push(imp);
22
+ }
23
+ addExport(exp) {
24
+ this.exports.push(exp);
25
+ }
26
+ addConstant(kind, value) {
27
+ const key = `${kind}:${value}`;
28
+ if (this.constantMap.has(key)) {
29
+ return this.constantMap.get(key);
30
+ }
31
+ const index = this.constantPool.length;
32
+ this.constantPool.push({ index, kind, value });
33
+ this.constantMap.set(key, index);
34
+ return index;
35
+ }
36
+ build() {
37
+ let blockCount = 0;
38
+ let instructionCount = 0;
39
+ for (const f of this.functions) {
40
+ blockCount += f.blocks.length;
41
+ for (const b of f.blocks) {
42
+ instructionCount += b.instructions.length;
43
+ }
44
+ }
45
+ return {
46
+ id: `mod_${Date.now()}`,
47
+ sourceFile: this.sourceFile,
48
+ functions: this.functions,
49
+ globals: this.globals,
50
+ imports: this.imports,
51
+ exports: this.exports,
52
+ constantPool: this.constantPool,
53
+ metadata: {
54
+ sourceFile: this.sourceFile,
55
+ originalByteSize: 0,
56
+ functionCount: this.functions.length,
57
+ blockCount,
58
+ instructionCount,
59
+ buildTimestamp: Date.now()
60
+ }
61
+ };
62
+ }
63
+ getNextFunctionId() {
64
+ return `fn_${this.nextFunctionId++}`;
65
+ }
66
+ };
67
+ var IRFunctionBuilder = class {
68
+ constructor(id, name, returnType) {
69
+ this.id = id;
70
+ this.name = name;
71
+ this.returnType = returnType;
72
+ }
73
+ id;
74
+ name;
75
+ returnType;
76
+ blocks = [];
77
+ params = [];
78
+ locals = [];
79
+ attributes = [];
80
+ capturedVariables = [];
81
+ nextRegId = 0;
82
+ nextBlockId = 0;
83
+ addParam(name, type, isRest = false, defaultValue) {
84
+ const reg = this.allocRegister();
85
+ this.params.push({ name, register: reg, type, isRest, defaultValue });
86
+ return reg;
87
+ }
88
+ addLocal(name, type, isCaptured = false) {
89
+ const reg = this.allocRegister();
90
+ this.locals.push({ name, register: reg, type, isCaptured });
91
+ return reg;
92
+ }
93
+ addAttribute(attr) {
94
+ if (!this.attributes.includes(attr)) {
95
+ this.attributes.push(attr);
96
+ }
97
+ }
98
+ addCapturedVariable(name) {
99
+ if (!this.capturedVariables.includes(name)) {
100
+ this.capturedVariables.push(name);
101
+ }
102
+ }
103
+ allocRegister() {
104
+ return `r${this.nextRegId++}`;
105
+ }
106
+ createBlock(label) {
107
+ const id = `b${this.nextBlockId++}`;
108
+ const block = new BasicBlockBuilder(id, label);
109
+ return block;
110
+ }
111
+ addBlock(block) {
112
+ this.blocks.push(block);
113
+ }
114
+ getBlockCount() {
115
+ return this.blocks.length;
116
+ }
117
+ build(isVirtualized = true, isExported = false) {
118
+ return {
119
+ id: this.id,
120
+ name: this.name,
121
+ params: this.params,
122
+ returnType: this.returnType,
123
+ blocks: this.blocks,
124
+ locals: this.locals,
125
+ isVirtualized,
126
+ isExported,
127
+ attributes: this.attributes,
128
+ capturedVariables: this.capturedVariables
129
+ };
130
+ }
131
+ };
132
+ var BasicBlockBuilder = class {
133
+ constructor(id, label) {
134
+ this.id = id;
135
+ this.label = label;
136
+ }
137
+ id;
138
+ label;
139
+ instructions = [];
140
+ terminator;
141
+ predecessors = [];
142
+ successors = [];
143
+ addInstruction(opcode, operands, result) {
144
+ this.instructions.push({ opcode, operands, result });
145
+ }
146
+ setTerminator(term) {
147
+ this.terminator = term;
148
+ this.successors = [...term.targets];
149
+ }
150
+ addPredecessor(id) {
151
+ if (!this.predecessors.includes(id)) {
152
+ this.predecessors.push(id);
153
+ }
154
+ }
155
+ getInstructionCount() {
156
+ return this.instructions.length;
157
+ }
158
+ getPredecessorCount() {
159
+ return this.predecessors.length;
160
+ }
161
+ getTerminatorKind() {
162
+ return this.terminator?.kind;
163
+ }
164
+ build() {
165
+ if (!this.terminator) {
166
+ this.terminator = { kind: "unreachable", targets: [] };
167
+ }
168
+ return {
169
+ id: this.id,
170
+ label: this.label,
171
+ instructions: this.instructions,
172
+ terminator: this.terminator,
173
+ predecessors: this.predecessors,
174
+ successors: this.successors,
175
+ phiNodes: []
176
+ };
177
+ }
178
+ };
179
+ function createInstruction(opcode, operands, result) {
180
+ return { opcode, operands, result };
181
+ }
182
+ function createTerminator(kind, targets, condition, returnValue) {
183
+ return { kind, targets, condition, returnValue };
184
+ }
185
+
186
+ // src/builder.ts
187
+ import ts3 from "typescript";
188
+ import { DiagnosticSeverity, IRType, OpCode as OpCode2, OperandKind as OperandKind2, ConstantKind as ConstantKind2, FunctionAttribute } from "@tsvm/shared";
189
+
190
+ // src/lowering/types.ts
191
+ var LEXICAL_THIS_CAPTURE = "$$vm_lexical_this";
192
+ var LEXICAL_NEW_TARGET_CAPTURE = "$$vm_lexical_new_target";
193
+
194
+ // src/lowering/utils.ts
195
+ import ts from "typescript";
196
+ function pushUnique(target, value) {
197
+ if (!target.includes(value)) {
198
+ target.push(value);
199
+ }
200
+ }
201
+ function isNestedFunctionLike(node) {
202
+ return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node) || ts.isConstructorDeclaration(node);
203
+ }
204
+ function isTypePosition(node) {
205
+ const parent = node.parent;
206
+ return ts.isTypeNode(parent) || ts.isTypeAliasDeclaration(parent) || ts.isHeritageClause(parent);
207
+ }
208
+ function isIdentifierReference(node) {
209
+ const parent = node.parent;
210
+ if (!parent || isTypePosition(node)) {
211
+ return false;
212
+ }
213
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node || ts.isPropertyAssignment(parent) && parent.name === node || ts.isShorthandPropertyAssignment(parent) && parent.name !== node || ts.isMethodDeclaration(parent) && parent.name === node || ts.isPropertyDeclaration(parent) && parent.name === node || ts.isPropertySignature(parent) && parent.name === node || ts.isBindingElement(parent) && parent.name === node || ts.isVariableDeclaration(parent) && parent.name === node || ts.isParameter(parent) && parent.name === node || ts.isFunctionDeclaration(parent) && parent.name === node || ts.isFunctionExpression(parent) && parent.name === node || ts.isLabeledStatement(parent) && parent.label === node || ts.isBreakOrContinueStatement(parent) && parent.label === node) {
214
+ return false;
215
+ }
216
+ return true;
217
+ }
218
+ function collectFunctionLocalNames(node) {
219
+ const names = /* @__PURE__ */ new Set();
220
+ const isThisParameter = (param) => ts.isIdentifier(param.name) && param.name.text === "this";
221
+ const collectBindingNames = (bindingName) => {
222
+ if (ts.isIdentifier(bindingName)) {
223
+ names.add(bindingName.text);
224
+ return;
225
+ }
226
+ for (const element of bindingName.elements) {
227
+ if (ts.isOmittedExpression(element)) {
228
+ continue;
229
+ }
230
+ collectBindingNames(element.name);
231
+ }
232
+ };
233
+ node.parameters.forEach((param) => {
234
+ if (isThisParameter(param)) {
235
+ return;
236
+ }
237
+ collectBindingNames(param.name);
238
+ });
239
+ if ((ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) && node.name) {
240
+ names.add(node.name.text);
241
+ }
242
+ const visit = (current) => {
243
+ if (current !== node && isNestedFunctionLike(current)) {
244
+ return;
245
+ }
246
+ if (ts.isVariableDeclaration(current)) {
247
+ collectBindingNames(current.name);
248
+ }
249
+ if (ts.isCatchClause(current) && current.variableDeclaration) {
250
+ collectBindingNames(current.variableDeclaration.name);
251
+ }
252
+ ts.forEachChild(current, visit);
253
+ };
254
+ if (node.body) {
255
+ ts.forEachChild(node.body, visit);
256
+ }
257
+ return names;
258
+ }
259
+ function analyzeFunctionClosures(node, availableOuterNames) {
260
+ const localNames = collectFunctionLocalNames(node);
261
+ const capturedFromOuter = [];
262
+ const capturedByDescendants = /* @__PURE__ */ new Set();
263
+ const childAvailableOuterNames = new Set(availableOuterNames);
264
+ for (const name of localNames) {
265
+ childAvailableOuterNames.add(name);
266
+ }
267
+ const visit = (current) => {
268
+ if (current !== node && isNestedFunctionLike(current)) {
269
+ const childAnalysis = analyzeFunctionClosures(current, childAvailableOuterNames);
270
+ for (const name of childAnalysis.capturedFromOuter) {
271
+ if (localNames.has(name) || name === LEXICAL_THIS_CAPTURE || name === LEXICAL_NEW_TARGET_CAPTURE) {
272
+ capturedByDescendants.add(name);
273
+ } else if (availableOuterNames.has(name)) {
274
+ pushUnique(capturedFromOuter, name);
275
+ }
276
+ }
277
+ return;
278
+ }
279
+ if (ts.isIdentifier(current) && isIdentifierReference(current)) {
280
+ const name = current.text;
281
+ if (!localNames.has(name) && availableOuterNames.has(name)) {
282
+ pushUnique(capturedFromOuter, name);
283
+ }
284
+ }
285
+ if (ts.isArrowFunction(node) && current.kind === ts.SyntaxKind.ThisKeyword && availableOuterNames.has(LEXICAL_THIS_CAPTURE)) {
286
+ pushUnique(capturedFromOuter, LEXICAL_THIS_CAPTURE);
287
+ }
288
+ if (ts.isArrowFunction(node) && ts.isMetaProperty(current) && current.keywordToken === ts.SyntaxKind.NewKeyword && current.name.text === "target" && availableOuterNames.has(LEXICAL_NEW_TARGET_CAPTURE)) {
289
+ pushUnique(capturedFromOuter, LEXICAL_NEW_TARGET_CAPTURE);
290
+ }
291
+ ts.forEachChild(current, visit);
292
+ };
293
+ if (node.body) {
294
+ ts.forEachChild(node.body, visit);
295
+ }
296
+ return {
297
+ localNames,
298
+ capturedFromOuter,
299
+ capturedByDescendants
300
+ };
301
+ }
302
+ var ScopeMap = class {
303
+ constructor(parent) {
304
+ this.parent = parent;
305
+ }
306
+ parent;
307
+ map = /* @__PURE__ */ new Map();
308
+ get(name) {
309
+ return this.map.get(name) ?? this.parent?.get(name);
310
+ }
311
+ set(name, binding) {
312
+ this.map.set(name, binding);
313
+ }
314
+ has(name) {
315
+ return this.map.has(name) || (this.parent?.has(name) ?? false);
316
+ }
317
+ hasOwn(name) {
318
+ return this.map.has(name);
319
+ }
320
+ delete(name) {
321
+ if (this.map.has(name)) {
322
+ return this.map.delete(name);
323
+ }
324
+ return this.parent?.delete(name) ?? false;
325
+ }
326
+ keys() {
327
+ const all = /* @__PURE__ */ new Set();
328
+ let curr = this;
329
+ while (curr) {
330
+ for (const k of curr.map.keys()) {
331
+ all.add(k);
332
+ }
333
+ curr = curr.parent;
334
+ }
335
+ return Array.from(all);
336
+ }
337
+ entries() {
338
+ const all = /* @__PURE__ */ new Map();
339
+ let curr = this;
340
+ const scopes = [];
341
+ while (curr) {
342
+ scopes.unshift(curr);
343
+ curr = curr.parent;
344
+ }
345
+ for (const scope of scopes) {
346
+ for (const [k, v] of scope.map.entries()) {
347
+ all.set(k, v);
348
+ }
349
+ }
350
+ return Array.from(all.entries());
351
+ }
352
+ };
353
+
354
+ // src/lowering/expressions.ts
355
+ import { OpCode, OperandKind, ConstantKind } from "@tsvm/shared";
356
+ import ts2 from "typescript";
357
+ function lowerConditionalExpression(self, conditionReg, whenTrue, whenFalse) {
358
+ const tempReg = self.createTempLocal("expr");
359
+ const trueBlock = self.fnBuilder.createBlock("expr_true");
360
+ const falseBlock = self.fnBuilder.createBlock("expr_false");
361
+ const endBlock = self.fnBuilder.createBlock("expr_end");
362
+ trueBlock.addPredecessor(self.currentBlock.id);
363
+ falseBlock.addPredecessor(self.currentBlock.id);
364
+ self.currentBlock.setTerminator({ kind: "branch", condition: conditionReg, targets: [trueBlock.id, falseBlock.id] });
365
+ self.fnBuilder.addBlock(self.currentBlock.build());
366
+ self.currentBlock = trueBlock;
367
+ const trueValue = whenTrue();
368
+ self.currentBlock.addInstruction(OpCode.StoreLocal, [
369
+ { kind: OperandKind.Register, value: tempReg },
370
+ { kind: OperandKind.Register, value: trueValue }
371
+ ]);
372
+ self.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
373
+ endBlock.addPredecessor(self.currentBlock.id);
374
+ self.fnBuilder.addBlock(self.currentBlock.build());
375
+ self.currentBlock = falseBlock;
376
+ const falseValue = whenFalse();
377
+ self.currentBlock.addInstruction(OpCode.StoreLocal, [
378
+ { kind: OperandKind.Register, value: tempReg },
379
+ { kind: OperandKind.Register, value: falseValue }
380
+ ]);
381
+ self.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
382
+ endBlock.addPredecessor(self.currentBlock.id);
383
+ self.fnBuilder.addBlock(self.currentBlock.build());
384
+ self.currentBlock = endBlock;
385
+ return self.loadFromLocal(tempReg);
386
+ }
387
+ function lowerNullishCoalesce(self, leftReg, rightExpr) {
388
+ const tempReg = self.createTempLocal("nullish");
389
+ self.currentBlock.addInstruction(OpCode.StoreLocal, [
390
+ { kind: OperandKind.Register, value: tempReg },
391
+ { kind: OperandKind.Register, value: leftReg }
392
+ ]);
393
+ const nullConst = self.emitConstant(ConstantKind.Null, null);
394
+ const undefConst = self.emitConstant(ConstantKind.Undefined, null);
395
+ const isNullReg = self.fnBuilder.allocRegister();
396
+ self.currentBlock.addInstruction(
397
+ OpCode.StrictEq,
398
+ [
399
+ { kind: OperandKind.Register, value: leftReg },
400
+ { kind: OperandKind.Register, value: nullConst }
401
+ ],
402
+ isNullReg
403
+ );
404
+ const rhsBlock = self.fnBuilder.createBlock("nullish_rhs");
405
+ const undefCheckBlock = self.fnBuilder.createBlock("nullish_undef");
406
+ const endBlock = self.fnBuilder.createBlock("nullish_end");
407
+ rhsBlock.addPredecessor(self.currentBlock.id);
408
+ undefCheckBlock.addPredecessor(self.currentBlock.id);
409
+ self.currentBlock.setTerminator({ kind: "branch", condition: isNullReg, targets: [rhsBlock.id, undefCheckBlock.id] });
410
+ self.fnBuilder.addBlock(self.currentBlock.build());
411
+ self.currentBlock = undefCheckBlock;
412
+ const isUndefReg = self.fnBuilder.allocRegister();
413
+ self.currentBlock.addInstruction(
414
+ OpCode.StrictEq,
415
+ [
416
+ { kind: OperandKind.Register, value: leftReg },
417
+ { kind: OperandKind.Register, value: undefConst }
418
+ ],
419
+ isUndefReg
420
+ );
421
+ rhsBlock.addPredecessor(self.currentBlock.id);
422
+ endBlock.addPredecessor(self.currentBlock.id);
423
+ self.currentBlock.setTerminator({ kind: "branch", condition: isUndefReg, targets: [rhsBlock.id, endBlock.id] });
424
+ self.fnBuilder.addBlock(self.currentBlock.build());
425
+ self.currentBlock = rhsBlock;
426
+ const rightReg = visitExpression(self, rightExpr);
427
+ self.currentBlock.addInstruction(OpCode.StoreLocal, [
428
+ { kind: OperandKind.Register, value: tempReg },
429
+ { kind: OperandKind.Register, value: rightReg }
430
+ ]);
431
+ self.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
432
+ endBlock.addPredecessor(self.currentBlock.id);
433
+ self.fnBuilder.addBlock(self.currentBlock.build());
434
+ self.currentBlock = endBlock;
435
+ return self.loadFromLocal(tempReg);
436
+ }
437
+ function lowerTemplateExpression(self, expr) {
438
+ let accReg = self.emitConstant(ConstantKind.String, expr.head.text);
439
+ for (const span of expr.templateSpans) {
440
+ const valueReg = visitExpression(self, span.expression);
441
+ const combinedValue = self.fnBuilder.allocRegister();
442
+ self.currentBlock.addInstruction(
443
+ OpCode.Add,
444
+ [
445
+ { kind: OperandKind.Register, value: accReg },
446
+ { kind: OperandKind.Register, value: valueReg }
447
+ ],
448
+ combinedValue
449
+ );
450
+ accReg = combinedValue;
451
+ if (span.literal.text.length > 0) {
452
+ const literalReg = self.emitConstant(ConstantKind.String, span.literal.text);
453
+ const combinedLiteral = self.fnBuilder.allocRegister();
454
+ self.currentBlock.addInstruction(
455
+ OpCode.Add,
456
+ [
457
+ { kind: OperandKind.Register, value: accReg },
458
+ { kind: OperandKind.Register, value: literalReg }
459
+ ],
460
+ combinedLiteral
461
+ );
462
+ accReg = combinedLiteral;
463
+ }
464
+ }
465
+ return accReg;
466
+ }
467
+ function lowerNestedFunctionNode(self, expr, explicitName) {
468
+ return self.lowerNestedFunctionLike(expr, explicitName);
469
+ }
470
+ function visitExpression(self, expr) {
471
+ expr = self.normalizeExpression(expr);
472
+ if (ts2.isNumericLiteral(expr)) {
473
+ return self.emitConstant(ConstantKind.Number, Number.parseFloat(expr.text));
474
+ }
475
+ if (ts2.isStringLiteral(expr) || ts2.isNoSubstitutionTemplateLiteral(expr)) {
476
+ return self.emitConstant(ConstantKind.String, expr.text);
477
+ }
478
+ if (ts2.isBigIntLiteral(expr)) {
479
+ const bigintGlobalReg = self.fnBuilder.allocRegister();
480
+ self.currentBlock.addInstruction(
481
+ OpCode.LoadGlobal,
482
+ [{ kind: OperandKind.Register, value: self.emitConstant(ConstantKind.String, "BigInt") }],
483
+ bigintGlobalReg
484
+ );
485
+ const valReg = self.emitConstant(ConstantKind.String, expr.text);
486
+ const resReg = self.fnBuilder.allocRegister();
487
+ self.currentBlock.addInstruction(
488
+ OpCode.Call,
489
+ [
490
+ { kind: OperandKind.Register, value: bigintGlobalReg },
491
+ { kind: OperandKind.Register, value: valReg }
492
+ ],
493
+ resReg
494
+ );
495
+ return resReg;
496
+ }
497
+ if (ts2.isRegularExpressionLiteral(expr)) {
498
+ const rawText = expr.text;
499
+ const lastSlashIndex = rawText.lastIndexOf("/");
500
+ const pattern = rawText.slice(1, lastSlashIndex);
501
+ const flags = rawText.slice(lastSlashIndex + 1);
502
+ const objectGlobalReg = self.fnBuilder.allocRegister();
503
+ self.currentBlock.addInstruction(
504
+ OpCode.LoadGlobal,
505
+ [{ kind: OperandKind.Register, value: self.emitConstant(ConstantKind.String, "RegExp") }],
506
+ objectGlobalReg
507
+ );
508
+ const patternReg = self.emitConstant(ConstantKind.String, pattern);
509
+ const flagsReg = self.emitConstant(ConstantKind.String, flags);
510
+ const resReg = self.fnBuilder.allocRegister();
511
+ self.currentBlock.addInstruction(
512
+ OpCode.New,
513
+ [
514
+ { kind: OperandKind.Register, value: objectGlobalReg },
515
+ { kind: OperandKind.Register, value: patternReg },
516
+ { kind: OperandKind.Register, value: flagsReg }
517
+ ],
518
+ resReg
519
+ );
520
+ return resReg;
521
+ }
522
+ if (ts2.isTemplateExpression(expr)) {
523
+ return lowerTemplateExpression(self, expr);
524
+ }
525
+ if (ts2.isAwaitExpression(expr)) {
526
+ if (!self.isAsyncFunction) {
527
+ self.failUnsupported(expr, "await outside async function is not supported");
528
+ }
529
+ const awaitedSourceReg = visitExpression(self, expr.expression);
530
+ const awaitedValueReg = self.fnBuilder.allocRegister();
531
+ self.currentBlock.addInstruction(OpCode.Await, [{ kind: OperandKind.Register, value: awaitedSourceReg }], awaitedValueReg);
532
+ return awaitedValueReg;
533
+ }
534
+ if (ts2.isYieldExpression(expr)) {
535
+ if (!self.isGenerator) {
536
+ self.failUnsupported(expr, "yield outside generator is not supported");
537
+ }
538
+ const yieldedValueReg = expr.expression ? visitExpression(self, expr.expression) : self.emitConstant(ConstantKind.Undefined, null);
539
+ const resReg = self.fnBuilder.allocRegister();
540
+ if (expr.asteriskToken) {
541
+ self.currentBlock.addInstruction(OpCode.YieldStar, [{ kind: OperandKind.Register, value: yieldedValueReg }], resReg);
542
+ } else {
543
+ self.currentBlock.addInstruction(OpCode.Yield, [{ kind: OperandKind.Register, value: yieldedValueReg }], resReg);
544
+ }
545
+ return resReg;
546
+ }
547
+ if (expr.kind === ts2.SyntaxKind.NullKeyword) return self.emitConstant(ConstantKind.Null, null);
548
+ if (expr.kind === ts2.SyntaxKind.TrueKeyword) return self.emitConstant(ConstantKind.Boolean, true);
549
+ if (expr.kind === ts2.SyntaxKind.FalseKeyword) return self.emitConstant(ConstantKind.Boolean, false);
550
+ if (expr.kind === ts2.SyntaxKind.ThisKeyword) {
551
+ if (ts2.isArrowFunction(self.node) && (self.scope.has(LEXICAL_THIS_CAPTURE) || self.outerCaptureBindings.has(LEXICAL_THIS_CAPTURE))) {
552
+ return self.resolveLexicalCapture(
553
+ LEXICAL_THIS_CAPTURE,
554
+ expr,
555
+ "lexical this in arrow function requires an enclosing function context"
556
+ );
557
+ }
558
+ const thisReg = self.fnBuilder.allocRegister();
559
+ self.currentBlock.addInstruction(OpCode.LoadThis, [], thisReg);
560
+ return thisReg;
561
+ }
562
+ if (ts2.isMetaProperty(expr)) {
563
+ if (expr.keywordToken === ts2.SyntaxKind.NewKeyword && expr.name.text === "target") {
564
+ if (ts2.isArrowFunction(self.node) && (self.scope.has(LEXICAL_NEW_TARGET_CAPTURE) || self.outerCaptureBindings.has(LEXICAL_NEW_TARGET_CAPTURE))) {
565
+ return self.resolveLexicalCapture(
566
+ LEXICAL_NEW_TARGET_CAPTURE,
567
+ expr,
568
+ "lexical new.target in arrow function requires an enclosing function context"
569
+ );
570
+ }
571
+ const newTargetReg = self.fnBuilder.allocRegister();
572
+ self.currentBlock.addInstruction(OpCode.LoadNewTarget, [], newTargetReg);
573
+ return newTargetReg;
574
+ }
575
+ self.failUnsupported(expr, "meta property is not supported");
576
+ }
577
+ if (ts2.isIdentifier(expr)) {
578
+ return self.resolveVar(expr.text);
579
+ }
580
+ if (ts2.isConditionalExpression(expr)) {
581
+ const conditionReg = visitExpression(self, expr.condition);
582
+ return lowerConditionalExpression(
583
+ self,
584
+ conditionReg,
585
+ () => visitExpression(self, expr.whenTrue),
586
+ () => visitExpression(self, expr.whenFalse)
587
+ );
588
+ }
589
+ if (ts2.isBinaryExpression(expr)) {
590
+ if (expr.operatorToken.kind === ts2.SyntaxKind.AmpersandAmpersandToken || expr.operatorToken.kind === ts2.SyntaxKind.BarBarToken || expr.operatorToken.kind === ts2.SyntaxKind.QuestionQuestionToken) {
591
+ const leftReg2 = visitExpression(self, expr.left);
592
+ if (expr.operatorToken.kind === ts2.SyntaxKind.AmpersandAmpersandToken) {
593
+ return lowerConditionalExpression(
594
+ self,
595
+ leftReg2,
596
+ () => visitExpression(self, expr.right),
597
+ () => leftReg2
598
+ );
599
+ }
600
+ if (expr.operatorToken.kind === ts2.SyntaxKind.BarBarToken) {
601
+ return lowerConditionalExpression(
602
+ self,
603
+ leftReg2,
604
+ () => leftReg2,
605
+ () => visitExpression(self, expr.right)
606
+ );
607
+ }
608
+ return lowerNullishCoalesce(self, leftReg2, expr.right);
609
+ }
610
+ if (expr.operatorToken.kind === ts2.SyntaxKind.EqualsToken) {
611
+ const valueReg = visitExpression(self, expr.right);
612
+ self.storeValue(expr.left, valueReg);
613
+ return valueReg;
614
+ }
615
+ if (expr.operatorToken.kind === ts2.SyntaxKind.InKeyword && ts2.isPrivateIdentifier(expr.left)) {
616
+ const leftReg2 = self.resolvePrivateIdentifierRegister(expr.left);
617
+ const rightReg2 = visitExpression(self, expr.right);
618
+ const resReg2 = self.fnBuilder.allocRegister();
619
+ self.currentBlock.addInstruction(
620
+ OpCode.PrivateIn,
621
+ [
622
+ { kind: OperandKind.Register, value: rightReg2 },
623
+ { kind: OperandKind.Register, value: leftReg2 }
624
+ ],
625
+ resReg2
626
+ );
627
+ return resReg2;
628
+ }
629
+ const leftReg = visitExpression(self, expr.left);
630
+ const rightReg = visitExpression(self, expr.right);
631
+ const resReg = self.fnBuilder.allocRegister();
632
+ const opMap = {
633
+ [ts2.SyntaxKind.PlusToken]: OpCode.Add,
634
+ [ts2.SyntaxKind.MinusToken]: OpCode.Sub,
635
+ [ts2.SyntaxKind.AsteriskToken]: OpCode.Mul,
636
+ [ts2.SyntaxKind.SlashToken]: OpCode.Div,
637
+ [ts2.SyntaxKind.PercentToken]: OpCode.Mod,
638
+ [ts2.SyntaxKind.LessThanLessThanToken]: OpCode.Shl,
639
+ [ts2.SyntaxKind.GreaterThanGreaterThanToken]: OpCode.Shr,
640
+ [ts2.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: OpCode.UShr,
641
+ [ts2.SyntaxKind.BarToken]: OpCode.BitOr,
642
+ [ts2.SyntaxKind.AmpersandToken]: OpCode.BitAnd,
643
+ [ts2.SyntaxKind.CaretToken]: OpCode.BitXor,
644
+ [ts2.SyntaxKind.LessThanToken]: OpCode.Lt,
645
+ [ts2.SyntaxKind.GreaterThanToken]: OpCode.Gt,
646
+ [ts2.SyntaxKind.LessThanEqualsToken]: OpCode.LtEq,
647
+ [ts2.SyntaxKind.GreaterThanEqualsToken]: OpCode.GtEq,
648
+ [ts2.SyntaxKind.EqualsEqualsToken]: OpCode.Eq,
649
+ [ts2.SyntaxKind.EqualsEqualsEqualsToken]: OpCode.StrictEq,
650
+ [ts2.SyntaxKind.ExclamationEqualsToken]: OpCode.Eq,
651
+ [ts2.SyntaxKind.ExclamationEqualsEqualsToken]: OpCode.StrictEq,
652
+ [ts2.SyntaxKind.InKeyword]: OpCode.In,
653
+ [ts2.SyntaxKind.InstanceOfKeyword]: OpCode.InstanceOf
654
+ };
655
+ const isCompound = expr.operatorToken.kind >= ts2.SyntaxKind.PlusEqualsToken && expr.operatorToken.kind <= ts2.SyntaxKind.CaretEqualsToken;
656
+ if (isCompound) {
657
+ const cmpMap = {
658
+ [ts2.SyntaxKind.PlusEqualsToken]: OpCode.Add,
659
+ [ts2.SyntaxKind.MinusEqualsToken]: OpCode.Sub,
660
+ [ts2.SyntaxKind.AsteriskEqualsToken]: OpCode.Mul,
661
+ [ts2.SyntaxKind.SlashEqualsToken]: OpCode.Div,
662
+ [ts2.SyntaxKind.PercentEqualsToken]: OpCode.Mod,
663
+ [ts2.SyntaxKind.LessThanLessThanEqualsToken]: OpCode.Shl,
664
+ [ts2.SyntaxKind.GreaterThanGreaterThanEqualsToken]: OpCode.Shr,
665
+ [ts2.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: OpCode.UShr,
666
+ [ts2.SyntaxKind.AmpersandEqualsToken]: OpCode.BitAnd,
667
+ [ts2.SyntaxKind.BarEqualsToken]: OpCode.BitOr,
668
+ [ts2.SyntaxKind.CaretEqualsToken]: OpCode.BitXor
669
+ };
670
+ const opc = cmpMap[expr.operatorToken.kind] || OpCode.Add;
671
+ const leftValueReg = self.readValue(expr.left);
672
+ self.currentBlock.addInstruction(
673
+ opc,
674
+ [
675
+ { kind: OperandKind.Register, value: leftValueReg },
676
+ { kind: OperandKind.Register, value: rightReg }
677
+ ],
678
+ resReg
679
+ );
680
+ self.storeValue(expr.left, resReg);
681
+ return resReg;
682
+ }
683
+ if (opMap[expr.operatorToken.kind]) {
684
+ self.currentBlock.addInstruction(
685
+ opMap[expr.operatorToken.kind],
686
+ [
687
+ { kind: OperandKind.Register, value: leftReg },
688
+ { kind: OperandKind.Register, value: rightReg }
689
+ ],
690
+ resReg
691
+ );
692
+ if (expr.operatorToken.kind === ts2.SyntaxKind.ExclamationEqualsToken || expr.operatorToken.kind === ts2.SyntaxKind.ExclamationEqualsEqualsToken) {
693
+ const notReg = self.fnBuilder.allocRegister();
694
+ self.currentBlock.addInstruction(OpCode.Not, [{ kind: OperandKind.Register, value: resReg }], notReg);
695
+ return notReg;
696
+ }
697
+ return resReg;
698
+ }
699
+ }
700
+ if (ts2.isPropertyAccessExpression(expr)) {
701
+ if (expr.expression.kind === ts2.SyntaxKind.SuperKeyword) {
702
+ const propReg2 = self.emitConstant(ConstantKind.String, expr.name.text);
703
+ const resReg2 = self.fnBuilder.allocRegister();
704
+ self.currentBlock.addInstruction(OpCode.SuperPropGet, [{ kind: OperandKind.Register, value: propReg2 }], resReg2);
705
+ return resReg2;
706
+ }
707
+ const objReg = visitExpression(self, expr.expression);
708
+ const resReg = self.fnBuilder.allocRegister();
709
+ if (ts2.isPrivateIdentifier(expr.name)) {
710
+ const privateKeyReg = self.resolvePrivateIdentifierRegister(expr.name);
711
+ self.currentBlock.addInstruction(
712
+ OpCode.PrivateGet,
713
+ [
714
+ { kind: OperandKind.Register, value: objReg },
715
+ { kind: OperandKind.Register, value: privateKeyReg }
716
+ ],
717
+ resReg
718
+ );
719
+ return resReg;
720
+ }
721
+ const propReg = self.emitConstant(ConstantKind.String, expr.name.text);
722
+ self.currentBlock.addInstruction(
723
+ OpCode.PropGet,
724
+ [
725
+ { kind: OperandKind.Register, value: objReg },
726
+ { kind: OperandKind.Register, value: propReg }
727
+ ],
728
+ resReg
729
+ );
730
+ return resReg;
731
+ }
732
+ if (ts2.isElementAccessExpression(expr)) {
733
+ if (!expr.argumentExpression) {
734
+ self.failUnsupported(expr, "Element access requires an index expression");
735
+ }
736
+ const objReg = visitExpression(self, expr.expression);
737
+ const indexReg = visitExpression(self, expr.argumentExpression);
738
+ const resReg = self.fnBuilder.allocRegister();
739
+ self.currentBlock.addInstruction(
740
+ OpCode.ComputedGet,
741
+ [
742
+ { kind: OperandKind.Register, value: objReg },
743
+ { kind: OperandKind.Register, value: indexReg }
744
+ ],
745
+ resReg
746
+ );
747
+ return resReg;
748
+ }
749
+ if (ts2.isArrayLiteralExpression(expr)) {
750
+ const arrayReg = self.fnBuilder.allocRegister();
751
+ self.currentBlock.addInstruction(OpCode.ArrayNew, [], arrayReg);
752
+ const indexLocal = self.createTempLocal("array_index");
753
+ const zeroReg = self.emitConstant(ConstantKind.Number, 0);
754
+ const oneReg = self.emitConstant(ConstantKind.Number, 1);
755
+ const lengthKeyReg = self.emitConstant(ConstantKind.String, "length");
756
+ self.storeToLocal(indexLocal, zeroReg);
757
+ expr.elements.forEach((element) => {
758
+ if (ts2.isOmittedExpression(element)) {
759
+ const currentIndexReg2 = self.loadFromLocal(indexLocal);
760
+ const nextIndexReg2 = self.fnBuilder.allocRegister();
761
+ self.currentBlock.addInstruction(
762
+ OpCode.Add,
763
+ [
764
+ { kind: OperandKind.Register, value: currentIndexReg2 },
765
+ { kind: OperandKind.Register, value: oneReg }
766
+ ],
767
+ nextIndexReg2
768
+ );
769
+ self.storeToLocal(indexLocal, nextIndexReg2);
770
+ return;
771
+ }
772
+ if (ts2.isSpreadElement(element)) {
773
+ const currentIndexReg2 = self.loadFromLocal(indexLocal);
774
+ const spreadReg = visitExpression(self, element.expression);
775
+ self.emitSpreadIntoArray(arrayReg, spreadReg, currentIndexReg2, indexLocal);
776
+ return;
777
+ }
778
+ const currentIndexReg = self.loadFromLocal(indexLocal);
779
+ const valueReg = visitExpression(self, element);
780
+ self.currentBlock.addInstruction(OpCode.ComputedSet, [
781
+ { kind: OperandKind.Register, value: arrayReg },
782
+ { kind: OperandKind.Register, value: currentIndexReg },
783
+ { kind: OperandKind.Register, value: valueReg }
784
+ ]);
785
+ const nextIndexReg = self.fnBuilder.allocRegister();
786
+ self.currentBlock.addInstruction(
787
+ OpCode.Add,
788
+ [
789
+ { kind: OperandKind.Register, value: currentIndexReg },
790
+ { kind: OperandKind.Register, value: oneReg }
791
+ ],
792
+ nextIndexReg
793
+ );
794
+ self.storeToLocal(indexLocal, nextIndexReg);
795
+ });
796
+ const finalLengthReg = self.loadFromLocal(indexLocal);
797
+ self.currentBlock.addInstruction(OpCode.PropSet, [
798
+ { kind: OperandKind.Register, value: arrayReg },
799
+ { kind: OperandKind.Register, value: lengthKeyReg },
800
+ { kind: OperandKind.Register, value: finalLengthReg }
801
+ ]);
802
+ return arrayReg;
803
+ }
804
+ if (ts2.isObjectLiteralExpression(expr)) {
805
+ const objectReg = self.fnBuilder.allocRegister();
806
+ self.currentBlock.addInstruction(OpCode.ObjectNew, [], objectReg);
807
+ for (const property of expr.properties) {
808
+ if (ts2.isPropertyAssignment(property)) {
809
+ const valueReg = visitExpression(self, property.initializer);
810
+ if (ts2.isComputedPropertyName(property.name)) {
811
+ const nameReg = visitExpression(self, property.name.expression);
812
+ self.emitObjectPropertyAssignment(objectReg, nameReg, valueReg, true);
813
+ } else {
814
+ const propName = self.getPropertyNameText(property.name);
815
+ const nameReg = self.emitConstant(ConstantKind.String, propName);
816
+ self.emitObjectPropertyAssignment(objectReg, nameReg, valueReg);
817
+ }
818
+ continue;
819
+ }
820
+ if (ts2.isShorthandPropertyAssignment(property)) {
821
+ const nameReg = self.emitConstant(ConstantKind.String, property.name.text);
822
+ const valueReg = self.resolveVar(property.name.text);
823
+ self.emitObjectPropertyAssignment(objectReg, nameReg, valueReg);
824
+ continue;
825
+ }
826
+ if (ts2.isMethodDeclaration(property)) {
827
+ const valueReg = lowerNestedFunctionNode(self, property);
828
+ if (ts2.isComputedPropertyName(property.name)) {
829
+ const nameReg = visitExpression(self, property.name.expression);
830
+ self.emitObjectPropertyAssignment(objectReg, nameReg, valueReg, true);
831
+ } else {
832
+ const nameReg = self.emitConstant(ConstantKind.String, self.getPropertyNameText(property.name));
833
+ self.emitObjectPropertyAssignment(objectReg, nameReg, valueReg);
834
+ }
835
+ continue;
836
+ }
837
+ if (ts2.isSpreadAssignment(property)) {
838
+ const valueReg = visitExpression(self, property.expression);
839
+ self.emitSpreadInto(objectReg, valueReg);
840
+ continue;
841
+ }
842
+ if (ts2.isGetAccessor(property) || ts2.isSetAccessor(property)) {
843
+ const valueReg = lowerNestedFunctionNode(self, property);
844
+ const nameReg = ts2.isComputedPropertyName(property.name) ? visitExpression(self, property.name.expression) : self.emitConstant(ConstantKind.String, self.getPropertyNameText(property.name));
845
+ const objectGlobalReg = self.fnBuilder.allocRegister();
846
+ self.currentBlock.addInstruction(
847
+ OpCode.LoadGlobal,
848
+ [{ kind: OperandKind.Register, value: self.emitConstant(ConstantKind.String, "Object") }],
849
+ objectGlobalReg
850
+ );
851
+ const descriptorReg = self.fnBuilder.allocRegister();
852
+ self.currentBlock.addInstruction(OpCode.ObjectNew, [], descriptorReg);
853
+ const getPropReg = self.emitConstant(ConstantKind.String, ts2.isGetAccessor(property) ? "get" : "set");
854
+ self.emitObjectPropertyAssignment(descriptorReg, getPropReg, valueReg);
855
+ const trueReg = self.emitConstant(ConstantKind.Boolean, true);
856
+ self.emitObjectPropertyAssignment(descriptorReg, self.emitConstant(ConstantKind.String, "configurable"), trueReg);
857
+ self.emitObjectPropertyAssignment(descriptorReg, self.emitConstant(ConstantKind.String, "enumerable"), trueReg);
858
+ self.currentBlock.addInstruction(
859
+ OpCode.CallMethod,
860
+ [
861
+ { kind: OperandKind.Register, value: objectGlobalReg },
862
+ { kind: OperandKind.Register, value: self.emitConstant(ConstantKind.String, "defineProperty") },
863
+ { kind: OperandKind.Register, value: objectReg },
864
+ { kind: OperandKind.Register, value: nameReg },
865
+ { kind: OperandKind.Register, value: descriptorReg }
866
+ ],
867
+ self.fnBuilder.allocRegister()
868
+ );
869
+ continue;
870
+ }
871
+ self.failUnsupported(property, "Unsupported object literal property kind");
872
+ }
873
+ return objectReg;
874
+ }
875
+ if (ts2.isCallExpression(expr)) {
876
+ const resReg = self.fnBuilder.allocRegister();
877
+ const hasSpread = expr.arguments.some((arg) => ts2.isSpreadElement(arg));
878
+ if (hasSpread) {
879
+ const argArrayReg = self.materializeArgumentArray(expr.arguments);
880
+ if (expr.expression.kind === ts2.SyntaxKind.SuperKeyword) {
881
+ self.currentBlock.addInstruction(OpCode.SuperCallWithArray, [{ kind: OperandKind.Register, value: argArrayReg }], resReg);
882
+ if (self.instanceFieldsToInitialize) {
883
+ self.emitInstanceFieldInitializers(self.instanceFieldsToInitialize);
884
+ }
885
+ return resReg;
886
+ }
887
+ if (ts2.isPropertyAccessExpression(expr.expression)) {
888
+ if (expr.expression.expression.kind === ts2.SyntaxKind.SuperKeyword) {
889
+ const propReg2 = self.emitConstant(ConstantKind.String, expr.expression.name.text);
890
+ const fnReg = self.fnBuilder.allocRegister();
891
+ self.currentBlock.addInstruction(OpCode.SuperPropGet, [{ kind: OperandKind.Register, value: propReg2 }], fnReg);
892
+ const applyReg = self.emitConstant(ConstantKind.String, "apply");
893
+ const thisReg = self.fnBuilder.allocRegister();
894
+ self.currentBlock.addInstruction(OpCode.LoadThis, [], thisReg);
895
+ self.currentBlock.addInstruction(
896
+ OpCode.CallMethod,
897
+ [
898
+ { kind: OperandKind.Register, value: fnReg },
899
+ { kind: OperandKind.Register, value: applyReg },
900
+ { kind: OperandKind.Register, value: thisReg },
901
+ { kind: OperandKind.Register, value: argArrayReg }
902
+ ],
903
+ resReg
904
+ );
905
+ return resReg;
906
+ }
907
+ const objReg = visitExpression(self, expr.expression.expression);
908
+ const propReg = self.emitConstant(ConstantKind.String, expr.expression.name.text);
909
+ self.currentBlock.addInstruction(
910
+ OpCode.CallMethodWithArray,
911
+ [
912
+ { kind: OperandKind.Register, value: objReg },
913
+ { kind: OperandKind.Register, value: propReg },
914
+ { kind: OperandKind.Register, value: argArrayReg }
915
+ ],
916
+ resReg
917
+ );
918
+ return resReg;
919
+ }
920
+ const calleeReg = visitExpression(self, expr.expression);
921
+ self.currentBlock.addInstruction(
922
+ OpCode.CallWithArray,
923
+ [
924
+ { kind: OperandKind.Register, value: calleeReg },
925
+ { kind: OperandKind.Register, value: argArrayReg }
926
+ ],
927
+ resReg
928
+ );
929
+ return resReg;
930
+ }
931
+ const args = expr.arguments.map((a) => visitExpression(self, a));
932
+ if (expr.expression.kind === ts2.SyntaxKind.SuperKeyword) {
933
+ const ops = args.map((a) => ({ kind: OperandKind.Register, value: a }));
934
+ self.currentBlock.addInstruction(OpCode.SuperCall, ops, resReg);
935
+ if (self.instanceFieldsToInitialize) {
936
+ self.emitInstanceFieldInitializers(self.instanceFieldsToInitialize);
937
+ }
938
+ return resReg;
939
+ }
940
+ if (ts2.isPropertyAccessExpression(expr.expression)) {
941
+ if (expr.expression.expression.kind === ts2.SyntaxKind.SuperKeyword) {
942
+ const propReg2 = self.emitConstant(ConstantKind.String, expr.expression.name.text);
943
+ const fnReg = self.fnBuilder.allocRegister();
944
+ self.currentBlock.addInstruction(OpCode.SuperPropGet, [{ kind: OperandKind.Register, value: propReg2 }], fnReg);
945
+ const callReg = self.emitConstant(ConstantKind.String, "call");
946
+ const thisReg = self.fnBuilder.allocRegister();
947
+ self.currentBlock.addInstruction(OpCode.LoadThis, [], thisReg);
948
+ const ops2 = [
949
+ { kind: OperandKind.Register, value: fnReg },
950
+ { kind: OperandKind.Register, value: callReg },
951
+ { kind: OperandKind.Register, value: thisReg },
952
+ ...args.map((a) => ({ kind: OperandKind.Register, value: a }))
953
+ ];
954
+ self.currentBlock.addInstruction(OpCode.CallMethod, ops2, resReg);
955
+ return resReg;
956
+ }
957
+ const objReg = visitExpression(self, expr.expression.expression);
958
+ const propReg = self.emitConstant(ConstantKind.String, expr.expression.name.text);
959
+ const ops = [
960
+ { kind: OperandKind.Register, value: objReg },
961
+ { kind: OperandKind.Register, value: propReg },
962
+ ...args.map((a) => ({ kind: OperandKind.Register, value: a }))
963
+ ];
964
+ self.currentBlock.addInstruction(OpCode.CallMethod, ops, resReg);
965
+ } else {
966
+ const calleeReg = visitExpression(self, expr.expression);
967
+ const ops = [
968
+ { kind: OperandKind.Register, value: calleeReg },
969
+ ...args.map((a) => ({ kind: OperandKind.Register, value: a }))
970
+ ];
971
+ self.currentBlock.addInstruction(OpCode.Call, ops, resReg);
972
+ }
973
+ return resReg;
974
+ }
975
+ if (ts2.isNewExpression(expr)) {
976
+ const calleeReg = visitExpression(self, expr.expression);
977
+ const resReg = self.fnBuilder.allocRegister();
978
+ const argsList = expr.arguments ? [...expr.arguments] : [];
979
+ if (argsList.some((arg) => ts2.isSpreadElement(arg))) {
980
+ const argArrayReg = self.materializeArgumentArray(argsList);
981
+ self.currentBlock.addInstruction(
982
+ OpCode.NewWithArray,
983
+ [
984
+ { kind: OperandKind.Register, value: calleeReg },
985
+ { kind: OperandKind.Register, value: argArrayReg }
986
+ ],
987
+ resReg
988
+ );
989
+ return resReg;
990
+ }
991
+ const args = argsList.map((arg) => visitExpression(self, arg));
992
+ const ops = [
993
+ { kind: OperandKind.Register, value: calleeReg },
994
+ ...args.map((arg) => ({ kind: OperandKind.Register, value: arg }))
995
+ ];
996
+ self.currentBlock.addInstruction(OpCode.New, ops, resReg);
997
+ return resReg;
998
+ }
999
+ if (ts2.isArrowFunction(expr) || ts2.isFunctionExpression(expr)) {
1000
+ return lowerNestedFunctionNode(self, expr);
1001
+ }
1002
+ if (ts2.isClassExpression(expr)) {
1003
+ return self.lowerClassLike(expr, expr.name?.text);
1004
+ }
1005
+ if (ts2.isPostfixUnaryExpression(expr)) {
1006
+ if (expr.operator === ts2.SyntaxKind.PlusPlusToken) {
1007
+ if (ts2.isIdentifier(expr.operand)) {
1008
+ const vReg = self.resolveVar(expr.operand.text);
1009
+ const oneReg = self.emitConstant(ConstantKind.Number, 1);
1010
+ const resReg = self.fnBuilder.allocRegister();
1011
+ self.currentBlock.addInstruction(
1012
+ OpCode.Add,
1013
+ [
1014
+ { kind: OperandKind.Register, value: vReg },
1015
+ { kind: OperandKind.Register, value: oneReg }
1016
+ ],
1017
+ resReg
1018
+ );
1019
+ self.storeValue(expr.operand, resReg);
1020
+ return vReg;
1021
+ }
1022
+ }
1023
+ }
1024
+ if (ts2.isPrefixUnaryExpression(expr)) {
1025
+ const operandReg = visitExpression(self, expr.operand);
1026
+ const resReg = self.fnBuilder.allocRegister();
1027
+ if (expr.operator === ts2.SyntaxKind.ExclamationToken) {
1028
+ self.currentBlock.addInstruction(OpCode.Not, [{ kind: OperandKind.Register, value: operandReg }], resReg);
1029
+ } else if (expr.operator === ts2.SyntaxKind.MinusToken) {
1030
+ self.currentBlock.addInstruction(OpCode.Neg, [{ kind: OperandKind.Register, value: operandReg }], resReg);
1031
+ } else if (expr.operator === ts2.SyntaxKind.TildeToken) {
1032
+ const notReg = self.fnBuilder.allocRegister();
1033
+ self.currentBlock.addInstruction(OpCode.Not, [{ kind: OperandKind.Register, value: operandReg }], notReg);
1034
+ self.currentBlock.addInstruction(
1035
+ OpCode.BitXor,
1036
+ [
1037
+ { kind: OperandKind.Register, value: operandReg },
1038
+ { kind: OperandKind.Register, value: notReg }
1039
+ ],
1040
+ resReg
1041
+ );
1042
+ } else {
1043
+ self.failUnsupported(expr, "Unsupported prefix unary operator");
1044
+ }
1045
+ return resReg;
1046
+ }
1047
+ if (ts2.isTypeOfExpression(expr)) {
1048
+ const operandReg = visitExpression(self, expr.expression);
1049
+ const resReg = self.fnBuilder.allocRegister();
1050
+ self.currentBlock.addInstruction(OpCode.TypeOf, [{ kind: OperandKind.Register, value: operandReg }], resReg);
1051
+ return resReg;
1052
+ }
1053
+ if (ts2.isDeleteExpression(expr)) {
1054
+ if (ts2.isPropertyAccessExpression(expr.expression)) {
1055
+ const objReg = visitExpression(self, expr.expression.expression);
1056
+ const propReg = self.emitConstant(ConstantKind.String, expr.expression.name.text);
1057
+ const resReg = self.fnBuilder.allocRegister();
1058
+ self.currentBlock.addInstruction(OpCode.Delete, [
1059
+ { kind: OperandKind.Register, value: objReg },
1060
+ { kind: OperandKind.Register, value: propReg }
1061
+ ]);
1062
+ self.currentBlock.addInstruction(
1063
+ OpCode.LoadConst,
1064
+ [{ kind: OperandKind.ConstantIndex, value: self.modBuilder.addConstant(ConstantKind.Boolean, true) }],
1065
+ resReg
1066
+ );
1067
+ return resReg;
1068
+ }
1069
+ if (ts2.isElementAccessExpression(expr.expression)) {
1070
+ if (!expr.expression.argumentExpression) {
1071
+ self.failUnsupported(expr.expression, "Element access requires an index expression");
1072
+ }
1073
+ const objReg = visitExpression(self, expr.expression.expression);
1074
+ const propReg = visitExpression(self, expr.expression.argumentExpression);
1075
+ const resReg = self.fnBuilder.allocRegister();
1076
+ self.currentBlock.addInstruction(OpCode.Delete, [
1077
+ { kind: OperandKind.Register, value: objReg },
1078
+ { kind: OperandKind.Register, value: propReg }
1079
+ ]);
1080
+ self.currentBlock.addInstruction(
1081
+ OpCode.LoadConst,
1082
+ [{ kind: OperandKind.ConstantIndex, value: self.modBuilder.addConstant(ConstantKind.Boolean, true) }],
1083
+ resReg
1084
+ );
1085
+ return resReg;
1086
+ }
1087
+ self.failUnsupported(expr, "Unsupported delete target");
1088
+ }
1089
+ self.failUnsupported(expr);
1090
+ }
1091
+
1092
+ // src/builder.ts
1093
+ var ASTLowering = class _ASTLowering {
1094
+ constructor(modBuilder, node, options) {
1095
+ this.modBuilder = modBuilder;
1096
+ this.node = node;
1097
+ this.functionId = modBuilder.getNextFunctionId();
1098
+ this.fnBuilder = new IRFunctionBuilder(this.functionId, options.name, IRType.Any);
1099
+ this.sourceFile = node.getSourceFile();
1100
+ this.capturedLocals = options.analysis.capturedByDescendants;
1101
+ this.privateIdentifierBindings = options.privateIdentifierBindings ?? /* @__PURE__ */ new Map();
1102
+ this.instanceFieldsToInitialize = options.instanceFieldsToInitialize;
1103
+ if (options.isExported) {
1104
+ this.fnBuilder.addAttribute(FunctionAttribute.Exported);
1105
+ }
1106
+ if (options.isNested) {
1107
+ this.fnBuilder.addAttribute(FunctionAttribute.Nested);
1108
+ }
1109
+ for (const attribute of options.attributes ?? []) {
1110
+ this.fnBuilder.addAttribute(attribute);
1111
+ }
1112
+ this.isAsyncFunction = !!this.node.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword) || "asteriskToken" in this.node && !!this.node.asteriskToken && this.node.name?.text === "async" || (options.attributes ?? []).includes(FunctionAttribute.Async);
1113
+ this.isGenerator = "asteriskToken" in this.node && !!this.node.asteriskToken;
1114
+ this.currentBlock = this.fnBuilder.createBlock("entry");
1115
+ options.analysis.capturedFromOuter.forEach((name, index) => {
1116
+ this.outerCaptureBindings.set(name, index);
1117
+ this.fnBuilder.addCapturedVariable(name);
1118
+ });
1119
+ const isThisParameter = (param) => ts3.isIdentifier(param.name) && param.name.text === "this";
1120
+ let runtimeParamIndex = 0;
1121
+ node.parameters.forEach((param, index) => {
1122
+ if (isThisParameter(param)) {
1123
+ return;
1124
+ }
1125
+ const isPlainIdentifier = ts3.isIdentifier(param.name) && !param.dotDotDotToken && !param.initializer;
1126
+ const paramName = isPlainIdentifier ? param.name.text : `$param_${runtimeParamIndex}`;
1127
+ const reg = this.fnBuilder.addParam(paramName, IRType.Any, !!param.dotDotDotToken);
1128
+ if (isPlainIdentifier && ts3.isIdentifier(param.name)) {
1129
+ this.scope.set(param.name.text, { register: reg, boxed: this.capturedLocals.has(param.name.text) });
1130
+ } else {
1131
+ this.pendingParameterBindings.push({ param, register: reg });
1132
+ }
1133
+ runtimeParamIndex++;
1134
+ });
1135
+ this.boxCapturedParameters();
1136
+ this.lowerPendingParameterBindings();
1137
+ this.initializeLexicalSemanticCaptures(options.analysis);
1138
+ options.prologueEmitter?.(this);
1139
+ if (node.body) {
1140
+ if (ts3.isBlock(node.body)) {
1141
+ this.visitStatement(node.body);
1142
+ } else {
1143
+ const valueReg = this.visitExpression(node.body);
1144
+ this.currentBlock.setTerminator({ kind: "return", targets: [], returnValue: valueReg });
1145
+ }
1146
+ }
1147
+ if (this.shouldEmitFallthroughReturn()) {
1148
+ const undefConst = this.modBuilder.addConstant(ConstantKind2.Undefined, null);
1149
+ const rRet = this.fnBuilder.allocRegister();
1150
+ this.currentBlock.addInstruction(OpCode2.LoadConst, [{ kind: OperandKind2.ConstantIndex, value: undefConst }], rRet);
1151
+ this.currentBlock.setTerminator({ kind: "return", targets: [], returnValue: rRet });
1152
+ }
1153
+ if (!this.isSyntheticDeadBlock(this.currentBlock)) {
1154
+ this.fnBuilder.addBlock(this.currentBlock.build());
1155
+ }
1156
+ this.modBuilder.addFunction(this.fnBuilder.build(options.isVirtualized, options.isExported));
1157
+ }
1158
+ modBuilder;
1159
+ node;
1160
+ fnBuilder;
1161
+ currentBlock;
1162
+ scope = new ScopeMap();
1163
+ functionId;
1164
+ nestedFunctionCount = 0;
1165
+ tempLocalCount = 0;
1166
+ sourceFile;
1167
+ capturedLocals;
1168
+ outerCaptureBindings = /* @__PURE__ */ new Map();
1169
+ breakTargets = [];
1170
+ continueTargets = [];
1171
+ finallyContexts = [];
1172
+ throwPassthroughFinallyDepth = 0;
1173
+ pendingParameterBindings = [];
1174
+ isAsyncFunction;
1175
+ isGenerator;
1176
+ privateIdentifierBindings;
1177
+ instanceFieldsToInitialize;
1178
+ failUnsupported(node, detail) {
1179
+ const kind = ts3.SyntaxKind[node.kind];
1180
+ const snippet = node.getText(this.sourceFile).replace(/\s+/g, " ").slice(0, 80);
1181
+ const { line, character } = this.sourceFile.getLineAndCharacterOfPosition(node.getStart(this.sourceFile));
1182
+ const message = detail ? `${kind} (${detail})` : kind;
1183
+ throw new Error(
1184
+ `Unsupported AST in IR builder: ${message} at ${this.sourceFile.fileName}:${line + 1}:${character + 1} near "${snippet}"`
1185
+ );
1186
+ }
1187
+ isSyntheticDeadBlock(block) {
1188
+ const label = block.label;
1189
+ const syntheticLabel = label === "unreachable" || label === "after_break" || label === "after_continue";
1190
+ return syntheticLabel && block.getInstructionCount() === 0 && (block.getTerminatorKind() === void 0 || block.getTerminatorKind() === "unreachable") && this.fnBuilder.getBlockCount() > 0;
1191
+ }
1192
+ shouldEmitFallthroughReturn() {
1193
+ if (this.isSyntheticDeadBlock(this.currentBlock)) {
1194
+ return false;
1195
+ }
1196
+ const termKind = this.currentBlock.getTerminatorKind();
1197
+ return termKind === void 0 || termKind === "unreachable";
1198
+ }
1199
+ enterBreakTarget(target) {
1200
+ this.breakTargets.push(target);
1201
+ }
1202
+ leaveBreakTarget() {
1203
+ this.breakTargets.pop();
1204
+ }
1205
+ enterContinueTarget(target) {
1206
+ this.continueTargets.push(target);
1207
+ }
1208
+ leaveContinueTarget() {
1209
+ this.continueTargets.pop();
1210
+ }
1211
+ emitJumpAndAdvance(targetBlockId, nextLabel) {
1212
+ this.currentBlock.setTerminator({ kind: "jump", targets: [targetBlockId] });
1213
+ this.fnBuilder.addBlock(this.currentBlock.build());
1214
+ this.currentBlock = this.fnBuilder.createBlock(nextLabel);
1215
+ }
1216
+ emitConstant(kind, value) {
1217
+ const idx = this.modBuilder.addConstant(kind, value);
1218
+ const reg = this.fnBuilder.allocRegister();
1219
+ this.currentBlock.addInstruction(OpCode2.LoadConst, [{ kind: OperandKind2.ConstantIndex, value: idx }], reg);
1220
+ return reg;
1221
+ }
1222
+ storeToLocal(localReg, valueReg) {
1223
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
1224
+ { kind: OperandKind2.Register, value: localReg },
1225
+ { kind: OperandKind2.Register, value: valueReg }
1226
+ ]);
1227
+ }
1228
+ boxCapturedParameters() {
1229
+ for (const [name, binding] of this.scope.entries()) {
1230
+ if (binding.boxed) {
1231
+ this.currentBlock.addInstruction(OpCode2.CellNew, [{ kind: OperandKind2.Register, value: binding.register }], binding.register);
1232
+ this.fnBuilder.addCapturedVariable(name);
1233
+ }
1234
+ }
1235
+ }
1236
+ lowerPendingParameterBindings() {
1237
+ this.pendingParameterBindings.forEach(({ param, register }, index) => {
1238
+ let valueReg = param.dotDotDotToken ? this.emitRestArgs(index) : this.loadFromLocal(register);
1239
+ valueReg = this.applyDefaultValue(valueReg, param.initializer);
1240
+ if (ts3.isIdentifier(param.name)) {
1241
+ this.initializeDeclaredIdentifier(param.name.text, valueReg);
1242
+ return;
1243
+ }
1244
+ this.bindPattern(param.name, valueReg, "declare");
1245
+ });
1246
+ }
1247
+ loadOuterCaptureCell(name) {
1248
+ const envIndex = this.outerCaptureBindings.get(name);
1249
+ if (envIndex === void 0) {
1250
+ this.failUnsupported(this.findIdentifierNode(name) ?? this.node, `Unknown closure binding "${name}"`);
1251
+ }
1252
+ const cellReg = this.fnBuilder.allocRegister();
1253
+ this.currentBlock.addInstruction(OpCode2.EnvGet, [{ kind: OperandKind2.Immediate, value: envIndex }], cellReg);
1254
+ return cellReg;
1255
+ }
1256
+ resolveVar(name) {
1257
+ if (this.scope.has(name)) {
1258
+ const binding = this.scope.get(name);
1259
+ const destReg2 = this.fnBuilder.allocRegister();
1260
+ if (binding.boxed) {
1261
+ this.currentBlock.addInstruction(OpCode2.CellGet, [{ kind: OperandKind2.Register, value: binding.register }], destReg2);
1262
+ } else {
1263
+ this.currentBlock.addInstruction(OpCode2.LoadLocal, [{ kind: OperandKind2.Register, value: binding.register }], destReg2);
1264
+ }
1265
+ return destReg2;
1266
+ }
1267
+ if (this.outerCaptureBindings.has(name)) {
1268
+ const cellReg = this.loadOuterCaptureCell(name);
1269
+ const destReg2 = this.fnBuilder.allocRegister();
1270
+ this.currentBlock.addInstruction(OpCode2.CellGet, [{ kind: OperandKind2.Register, value: cellReg }], destReg2);
1271
+ return destReg2;
1272
+ }
1273
+ const strReg = this.emitConstant(ConstantKind2.String, name);
1274
+ const destReg = this.fnBuilder.allocRegister();
1275
+ this.currentBlock.addInstruction(OpCode2.LoadGlobal, [{ kind: OperandKind2.Register, value: strReg }], destReg);
1276
+ return destReg;
1277
+ }
1278
+ findIdentifierNode(name) {
1279
+ let match;
1280
+ const visit = (node) => {
1281
+ if (match) {
1282
+ return;
1283
+ }
1284
+ if (ts3.isIdentifier(node) && node.text === name) {
1285
+ match = node;
1286
+ return;
1287
+ }
1288
+ ts3.forEachChild(node, visit);
1289
+ };
1290
+ if (this.node.body) {
1291
+ ts3.forEachChild(this.node.body, visit);
1292
+ }
1293
+ return match;
1294
+ }
1295
+ createNestedFunctionName() {
1296
+ const suffix = this.nestedFunctionCount++;
1297
+ return `${this.fnBuilder.name}$closure$${suffix}`;
1298
+ }
1299
+ getAvailableOuterCaptureNames() {
1300
+ const availableOuterNames = new Set(this.outerCaptureBindings.keys());
1301
+ for (const name of this.scope.keys()) {
1302
+ availableOuterNames.add(name);
1303
+ }
1304
+ if (this.canProvideLexicalThis()) {
1305
+ availableOuterNames.add(LEXICAL_THIS_CAPTURE);
1306
+ }
1307
+ if (this.canProvideLexicalNewTarget()) {
1308
+ availableOuterNames.add(LEXICAL_NEW_TARGET_CAPTURE);
1309
+ }
1310
+ return availableOuterNames;
1311
+ }
1312
+ canProvideLexicalThis() {
1313
+ if (!ts3.isArrowFunction(this.node)) {
1314
+ return true;
1315
+ }
1316
+ return this.scope.has(LEXICAL_THIS_CAPTURE) || this.outerCaptureBindings.has(LEXICAL_THIS_CAPTURE);
1317
+ }
1318
+ canProvideLexicalNewTarget() {
1319
+ if (!ts3.isArrowFunction(this.node)) {
1320
+ return true;
1321
+ }
1322
+ return this.scope.has(LEXICAL_NEW_TARGET_CAPTURE) || this.outerCaptureBindings.has(LEXICAL_NEW_TARGET_CAPTURE);
1323
+ }
1324
+ initializeLexicalSemanticCaptures(analysis) {
1325
+ if (!ts3.isArrowFunction(this.node)) {
1326
+ if (analysis.capturedByDescendants.has(LEXICAL_THIS_CAPTURE)) {
1327
+ this.initializeLexicalCapture(LEXICAL_THIS_CAPTURE);
1328
+ }
1329
+ if (analysis.capturedByDescendants.has(LEXICAL_NEW_TARGET_CAPTURE)) {
1330
+ this.initializeLexicalCapture(LEXICAL_NEW_TARGET_CAPTURE);
1331
+ }
1332
+ return;
1333
+ }
1334
+ if (analysis.capturedFromOuter.includes(LEXICAL_THIS_CAPTURE)) {
1335
+ this.initializeLexicalCapture(LEXICAL_THIS_CAPTURE);
1336
+ }
1337
+ if (analysis.capturedFromOuter.includes(LEXICAL_NEW_TARGET_CAPTURE)) {
1338
+ this.initializeLexicalCapture(LEXICAL_NEW_TARGET_CAPTURE);
1339
+ }
1340
+ }
1341
+ initializeLexicalCapture(name) {
1342
+ if (this.scope.has(name)) {
1343
+ return;
1344
+ }
1345
+ const localReg = this.fnBuilder.addLocal(name, IRType.Any, true);
1346
+ this.scope.set(name, { register: localReg, boxed: true });
1347
+ let sourceValueReg;
1348
+ if (this.outerCaptureBindings.has(name)) {
1349
+ const outerCellReg = this.loadOuterCaptureCell(name);
1350
+ sourceValueReg = this.fnBuilder.allocRegister();
1351
+ this.currentBlock.addInstruction(OpCode2.CellGet, [{ kind: OperandKind2.Register, value: outerCellReg }], sourceValueReg);
1352
+ } else if (name === LEXICAL_THIS_CAPTURE) {
1353
+ sourceValueReg = this.fnBuilder.allocRegister();
1354
+ this.currentBlock.addInstruction(OpCode2.LoadThis, [], sourceValueReg);
1355
+ } else {
1356
+ sourceValueReg = this.fnBuilder.allocRegister();
1357
+ this.currentBlock.addInstruction(OpCode2.LoadNewTarget, [], sourceValueReg);
1358
+ }
1359
+ this.currentBlock.addInstruction(OpCode2.CellNew, [{ kind: OperandKind2.Register, value: sourceValueReg }], localReg);
1360
+ this.fnBuilder.addCapturedVariable(name);
1361
+ }
1362
+ resolveLexicalCapture(name, node, detail) {
1363
+ if (!this.scope.has(name) && !this.outerCaptureBindings.has(name)) {
1364
+ this.failUnsupported(node, detail);
1365
+ }
1366
+ return this.resolveVar(name);
1367
+ }
1368
+ collectReferencedOuterNames(nodes, availableOuterNames) {
1369
+ const referenced = /* @__PURE__ */ new Set();
1370
+ const visit = (node) => {
1371
+ if (ts3.isIdentifier(node) && availableOuterNames.has(node.text)) {
1372
+ referenced.add(node.text);
1373
+ }
1374
+ ts3.forEachChild(node, visit);
1375
+ };
1376
+ nodes.forEach((node) => visit(node));
1377
+ return [...referenced];
1378
+ }
1379
+ getFunctionAttributes(node) {
1380
+ const attributes = [];
1381
+ if (node.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword)) {
1382
+ attributes.push(FunctionAttribute.Async);
1383
+ }
1384
+ if (ts3.isArrowFunction(node)) {
1385
+ attributes.push(FunctionAttribute.Arrow);
1386
+ }
1387
+ if (ts3.isMethodDeclaration(node)) {
1388
+ attributes.push(FunctionAttribute.Method);
1389
+ }
1390
+ if (ts3.isGetAccessorDeclaration(node)) {
1391
+ attributes.push(FunctionAttribute.Getter);
1392
+ }
1393
+ if (ts3.isSetAccessorDeclaration(node)) {
1394
+ attributes.push(FunctionAttribute.Setter);
1395
+ }
1396
+ if (ts3.isConstructorDeclaration(node)) {
1397
+ attributes.push(FunctionAttribute.Constructor);
1398
+ }
1399
+ if ("asteriskToken" in node && !!node.asteriskToken) {
1400
+ attributes.push(FunctionAttribute.Generator);
1401
+ }
1402
+ if (node.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.StaticKeyword)) {
1403
+ attributes.push(FunctionAttribute.Static);
1404
+ }
1405
+ return attributes;
1406
+ }
1407
+ normalizeClassLike(node) {
1408
+ let extendsExpression;
1409
+ const extendsClause = node.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
1410
+ if (extendsClause?.types && extendsClause.types.length > 0) {
1411
+ extendsExpression = extendsClause.types[0]?.expression;
1412
+ }
1413
+ const computedNames = [];
1414
+ const privateIdentifiers = /* @__PURE__ */ new Map();
1415
+ const instanceElements = [];
1416
+ const staticElements = [];
1417
+ let constructorElement;
1418
+ const pushElement = (element) => {
1419
+ if (element.kind === "constructor") {
1420
+ if (constructorElement) {
1421
+ this.failUnsupported(element.node, "multiple constructors are not supported");
1422
+ }
1423
+ constructorElement = element;
1424
+ instanceElements.push(element);
1425
+ return;
1426
+ }
1427
+ if (element.isStatic) {
1428
+ staticElements.push(element);
1429
+ } else {
1430
+ instanceElements.push(element);
1431
+ }
1432
+ };
1433
+ for (const member of node.members) {
1434
+ if (ts3.isSemicolonClassElement(member)) {
1435
+ continue;
1436
+ }
1437
+ if (ts3.isClassStaticBlockDeclaration(member)) {
1438
+ pushElement({
1439
+ kind: "static_block",
1440
+ node: member,
1441
+ isStatic: true
1442
+ });
1443
+ continue;
1444
+ }
1445
+ const modifiers = ts3.canHaveModifiers(member) ? ts3.getModifiers(member) ?? [] : [];
1446
+ if (ts3.isPropertyDeclaration(member) && modifiers.some((modifier) => modifier.kind === ts3.SyntaxKind.DeclareKeyword)) {
1447
+ continue;
1448
+ }
1449
+ const privateBindingName = "name" in member && member.name && ts3.isPrivateIdentifier(member.name) ? privateIdentifiers.get(member.name.text) ?? this.createSyntheticBindingName("private_slot") : void 0;
1450
+ if (privateBindingName && "name" in member && member.name && ts3.isPrivateIdentifier(member.name) && !privateIdentifiers.has(member.name.text)) {
1451
+ privateIdentifiers.set(member.name.text, privateBindingName);
1452
+ }
1453
+ if (privateBindingName && !ts3.isPropertyDeclaration(member)) {
1454
+ this.failUnsupported(member.name, "private class methods and accessors are not supported on the vm-safe path");
1455
+ }
1456
+ const isStatic = modifiers.some((modifier) => modifier.kind === ts3.SyntaxKind.StaticKeyword);
1457
+ const computedBindingName = "name" in member && member.name && ts3.isComputedPropertyName(member.name) ? this.createSyntheticBindingName("class_key") : void 0;
1458
+ if (computedBindingName && "name" in member && member.name && ts3.isComputedPropertyName(member.name)) {
1459
+ computedNames.push({
1460
+ bindingName: computedBindingName,
1461
+ expression: member.name.expression
1462
+ });
1463
+ }
1464
+ const keyName = "name" in member && member.name && !ts3.isComputedPropertyName(member.name) && !ts3.isPrivateIdentifier(member.name) ? this.getPropertyNameText(member.name) : void 0;
1465
+ if (ts3.isConstructorDeclaration(member)) {
1466
+ if (member.parameters.some((parameter) => parameter.modifiers?.length)) {
1467
+ this.failUnsupported(member, "parameter properties are not supported on the vm-safe path");
1468
+ }
1469
+ pushElement({
1470
+ kind: "constructor",
1471
+ node: member,
1472
+ isStatic: false
1473
+ });
1474
+ continue;
1475
+ }
1476
+ if (ts3.isMethodDeclaration(member)) {
1477
+ pushElement({
1478
+ kind: "method",
1479
+ node: member,
1480
+ isStatic,
1481
+ keyName,
1482
+ computedBindingName
1483
+ });
1484
+ continue;
1485
+ }
1486
+ if (ts3.isGetAccessorDeclaration(member)) {
1487
+ pushElement({
1488
+ kind: "getter",
1489
+ node: member,
1490
+ isStatic,
1491
+ keyName,
1492
+ computedBindingName
1493
+ });
1494
+ continue;
1495
+ }
1496
+ if (ts3.isSetAccessorDeclaration(member)) {
1497
+ pushElement({
1498
+ kind: "setter",
1499
+ node: member,
1500
+ isStatic,
1501
+ keyName,
1502
+ computedBindingName
1503
+ });
1504
+ continue;
1505
+ }
1506
+ if (ts3.isPropertyDeclaration(member)) {
1507
+ pushElement({
1508
+ kind: "field",
1509
+ node: member,
1510
+ isStatic,
1511
+ keyName,
1512
+ computedBindingName,
1513
+ privateBindingName
1514
+ });
1515
+ continue;
1516
+ }
1517
+ this.failUnsupported(member, "unsupported class element");
1518
+ }
1519
+ return {
1520
+ bindingName: node.name?.text,
1521
+ constructorElement,
1522
+ computedNames,
1523
+ privateIdentifiers,
1524
+ instanceElements,
1525
+ staticElements,
1526
+ extendsExpression
1527
+ };
1528
+ }
1529
+ lowerClassConstructor(normalized, availableOuterNames, classDisplayName) {
1530
+ const instanceFields = normalized.instanceElements.filter(
1531
+ (element) => element.kind === "field"
1532
+ );
1533
+ const explicitCtor = normalized.constructorElement?.kind === "constructor" ? normalized.constructorElement.node : void 0;
1534
+ let ctorNode;
1535
+ if (explicitCtor) {
1536
+ ctorNode = explicitCtor;
1537
+ } else {
1538
+ const bodyStatements = [];
1539
+ if (normalized.extendsExpression) {
1540
+ bodyStatements.push(ts3.factory.createExpressionStatement(ts3.factory.createCallExpression(ts3.factory.createSuper(), void 0, [])));
1541
+ }
1542
+ ctorNode = ts3.factory.createFunctionExpression(
1543
+ void 0,
1544
+ void 0,
1545
+ void 0,
1546
+ void 0,
1547
+ [],
1548
+ void 0,
1549
+ ts3.factory.createBlock(bodyStatements, true)
1550
+ );
1551
+ }
1552
+ const fieldInitializerNodes = instanceFields.flatMap((field) => {
1553
+ const nodes = [];
1554
+ if (field.node.initializer) {
1555
+ nodes.push(field.node.initializer);
1556
+ }
1557
+ return nodes;
1558
+ });
1559
+ const computedFieldCaptureNames = instanceFields.map((field) => field.computedBindingName).filter((name) => !!name);
1560
+ const extraCapturedFromOuter = [
1561
+ ...this.collectReferencedOuterNames(fieldInitializerNodes, availableOuterNames),
1562
+ ...computedFieldCaptureNames,
1563
+ ...normalized.privateIdentifiers.values()
1564
+ ];
1565
+ const analysisBase = analyzeFunctionClosures(ctorNode, new Set(availableOuterNames));
1566
+ const analysis = {
1567
+ ...analysisBase,
1568
+ capturedFromOuter: [.../* @__PURE__ */ new Set([...analysisBase.capturedFromOuter, ...extraCapturedFromOuter])]
1569
+ };
1570
+ const nestedName = this.createNestedFunctionName();
1571
+ const nestedLowering = new _ASTLowering(this.modBuilder, ctorNode, {
1572
+ name: nestedName,
1573
+ isExported: false,
1574
+ isVirtualized: true,
1575
+ analysis,
1576
+ attributes: [FunctionAttribute.Constructor],
1577
+ isNested: true,
1578
+ prologueEmitter: (lowering) => {
1579
+ lowering.emitClassConstructorGuard(classDisplayName);
1580
+ if (!normalized.extendsExpression) {
1581
+ lowering.emitInstanceFieldInitializers(instanceFields);
1582
+ }
1583
+ },
1584
+ privateIdentifierBindings: normalized.privateIdentifiers,
1585
+ instanceFieldsToInitialize: normalized.extendsExpression ? instanceFields : void 0
1586
+ });
1587
+ const envReg = this.buildClosureEnvironment(analysis.capturedFromOuter);
1588
+ const functionIdIndex = this.modBuilder.addConstant(ConstantKind2.String, nestedLowering.functionId);
1589
+ const closureReg = this.fnBuilder.allocRegister();
1590
+ this.currentBlock.addInstruction(
1591
+ OpCode2.ClosureNew,
1592
+ [
1593
+ { kind: OperandKind2.ConstantIndex, value: functionIdIndex },
1594
+ { kind: OperandKind2.Register, value: envReg }
1595
+ ],
1596
+ closureReg
1597
+ );
1598
+ return closureReg;
1599
+ }
1600
+ lowerNestedFunctionLike(expr, explicitName, overrides) {
1601
+ const nestedName = explicitName ?? this.createNestedFunctionName();
1602
+ const availableOuterNames = overrides?.availableOuterNames ?? this.getAvailableOuterCaptureNames();
1603
+ const baseAnalysis = analyzeFunctionClosures(expr, new Set(availableOuterNames));
1604
+ const analysis = {
1605
+ ...baseAnalysis,
1606
+ capturedFromOuter: [.../* @__PURE__ */ new Set([...baseAnalysis.capturedFromOuter, ...overrides?.extraCapturedFromOuter ?? []])]
1607
+ };
1608
+ const nestedLowering = new _ASTLowering(this.modBuilder, expr, {
1609
+ name: nestedName,
1610
+ isExported: false,
1611
+ isVirtualized: true,
1612
+ analysis,
1613
+ attributes: [...this.getFunctionAttributes(expr), ...overrides?.attributes ?? []],
1614
+ isNested: true,
1615
+ prologueEmitter: overrides?.prologueEmitter,
1616
+ privateIdentifierBindings: overrides?.privateIdentifierBindings
1617
+ });
1618
+ const envReg = this.buildClosureEnvironment(analysis.capturedFromOuter);
1619
+ const functionIdIndex = this.modBuilder.addConstant(ConstantKind2.String, nestedLowering.functionId);
1620
+ const closureReg = this.fnBuilder.allocRegister();
1621
+ this.currentBlock.addInstruction(
1622
+ OpCode2.ClosureNew,
1623
+ [
1624
+ { kind: OperandKind2.ConstantIndex, value: functionIdIndex },
1625
+ { kind: OperandKind2.Register, value: envReg }
1626
+ ],
1627
+ closureReg
1628
+ );
1629
+ return closureReg;
1630
+ }
1631
+ emitClassMethodOrAccessorDescriptor(targetReg, keyReg, computed, element, fnReg) {
1632
+ const descriptorReg = this.fnBuilder.allocRegister();
1633
+ this.currentBlock.addInstruction(OpCode2.ObjectNew, [], descriptorReg);
1634
+ const enumerableReg = this.emitConstant(ConstantKind2.Boolean, false);
1635
+ const configurableReg = this.emitConstant(ConstantKind2.Boolean, true);
1636
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "enumerable"), enumerableReg, false);
1637
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "configurable"), configurableReg, false);
1638
+ if (element.kind === "method" || element.kind === "constructor") {
1639
+ const writableReg = this.emitConstant(ConstantKind2.Boolean, true);
1640
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "writable"), writableReg, false);
1641
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "value"), fnReg, false);
1642
+ this.emitDefineProperty(targetReg, keyReg, descriptorReg);
1643
+ return;
1644
+ }
1645
+ const existingDescriptorReg = this.buildAccessorDescriptorSource(targetReg, keyReg, computed);
1646
+ const accessorValueReg = this.fnBuilder.allocRegister();
1647
+ if (element.kind === "getter") {
1648
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "get"), fnReg, false);
1649
+ this.currentBlock.addInstruction(
1650
+ OpCode2.PropGet,
1651
+ [
1652
+ { kind: OperandKind2.Register, value: existingDescriptorReg },
1653
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "set") }
1654
+ ],
1655
+ accessorValueReg
1656
+ );
1657
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "set"), accessorValueReg, false);
1658
+ } else {
1659
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "set"), fnReg, false);
1660
+ this.currentBlock.addInstruction(
1661
+ OpCode2.PropGet,
1662
+ [
1663
+ { kind: OperandKind2.Register, value: existingDescriptorReg },
1664
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "get") }
1665
+ ],
1666
+ accessorValueReg
1667
+ );
1668
+ this.emitObjectPropertyWrite(descriptorReg, this.emitConstant(ConstantKind2.String, "get"), accessorValueReg, false);
1669
+ }
1670
+ this.emitDefineProperty(targetReg, keyReg, descriptorReg);
1671
+ }
1672
+ lowerClassLike(node, classBindingNameOverride) {
1673
+ const extendsExpr = node.heritageClauses?.find((c) => c.token === ts3.SyntaxKind.ExtendsKeyword)?.types[0]?.expression;
1674
+ let parentClassReg;
1675
+ if (extendsExpr) {
1676
+ parentClassReg = this.visitExpression(extendsExpr);
1677
+ }
1678
+ const normalized = this.normalizeClassLike(node);
1679
+ const classBindingName = classBindingNameOverride ?? normalized.bindingName;
1680
+ let classBinding;
1681
+ let restoreBinding;
1682
+ if (classBindingName) {
1683
+ if (classBindingNameOverride && classBindingNameOverride === normalized.bindingName) {
1684
+ restoreBinding = this.scope.get(classBindingName);
1685
+ classBinding = this.declareForcedBoxedIdentifier(classBindingName);
1686
+ } else if (this.scope.has(classBindingName)) {
1687
+ classBinding = this.scope.get(classBindingName);
1688
+ } else {
1689
+ classBinding = this.declareForcedBoxedIdentifier(classBindingName);
1690
+ }
1691
+ }
1692
+ for (const computedName of normalized.computedNames) {
1693
+ this.declareForcedBoxedIdentifier(computedName.bindingName);
1694
+ const valueReg = this.visitExpression(computedName.expression);
1695
+ this.initializeDeclaredIdentifier(computedName.bindingName, valueReg);
1696
+ }
1697
+ for (const bindingName of normalized.privateIdentifiers.values()) {
1698
+ this.declareForcedBoxedIdentifier(bindingName);
1699
+ this.initializeDeclaredIdentifier(bindingName, this.emitConstant(ConstantKind2.String, bindingName));
1700
+ }
1701
+ const availableOuterNames = this.getAvailableOuterCaptureNames();
1702
+ const ctorReg = this.lowerClassConstructor(normalized, availableOuterNames, classBindingName ?? "<anonymous>");
1703
+ if (classBindingName) {
1704
+ this.initializeDeclaredIdentifier(classBindingName, ctorReg);
1705
+ }
1706
+ const prototypeReg = this.fnBuilder.allocRegister();
1707
+ this.currentBlock.addInstruction(
1708
+ OpCode2.PropGet,
1709
+ [
1710
+ { kind: OperandKind2.Register, value: ctorReg },
1711
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "prototype") }
1712
+ ],
1713
+ prototypeReg
1714
+ );
1715
+ if (parentClassReg) {
1716
+ const parentProtoReg = this.fnBuilder.allocRegister();
1717
+ this.currentBlock.addInstruction(
1718
+ OpCode2.PropGet,
1719
+ [
1720
+ { kind: OperandKind2.Register, value: parentClassReg },
1721
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "prototype") }
1722
+ ],
1723
+ parentProtoReg
1724
+ );
1725
+ const setPrototypeOfReg = this.resolveVar("Object");
1726
+ const setProtoMethodReg = this.fnBuilder.allocRegister();
1727
+ this.currentBlock.addInstruction(
1728
+ OpCode2.PropGet,
1729
+ [
1730
+ { kind: OperandKind2.Register, value: setPrototypeOfReg },
1731
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "setPrototypeOf") }
1732
+ ],
1733
+ setProtoMethodReg
1734
+ );
1735
+ this.currentBlock.addInstruction(
1736
+ OpCode2.Call,
1737
+ [
1738
+ { kind: OperandKind2.Register, value: setProtoMethodReg },
1739
+ { kind: OperandKind2.Register, value: prototypeReg },
1740
+ { kind: OperandKind2.Register, value: parentProtoReg }
1741
+ ],
1742
+ this.fnBuilder.allocRegister()
1743
+ );
1744
+ this.currentBlock.addInstruction(
1745
+ OpCode2.Call,
1746
+ [
1747
+ { kind: OperandKind2.Register, value: setProtoMethodReg },
1748
+ { kind: OperandKind2.Register, value: ctorReg },
1749
+ { kind: OperandKind2.Register, value: parentClassReg }
1750
+ ],
1751
+ this.fnBuilder.allocRegister()
1752
+ );
1753
+ }
1754
+ for (const element of normalized.instanceElements) {
1755
+ if (element.kind === "field" || element.kind === "constructor") {
1756
+ continue;
1757
+ }
1758
+ const methodElement = element;
1759
+ const methodReg = this.lowerNestedFunctionLike(methodElement.node, void 0, {
1760
+ availableOuterNames,
1761
+ extraCapturedFromOuter: [...normalized.privateIdentifiers.values()],
1762
+ privateIdentifierBindings: normalized.privateIdentifiers
1763
+ });
1764
+ const key = this.getPropertyKeyRegister(methodElement.keyName, methodElement.computedBindingName);
1765
+ this.emitClassMethodOrAccessorDescriptor(prototypeReg, key.register, key.computed, methodElement, methodReg);
1766
+ }
1767
+ for (const element of normalized.staticElements) {
1768
+ if (element.kind === "field") {
1769
+ const valueReg = element.node.initializer ? this.visitExpression(element.node.initializer) : this.emitConstant(ConstantKind2.Undefined, null);
1770
+ if (element.privateBindingName) {
1771
+ const privateKeyReg = this.resolveVar(element.privateBindingName);
1772
+ this.currentBlock.addInstruction(OpCode2.PrivateSet, [
1773
+ { kind: OperandKind2.Register, value: ctorReg },
1774
+ { kind: OperandKind2.Register, value: privateKeyReg },
1775
+ { kind: OperandKind2.Register, value: valueReg }
1776
+ ]);
1777
+ continue;
1778
+ }
1779
+ const key2 = this.getPropertyKeyRegister(element.keyName, element.computedBindingName);
1780
+ this.emitObjectPropertyWrite(ctorReg, key2.register, valueReg, key2.computed);
1781
+ continue;
1782
+ }
1783
+ if (element.kind === "static_block") {
1784
+ const fakeFn = ts3.factory.createFunctionExpression(void 0, void 0, void 0, void 0, [], void 0, element.node.body);
1785
+ const fnReg = this.lowerNestedFunctionLike(fakeFn, void 0, {
1786
+ availableOuterNames,
1787
+ extraCapturedFromOuter: [...normalized.privateIdentifiers.values()],
1788
+ privateIdentifierBindings: normalized.privateIdentifiers,
1789
+ attributes: [FunctionAttribute.Static]
1790
+ });
1791
+ const callPropReg = this.emitConstant(ConstantKind2.String, "call");
1792
+ this.currentBlock.addInstruction(
1793
+ OpCode2.CallMethod,
1794
+ [
1795
+ { kind: OperandKind2.Register, value: fnReg },
1796
+ { kind: OperandKind2.Register, value: callPropReg },
1797
+ { kind: OperandKind2.Register, value: ctorReg }
1798
+ ],
1799
+ this.fnBuilder.allocRegister()
1800
+ );
1801
+ continue;
1802
+ }
1803
+ const methodElement = element;
1804
+ const methodReg = this.lowerNestedFunctionLike(methodElement.node, void 0, {
1805
+ availableOuterNames,
1806
+ extraCapturedFromOuter: [...normalized.privateIdentifiers.values()],
1807
+ privateIdentifierBindings: normalized.privateIdentifiers
1808
+ });
1809
+ const key = this.getPropertyKeyRegister(methodElement.keyName, methodElement.computedBindingName);
1810
+ this.emitClassMethodOrAccessorDescriptor(ctorReg, key.register, key.computed, methodElement, methodReg);
1811
+ }
1812
+ if (restoreBinding) {
1813
+ this.scope.set(classBindingName, restoreBinding);
1814
+ } else if (classBindingNameOverride && classBindingNameOverride === normalized.bindingName) {
1815
+ this.scope.delete(classBindingName);
1816
+ }
1817
+ return ctorReg;
1818
+ }
1819
+ emitClassConstructorGuard(classDisplayName) {
1820
+ const newTargetReg = this.fnBuilder.allocRegister();
1821
+ this.currentBlock.addInstruction(OpCode2.LoadNewTarget, [], newTargetReg);
1822
+ const undefinedReg = this.emitConstant(ConstantKind2.Undefined, null);
1823
+ const isUndefinedReg = this.fnBuilder.allocRegister();
1824
+ this.currentBlock.addInstruction(
1825
+ OpCode2.StrictEq,
1826
+ [
1827
+ { kind: OperandKind2.Register, value: newTargetReg },
1828
+ { kind: OperandKind2.Register, value: undefinedReg }
1829
+ ],
1830
+ isUndefinedReg
1831
+ );
1832
+ const throwBlock = this.fnBuilder.createBlock("class_ctor_throw");
1833
+ const continueBlock = this.fnBuilder.createBlock("class_ctor_continue");
1834
+ this.currentBlock.setTerminator({ kind: "branch", condition: isUndefinedReg, targets: [throwBlock.id, continueBlock.id] });
1835
+ this.fnBuilder.addBlock(this.currentBlock.build());
1836
+ this.currentBlock = throwBlock;
1837
+ const typeErrorReg = this.resolveVar("TypeError");
1838
+ const messageReg = this.emitConstant(ConstantKind2.String, `Class constructor ${classDisplayName} cannot be invoked without 'new'`);
1839
+ const errorReg = this.fnBuilder.allocRegister();
1840
+ this.currentBlock.addInstruction(
1841
+ OpCode2.New,
1842
+ [
1843
+ { kind: OperandKind2.Register, value: typeErrorReg },
1844
+ { kind: OperandKind2.Register, value: messageReg }
1845
+ ],
1846
+ errorReg
1847
+ );
1848
+ this.currentBlock.setTerminator({ kind: "throw", targets: [], returnValue: errorReg });
1849
+ this.fnBuilder.addBlock(this.currentBlock.build());
1850
+ this.currentBlock = continueBlock;
1851
+ }
1852
+ emitInstanceFieldInitializers(fields) {
1853
+ if (fields.length === 0) {
1854
+ return;
1855
+ }
1856
+ const thisReg = this.fnBuilder.allocRegister();
1857
+ this.currentBlock.addInstruction(OpCode2.LoadThis, [], thisReg);
1858
+ for (const field of fields) {
1859
+ const valueReg = field.node.initializer ? this.visitExpression(field.node.initializer) : this.emitConstant(ConstantKind2.Undefined, null);
1860
+ if (field.privateBindingName) {
1861
+ const privateKeyReg = this.resolveVar(field.privateBindingName);
1862
+ this.currentBlock.addInstruction(OpCode2.PrivateSet, [
1863
+ { kind: OperandKind2.Register, value: thisReg },
1864
+ { kind: OperandKind2.Register, value: privateKeyReg },
1865
+ { kind: OperandKind2.Register, value: valueReg }
1866
+ ]);
1867
+ continue;
1868
+ }
1869
+ const key = this.getPropertyKeyRegister(field.keyName, field.computedBindingName);
1870
+ this.emitObjectPropertyWrite(thisReg, key.register, valueReg, key.computed);
1871
+ }
1872
+ }
1873
+ resolvePrivateIdentifierRegister(identifier) {
1874
+ const bindingName = this.privateIdentifierBindings.get(identifier.text);
1875
+ if (!bindingName) {
1876
+ this.failUnsupported(identifier, `Unknown private identifier ${identifier.text}`);
1877
+ }
1878
+ return this.resolveVar(bindingName);
1879
+ }
1880
+ lowerNestedFunction(expr) {
1881
+ return this.lowerNestedFunctionLike(expr);
1882
+ }
1883
+ buildClosureEnvironment(capturedNames) {
1884
+ const envReg = this.fnBuilder.allocRegister();
1885
+ this.currentBlock.addInstruction(OpCode2.ArrayNew, [], envReg);
1886
+ capturedNames.forEach((name, index) => {
1887
+ const indexReg = this.emitConstant(ConstantKind2.Number, index);
1888
+ const cellReg = this.getCellForCapture(name);
1889
+ this.currentBlock.addInstruction(OpCode2.ComputedSet, [
1890
+ { kind: OperandKind2.Register, value: envReg },
1891
+ { kind: OperandKind2.Register, value: indexReg },
1892
+ { kind: OperandKind2.Register, value: cellReg }
1893
+ ]);
1894
+ });
1895
+ return envReg;
1896
+ }
1897
+ getCellForCapture(name) {
1898
+ const localBinding = this.scope.get(name);
1899
+ if (localBinding) {
1900
+ if (!localBinding.boxed) {
1901
+ this.currentBlock.addInstruction(
1902
+ OpCode2.CellNew,
1903
+ [{ kind: OperandKind2.Register, value: localBinding.register }],
1904
+ localBinding.register
1905
+ );
1906
+ this.scope.set(name, { register: localBinding.register, boxed: true });
1907
+ this.fnBuilder.addCapturedVariable(name);
1908
+ return localBinding.register;
1909
+ }
1910
+ return localBinding.register;
1911
+ }
1912
+ if (name === LEXICAL_THIS_CAPTURE || name === LEXICAL_NEW_TARGET_CAPTURE) {
1913
+ this.initializeLexicalCapture(name);
1914
+ const lexicalBinding = this.scope.get(name);
1915
+ if (lexicalBinding?.boxed) {
1916
+ return lexicalBinding.register;
1917
+ }
1918
+ }
1919
+ if (this.outerCaptureBindings.has(name)) {
1920
+ return this.loadOuterCaptureCell(name);
1921
+ }
1922
+ this.failUnsupported(this.findIdentifierNode(name) ?? this.node, `Unknown capture "${name}"`);
1923
+ }
1924
+ storeValue(target, valueReg) {
1925
+ target = this.normalizeExpression(target);
1926
+ if (ts3.isIdentifier(target)) {
1927
+ if (this.scope.has(target.text)) {
1928
+ const binding = this.scope.get(target.text);
1929
+ if (binding.boxed) {
1930
+ this.currentBlock.addInstruction(OpCode2.CellSet, [
1931
+ { kind: OperandKind2.Register, value: binding.register },
1932
+ { kind: OperandKind2.Register, value: valueReg }
1933
+ ]);
1934
+ } else {
1935
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
1936
+ { kind: OperandKind2.Register, value: binding.register },
1937
+ { kind: OperandKind2.Register, value: valueReg }
1938
+ ]);
1939
+ }
1940
+ } else if (this.outerCaptureBindings.has(target.text)) {
1941
+ const cellReg = this.loadOuterCaptureCell(target.text);
1942
+ this.currentBlock.addInstruction(OpCode2.CellSet, [
1943
+ { kind: OperandKind2.Register, value: cellReg },
1944
+ { kind: OperandKind2.Register, value: valueReg }
1945
+ ]);
1946
+ } else {
1947
+ const strReg = this.emitConstant(ConstantKind2.String, target.text);
1948
+ this.currentBlock.addInstruction(OpCode2.StoreGlobal, [
1949
+ { kind: OperandKind2.Register, value: strReg },
1950
+ { kind: OperandKind2.Register, value: valueReg }
1951
+ ]);
1952
+ }
1953
+ return;
1954
+ }
1955
+ if (ts3.isPropertyAccessExpression(target)) {
1956
+ if (target.expression.kind === ts3.SyntaxKind.SuperKeyword) {
1957
+ const propReg2 = this.emitConstant(ConstantKind2.String, target.name.text);
1958
+ this.currentBlock.addInstruction(OpCode2.SuperPropSet, [
1959
+ { kind: OperandKind2.Register, value: propReg2 },
1960
+ { kind: OperandKind2.Register, value: valueReg }
1961
+ ]);
1962
+ return;
1963
+ }
1964
+ const objReg = this.visitExpression(target.expression);
1965
+ const propReg = this.emitConstant(ConstantKind2.String, target.name.text);
1966
+ if (ts3.isPrivateIdentifier(target.name)) {
1967
+ const privateKeyReg = this.resolvePrivateIdentifierRegister(target.name);
1968
+ this.currentBlock.addInstruction(OpCode2.PrivateSet, [
1969
+ { kind: OperandKind2.Register, value: objReg },
1970
+ { kind: OperandKind2.Register, value: privateKeyReg },
1971
+ { kind: OperandKind2.Register, value: valueReg }
1972
+ ]);
1973
+ return;
1974
+ }
1975
+ this.currentBlock.addInstruction(OpCode2.PropSet, [
1976
+ { kind: OperandKind2.Register, value: objReg },
1977
+ { kind: OperandKind2.Register, value: propReg },
1978
+ { kind: OperandKind2.Register, value: valueReg }
1979
+ ]);
1980
+ return;
1981
+ }
1982
+ if (ts3.isElementAccessExpression(target)) {
1983
+ if (!target.argumentExpression) {
1984
+ this.failUnsupported(target, "Element access assignment requires an index expression");
1985
+ }
1986
+ const objReg = this.visitExpression(target.expression);
1987
+ const indexReg = this.visitExpression(target.argumentExpression);
1988
+ this.currentBlock.addInstruction(OpCode2.ComputedSet, [
1989
+ { kind: OperandKind2.Register, value: objReg },
1990
+ { kind: OperandKind2.Register, value: indexReg },
1991
+ { kind: OperandKind2.Register, value: valueReg }
1992
+ ]);
1993
+ return;
1994
+ }
1995
+ if (ts3.isArrayLiteralExpression(target)) {
1996
+ this.storeArrayPattern(target, valueReg);
1997
+ return;
1998
+ }
1999
+ if (ts3.isObjectLiteralExpression(target)) {
2000
+ this.storeObjectPattern(target, valueReg);
2001
+ return;
2002
+ }
2003
+ this.failUnsupported(target, "Unsupported assignment target");
2004
+ }
2005
+ readValue(target) {
2006
+ if (ts3.isIdentifier(target)) {
2007
+ return this.resolveVar(target.text);
2008
+ }
2009
+ if (ts3.isPropertyAccessExpression(target) || ts3.isElementAccessExpression(target)) {
2010
+ return this.visitExpression(target);
2011
+ }
2012
+ this.failUnsupported(target, "Unsupported read target");
2013
+ }
2014
+ emitObjectPropertyAssignment(objectReg, nameReg, valueReg, computed = false) {
2015
+ this.currentBlock.addInstruction(computed ? OpCode2.ComputedSet : OpCode2.PropSet, [
2016
+ { kind: OperandKind2.Register, value: objectReg },
2017
+ { kind: OperandKind2.Register, value: nameReg },
2018
+ { kind: OperandKind2.Register, value: valueReg }
2019
+ ]);
2020
+ }
2021
+ normalizeExpression(expr) {
2022
+ let current = expr;
2023
+ while (ts3.isParenthesizedExpression(current) || ts3.isAsExpression(current) || ts3.isNonNullExpression(current) || ts3.isSatisfiesExpression(current) || ts3.isTypeAssertionExpression(current)) {
2024
+ if (ts3.isParenthesizedExpression(current)) {
2025
+ current = current.expression;
2026
+ } else {
2027
+ current = current.expression;
2028
+ }
2029
+ }
2030
+ return current;
2031
+ }
2032
+ createTempLocal(prefix) {
2033
+ return this.fnBuilder.addLocal(`$${prefix}_${this.tempLocalCount++}`, IRType.Any);
2034
+ }
2035
+ createSyntheticBindingName(prefix) {
2036
+ return `$$${prefix}_${this.tempLocalCount++}`;
2037
+ }
2038
+ declareScopedIdentifier(name) {
2039
+ if (this.scope.hasOwn(name)) {
2040
+ return this.scope.get(name);
2041
+ }
2042
+ const boxed = this.capturedLocals.has(name);
2043
+ const register = this.fnBuilder.addLocal(name, IRType.Any);
2044
+ const binding = { register, boxed };
2045
+ this.scope.set(name, binding);
2046
+ return binding;
2047
+ }
2048
+ declareForcedBoxedIdentifier(name) {
2049
+ const existing = this.scope.get(name);
2050
+ if (existing) {
2051
+ return existing;
2052
+ }
2053
+ const register = this.fnBuilder.addLocal(name, IRType.Any, true);
2054
+ const binding = { register, boxed: true };
2055
+ this.scope.set(name, binding);
2056
+ const undefReg = this.emitConstant(ConstantKind2.Undefined, null);
2057
+ this.currentBlock.addInstruction(OpCode2.CellNew, [{ kind: OperandKind2.Register, value: undefReg }], binding.register);
2058
+ this.fnBuilder.addCapturedVariable(name);
2059
+ return binding;
2060
+ }
2061
+ withTemporaryBinding(name, callback) {
2062
+ const restoreBinding = this.scope.get(name);
2063
+ const binding = this.declareForcedBoxedIdentifier(name);
2064
+ const result = callback();
2065
+ if (restoreBinding) {
2066
+ this.scope.set(name, restoreBinding);
2067
+ } else {
2068
+ this.scope.delete(name);
2069
+ }
2070
+ return { result, binding, restoreBinding };
2071
+ }
2072
+ getPropertyKeyRegister(keyName, computedBindingName) {
2073
+ if (computedBindingName) {
2074
+ return {
2075
+ register: this.resolveVar(computedBindingName),
2076
+ computed: true
2077
+ };
2078
+ }
2079
+ if (keyName === void 0) {
2080
+ this.failUnsupported(this.node, "Missing class element key");
2081
+ }
2082
+ return {
2083
+ register: this.emitConstant(ConstantKind2.String, keyName),
2084
+ computed: false
2085
+ };
2086
+ }
2087
+ buildAccessorDescriptorSource(targetReg, keyReg, computed) {
2088
+ const descriptorReg = this.fnBuilder.allocRegister();
2089
+ const objectReg = this.resolveVar("Object");
2090
+ const descriptorMethod = this.emitConstant(ConstantKind2.String, "getOwnPropertyDescriptor");
2091
+ const callArgs = [
2092
+ { kind: OperandKind2.Register, value: objectReg },
2093
+ { kind: OperandKind2.Register, value: descriptorMethod },
2094
+ { kind: OperandKind2.Register, value: targetReg },
2095
+ { kind: OperandKind2.Register, value: keyReg }
2096
+ ];
2097
+ this.currentBlock.addInstruction(OpCode2.CallMethod, callArgs, descriptorReg);
2098
+ const descriptorLocal = this.createTempLocal("accessor_desc");
2099
+ this.storeToLocal(descriptorLocal, descriptorReg);
2100
+ const undefinedReg = this.emitConstant(ConstantKind2.Undefined, null);
2101
+ const isUndefinedReg = this.fnBuilder.allocRegister();
2102
+ this.currentBlock.addInstruction(
2103
+ OpCode2.StrictEq,
2104
+ [
2105
+ { kind: OperandKind2.Register, value: descriptorReg },
2106
+ { kind: OperandKind2.Register, value: undefinedReg }
2107
+ ],
2108
+ isUndefinedReg
2109
+ );
2110
+ const undefinedBlock = this.fnBuilder.createBlock("accessor_desc_undefined");
2111
+ const endBlock = this.fnBuilder.createBlock("accessor_desc_end");
2112
+ this.currentBlock.setTerminator({ kind: "branch", condition: isUndefinedReg, targets: [undefinedBlock.id, endBlock.id] });
2113
+ this.fnBuilder.addBlock(this.currentBlock.build());
2114
+ this.currentBlock = undefinedBlock;
2115
+ const emptyDescriptorReg = this.fnBuilder.allocRegister();
2116
+ this.currentBlock.addInstruction(OpCode2.ObjectNew, [], emptyDescriptorReg);
2117
+ this.storeToLocal(descriptorLocal, emptyDescriptorReg);
2118
+ this.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
2119
+ this.fnBuilder.addBlock(this.currentBlock.build());
2120
+ this.currentBlock = endBlock;
2121
+ return this.loadFromLocal(descriptorLocal);
2122
+ }
2123
+ emitObjectPropertyWrite(targetReg, keyReg, valueReg, computed) {
2124
+ this.currentBlock.addInstruction(computed ? OpCode2.ComputedSet : OpCode2.PropSet, [
2125
+ { kind: OperandKind2.Register, value: targetReg },
2126
+ { kind: OperandKind2.Register, value: keyReg },
2127
+ { kind: OperandKind2.Register, value: valueReg }
2128
+ ]);
2129
+ }
2130
+ emitDefineProperty(targetReg, keyReg, descriptorReg) {
2131
+ const objectReg = this.resolveVar("Object");
2132
+ const definePropertyReg = this.emitConstant(ConstantKind2.String, "defineProperty");
2133
+ this.currentBlock.addInstruction(
2134
+ OpCode2.CallMethod,
2135
+ [
2136
+ { kind: OperandKind2.Register, value: objectReg },
2137
+ { kind: OperandKind2.Register, value: definePropertyReg },
2138
+ { kind: OperandKind2.Register, value: targetReg },
2139
+ { kind: OperandKind2.Register, value: keyReg },
2140
+ { kind: OperandKind2.Register, value: descriptorReg }
2141
+ ],
2142
+ this.fnBuilder.allocRegister()
2143
+ );
2144
+ }
2145
+ initializeDeclaredIdentifier(name, initializerReg) {
2146
+ const existing = this.scope.get(name);
2147
+ const binding = this.declareScopedIdentifier(name);
2148
+ if (initializerReg !== void 0) {
2149
+ if (binding.boxed) {
2150
+ if (existing) {
2151
+ this.currentBlock.addInstruction(OpCode2.CellSet, [
2152
+ { kind: OperandKind2.Register, value: binding.register },
2153
+ { kind: OperandKind2.Register, value: initializerReg }
2154
+ ]);
2155
+ } else {
2156
+ this.currentBlock.addInstruction(OpCode2.CellNew, [{ kind: OperandKind2.Register, value: initializerReg }], binding.register);
2157
+ this.fnBuilder.addCapturedVariable(name);
2158
+ }
2159
+ } else {
2160
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
2161
+ { kind: OperandKind2.Register, value: binding.register },
2162
+ { kind: OperandKind2.Register, value: initializerReg }
2163
+ ]);
2164
+ }
2165
+ return;
2166
+ }
2167
+ if (binding.boxed) {
2168
+ const undefReg = this.emitConstant(ConstantKind2.Undefined, null);
2169
+ this.currentBlock.addInstruction(OpCode2.CellNew, [{ kind: OperandKind2.Register, value: undefReg }], binding.register);
2170
+ this.fnBuilder.addCapturedVariable(name);
2171
+ }
2172
+ }
2173
+ applyDefaultValue(sourceReg, initializer) {
2174
+ if (!initializer) {
2175
+ return sourceReg;
2176
+ }
2177
+ const undefinedReg = this.emitConstant(ConstantKind2.Undefined, null);
2178
+ const isUndefinedReg = this.fnBuilder.allocRegister();
2179
+ this.currentBlock.addInstruction(
2180
+ OpCode2.StrictEq,
2181
+ [
2182
+ { kind: OperandKind2.Register, value: sourceReg },
2183
+ { kind: OperandKind2.Register, value: undefinedReg }
2184
+ ],
2185
+ isUndefinedReg
2186
+ );
2187
+ return this.lowerConditionalExpression(
2188
+ isUndefinedReg,
2189
+ () => this.visitExpression(initializer),
2190
+ () => sourceReg
2191
+ );
2192
+ }
2193
+ materializeBindingElementValue(element, sourceReg) {
2194
+ return this.applyDefaultValue(sourceReg, element.initializer);
2195
+ }
2196
+ emitRestArgs(startIndex) {
2197
+ const destReg = this.fnBuilder.allocRegister();
2198
+ const indexReg = this.emitConstant(ConstantKind2.Number, startIndex);
2199
+ this.currentBlock.addInstruction(OpCode2.RestArgs, [{ kind: OperandKind2.Register, value: indexReg }], destReg);
2200
+ return destReg;
2201
+ }
2202
+ emitArraySlice(sourceReg, startIndex) {
2203
+ const destReg = this.fnBuilder.allocRegister();
2204
+ const indexReg = this.emitConstant(ConstantKind2.Number, startIndex);
2205
+ this.currentBlock.addInstruction(
2206
+ OpCode2.CallMethod,
2207
+ [
2208
+ { kind: OperandKind2.Register, value: sourceReg },
2209
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "slice") },
2210
+ { kind: OperandKind2.Register, value: indexReg }
2211
+ ],
2212
+ destReg
2213
+ );
2214
+ return destReg;
2215
+ }
2216
+ emitSpreadInto(targetReg, sourceReg) {
2217
+ this.currentBlock.addInstruction(OpCode2.Spread, [
2218
+ { kind: OperandKind2.Register, value: targetReg },
2219
+ { kind: OperandKind2.Register, value: sourceReg }
2220
+ ]);
2221
+ }
2222
+ emitSpreadIntoArray(targetReg, sourceReg, startIndexReg, destIndexLocal) {
2223
+ const spreadCountReg = this.fnBuilder.allocRegister();
2224
+ this.currentBlock.addInstruction(
2225
+ OpCode2.SpreadIntoArray,
2226
+ [
2227
+ { kind: OperandKind2.Register, value: targetReg },
2228
+ { kind: OperandKind2.Register, value: sourceReg },
2229
+ { kind: OperandKind2.Register, value: startIndexReg }
2230
+ ],
2231
+ spreadCountReg
2232
+ );
2233
+ const nextIndexReg = this.fnBuilder.allocRegister();
2234
+ this.currentBlock.addInstruction(
2235
+ OpCode2.Add,
2236
+ [
2237
+ { kind: OperandKind2.Register, value: startIndexReg },
2238
+ { kind: OperandKind2.Register, value: spreadCountReg }
2239
+ ],
2240
+ nextIndexReg
2241
+ );
2242
+ this.storeToLocal(destIndexLocal, nextIndexReg);
2243
+ }
2244
+ bindPattern(bindingName, sourceReg, mode = "declare") {
2245
+ if (ts3.isIdentifier(bindingName)) {
2246
+ if (mode === "declare") {
2247
+ this.initializeDeclaredIdentifier(bindingName.text, sourceReg);
2248
+ } else {
2249
+ this.storeValue(bindingName, sourceReg);
2250
+ }
2251
+ return;
2252
+ }
2253
+ if (ts3.isObjectBindingPattern(bindingName)) {
2254
+ let restElement;
2255
+ const excludedKeys = [];
2256
+ for (const element of bindingName.elements) {
2257
+ if (element.dotDotDotToken) {
2258
+ restElement = element;
2259
+ continue;
2260
+ }
2261
+ const propertyName = element.propertyName ?? element.name;
2262
+ if (ts3.isObjectBindingPattern(propertyName) || ts3.isArrayBindingPattern(propertyName)) {
2263
+ this.failUnsupported(propertyName, "Invalid binding property name pattern");
2264
+ }
2265
+ const keyReg = ts3.isComputedPropertyName(propertyName) ? this.visitExpression(propertyName.expression) : this.emitConstant(ConstantKind2.String, this.getPropertyNameText(propertyName));
2266
+ excludedKeys.push(keyReg);
2267
+ const valueReg = this.fnBuilder.allocRegister();
2268
+ this.currentBlock.addInstruction(
2269
+ OpCode2.PropGet,
2270
+ [
2271
+ { kind: OperandKind2.Register, value: sourceReg },
2272
+ { kind: OperandKind2.Register, value: keyReg }
2273
+ ],
2274
+ valueReg
2275
+ );
2276
+ this.bindPattern(element.name, this.materializeBindingElementValue(element, valueReg), mode);
2277
+ }
2278
+ if (restElement) {
2279
+ const restReg = this.fnBuilder.allocRegister();
2280
+ this.currentBlock.addInstruction(OpCode2.ObjectNew, [], restReg);
2281
+ this.emitSpreadInto(restReg, sourceReg);
2282
+ excludedKeys.forEach((keyReg) => {
2283
+ this.currentBlock.addInstruction(OpCode2.Delete, [
2284
+ { kind: OperandKind2.Register, value: restReg },
2285
+ { kind: OperandKind2.Register, value: keyReg }
2286
+ ]);
2287
+ });
2288
+ this.bindPattern(restElement.name, restReg, mode);
2289
+ }
2290
+ return;
2291
+ }
2292
+ if (ts3.isArrayBindingPattern(bindingName)) {
2293
+ bindingName.elements.forEach((element, index) => {
2294
+ if (ts3.isOmittedExpression(element)) {
2295
+ return;
2296
+ }
2297
+ if (element.dotDotDotToken) {
2298
+ this.bindPattern(element.name, this.emitArraySlice(sourceReg, index), mode);
2299
+ return;
2300
+ }
2301
+ const indexReg = this.emitConstant(ConstantKind2.Number, index);
2302
+ const valueReg = this.fnBuilder.allocRegister();
2303
+ this.currentBlock.addInstruction(
2304
+ OpCode2.ComputedGet,
2305
+ [
2306
+ { kind: OperandKind2.Register, value: sourceReg },
2307
+ { kind: OperandKind2.Register, value: indexReg }
2308
+ ],
2309
+ valueReg
2310
+ );
2311
+ this.bindPattern(element.name, this.materializeBindingElementValue(element, valueReg), mode);
2312
+ });
2313
+ return;
2314
+ }
2315
+ this.failUnsupported(bindingName, "Unsupported binding pattern");
2316
+ }
2317
+ initializeVariableDeclaration(decl) {
2318
+ const initializerReg = decl.initializer ? this.visitExpression(decl.initializer) : void 0;
2319
+ if (ts3.isIdentifier(decl.name)) {
2320
+ this.initializeDeclaredIdentifier(decl.name.text, initializerReg);
2321
+ return;
2322
+ }
2323
+ if (initializerReg === void 0) {
2324
+ this.failUnsupported(decl.name, "Destructuring declarations require an initializer");
2325
+ }
2326
+ this.bindPattern(decl.name, initializerReg, "declare");
2327
+ }
2328
+ assignLoopBinding(initializer, valueReg, loopKind) {
2329
+ if (ts3.isVariableDeclarationList(initializer)) {
2330
+ if (initializer.declarations.length !== 1) {
2331
+ this.failUnsupported(initializer, `${loopKind} supports a single declaration only`);
2332
+ }
2333
+ const decl = initializer.declarations[0];
2334
+ if (decl.initializer) {
2335
+ this.failUnsupported(decl, `${loopKind} declaration initializers are not supported`);
2336
+ }
2337
+ if (ts3.isIdentifier(decl.name)) {
2338
+ this.initializeDeclaredIdentifier(decl.name.text, valueReg);
2339
+ return;
2340
+ }
2341
+ this.bindPattern(decl.name, valueReg, "declare");
2342
+ return;
2343
+ }
2344
+ this.storeValue(initializer, valueReg);
2345
+ }
2346
+ loadFromLocal(localReg) {
2347
+ const destReg = this.fnBuilder.allocRegister();
2348
+ this.currentBlock.addInstruction(OpCode2.LoadLocal, [{ kind: OperandKind2.Register, value: localReg }], destReg);
2349
+ return destReg;
2350
+ }
2351
+ getActiveFinallyContext() {
2352
+ return this.finallyContexts[this.finallyContexts.length - 1];
2353
+ }
2354
+ withFinallyContext(context, callback) {
2355
+ this.finallyContexts.push(context);
2356
+ try {
2357
+ return callback();
2358
+ } finally {
2359
+ this.finallyContexts.pop();
2360
+ }
2361
+ }
2362
+ withPassthroughThrow(callback) {
2363
+ this.throwPassthroughFinallyDepth += 1;
2364
+ try {
2365
+ return callback();
2366
+ } finally {
2367
+ this.throwPassthroughFinallyDepth -= 1;
2368
+ }
2369
+ }
2370
+ createFinallyContext(finallyBlockId) {
2371
+ return {
2372
+ finallyBlockId,
2373
+ completionKindLocal: this.createTempLocal("completion_kind"),
2374
+ completionValueLocal: this.createTempLocal("completion_value"),
2375
+ completionTargetLocal: this.createTempLocal("completion_target"),
2376
+ targets: []
2377
+ };
2378
+ }
2379
+ getCompletionTargetCode(context, kind, blockId) {
2380
+ const existing = context.targets.find((target) => target.kind === kind && target.blockId === blockId);
2381
+ if (existing) {
2382
+ return existing.code;
2383
+ }
2384
+ const code = context.targets.length + 1;
2385
+ context.targets.push({ code, kind, blockId });
2386
+ return code;
2387
+ }
2388
+ setFinallyCompletion(context, kind, valueReg, target) {
2389
+ this.storeToLocal(context.completionKindLocal, this.emitConstant(ConstantKind2.Number, kind));
2390
+ if (valueReg) {
2391
+ this.storeToLocal(context.completionValueLocal, valueReg);
2392
+ }
2393
+ if (target) {
2394
+ const codeReg = this.emitConstant(ConstantKind2.Number, this.getCompletionTargetCode(context, target.kind, target.blockId));
2395
+ this.storeToLocal(context.completionTargetLocal, codeReg);
2396
+ }
2397
+ }
2398
+ routeAbruptCompletionThroughFinally(kind, valueReg, target) {
2399
+ if (kind === 2 && this.throwPassthroughFinallyDepth > 0) {
2400
+ this.currentBlock.setTerminator({ kind: "throw", targets: [], returnValue: valueReg });
2401
+ this.fnBuilder.addBlock(this.currentBlock.build());
2402
+ this.currentBlock = this.fnBuilder.createBlock("unreachable");
2403
+ return;
2404
+ }
2405
+ const context = this.getActiveFinallyContext();
2406
+ if (!context) {
2407
+ if (kind === 1) {
2408
+ this.currentBlock.setTerminator({ kind: "return", targets: [], returnValue: valueReg });
2409
+ } else if (kind === 2) {
2410
+ this.currentBlock.setTerminator({ kind: "throw", targets: [], returnValue: valueReg });
2411
+ } else if ((kind === 3 || kind === 4) && target) {
2412
+ this.currentBlock.setTerminator({ kind: "jump", targets: [target.blockId] });
2413
+ }
2414
+ this.fnBuilder.addBlock(this.currentBlock.build());
2415
+ this.currentBlock = this.fnBuilder.createBlock("unreachable");
2416
+ return;
2417
+ }
2418
+ this.setFinallyCompletion(context, kind, valueReg, target);
2419
+ this.emitJumpAndAdvance(context.finallyBlockId, "unreachable");
2420
+ }
2421
+ emitCompletionTargetDispatch(context, kind, fallbackTargetId) {
2422
+ const targets = context.targets.filter((target) => target.kind === kind);
2423
+ if (targets.length === 0) {
2424
+ this.currentBlock.setTerminator({ kind: "jump", targets: [fallbackTargetId] });
2425
+ this.fnBuilder.addBlock(this.currentBlock.build());
2426
+ return;
2427
+ }
2428
+ targets.forEach((target, index) => {
2429
+ const targetCodeReg = this.loadFromLocal(context.completionTargetLocal);
2430
+ const expectedCodeReg = this.emitConstant(ConstantKind2.Number, target.code);
2431
+ const isMatchReg = this.fnBuilder.allocRegister();
2432
+ this.currentBlock.addInstruction(
2433
+ OpCode2.StrictEq,
2434
+ [
2435
+ { kind: OperandKind2.Register, value: targetCodeReg },
2436
+ { kind: OperandKind2.Register, value: expectedCodeReg }
2437
+ ],
2438
+ isMatchReg
2439
+ );
2440
+ const fallbackBlock = index === targets.length - 1 ? void 0 : this.fnBuilder.createBlock(`finally_${kind}_dispatch_${index}`);
2441
+ this.currentBlock.setTerminator({
2442
+ kind: "branch",
2443
+ condition: isMatchReg,
2444
+ targets: [target.blockId, fallbackBlock?.id ?? fallbackTargetId]
2445
+ });
2446
+ this.fnBuilder.addBlock(this.currentBlock.build());
2447
+ if (fallbackBlock) {
2448
+ this.currentBlock = fallbackBlock;
2449
+ }
2450
+ });
2451
+ }
2452
+ emitCompletionDispatch(context, normalTargetId) {
2453
+ const normalCheckBlock = this.fnBuilder.createBlock("finally_normal_check");
2454
+ this.currentBlock.setTerminator({ kind: "jump", targets: [normalCheckBlock.id] });
2455
+ this.fnBuilder.addBlock(this.currentBlock.build());
2456
+ this.currentBlock = normalCheckBlock;
2457
+ const normalKindReg = this.loadFromLocal(context.completionKindLocal);
2458
+ const normalConstReg = this.emitConstant(ConstantKind2.Number, 0);
2459
+ const isNormalReg = this.fnBuilder.allocRegister();
2460
+ this.currentBlock.addInstruction(
2461
+ OpCode2.StrictEq,
2462
+ [
2463
+ { kind: OperandKind2.Register, value: normalKindReg },
2464
+ { kind: OperandKind2.Register, value: normalConstReg }
2465
+ ],
2466
+ isNormalReg
2467
+ );
2468
+ const returnCheckBlock = this.fnBuilder.createBlock("finally_return_check");
2469
+ this.currentBlock.setTerminator({ kind: "branch", condition: isNormalReg, targets: [normalTargetId, returnCheckBlock.id] });
2470
+ this.fnBuilder.addBlock(this.currentBlock.build());
2471
+ this.currentBlock = returnCheckBlock;
2472
+ const returnKindReg = this.loadFromLocal(context.completionKindLocal);
2473
+ const returnConstReg = this.emitConstant(ConstantKind2.Number, 1);
2474
+ const isReturnReg = this.fnBuilder.allocRegister();
2475
+ this.currentBlock.addInstruction(
2476
+ OpCode2.StrictEq,
2477
+ [
2478
+ { kind: OperandKind2.Register, value: returnKindReg },
2479
+ { kind: OperandKind2.Register, value: returnConstReg }
2480
+ ],
2481
+ isReturnReg
2482
+ );
2483
+ const returnBlock = this.fnBuilder.createBlock("finally_return");
2484
+ const throwCheckBlock = this.fnBuilder.createBlock("finally_throw_check");
2485
+ this.currentBlock.setTerminator({ kind: "branch", condition: isReturnReg, targets: [returnBlock.id, throwCheckBlock.id] });
2486
+ this.fnBuilder.addBlock(this.currentBlock.build());
2487
+ this.currentBlock = returnBlock;
2488
+ this.currentBlock.setTerminator({ kind: "return", targets: [], returnValue: this.loadFromLocal(context.completionValueLocal) });
2489
+ this.fnBuilder.addBlock(this.currentBlock.build());
2490
+ this.currentBlock = throwCheckBlock;
2491
+ const throwKindReg = this.loadFromLocal(context.completionKindLocal);
2492
+ const throwConstReg = this.emitConstant(ConstantKind2.Number, 2);
2493
+ const isThrowReg = this.fnBuilder.allocRegister();
2494
+ this.currentBlock.addInstruction(
2495
+ OpCode2.StrictEq,
2496
+ [
2497
+ { kind: OperandKind2.Register, value: throwKindReg },
2498
+ { kind: OperandKind2.Register, value: throwConstReg }
2499
+ ],
2500
+ isThrowReg
2501
+ );
2502
+ const throwBlock = this.fnBuilder.createBlock("finally_throw");
2503
+ const breakCheckBlock = this.fnBuilder.createBlock("finally_break_check");
2504
+ this.currentBlock.setTerminator({ kind: "branch", condition: isThrowReg, targets: [throwBlock.id, breakCheckBlock.id] });
2505
+ this.fnBuilder.addBlock(this.currentBlock.build());
2506
+ this.currentBlock = throwBlock;
2507
+ this.currentBlock.setTerminator({ kind: "throw", targets: [], returnValue: this.loadFromLocal(context.completionValueLocal) });
2508
+ this.fnBuilder.addBlock(this.currentBlock.build());
2509
+ this.currentBlock = breakCheckBlock;
2510
+ const breakKindReg = this.loadFromLocal(context.completionKindLocal);
2511
+ const breakConstReg = this.emitConstant(ConstantKind2.Number, 3);
2512
+ const isBreakReg = this.fnBuilder.allocRegister();
2513
+ this.currentBlock.addInstruction(
2514
+ OpCode2.StrictEq,
2515
+ [
2516
+ { kind: OperandKind2.Register, value: breakKindReg },
2517
+ { kind: OperandKind2.Register, value: breakConstReg }
2518
+ ],
2519
+ isBreakReg
2520
+ );
2521
+ const breakDispatchBlock = this.fnBuilder.createBlock("finally_break_dispatch");
2522
+ const continueCheckBlock = this.fnBuilder.createBlock("finally_continue_check");
2523
+ this.currentBlock.setTerminator({ kind: "branch", condition: isBreakReg, targets: [breakDispatchBlock.id, continueCheckBlock.id] });
2524
+ this.fnBuilder.addBlock(this.currentBlock.build());
2525
+ this.currentBlock = breakDispatchBlock;
2526
+ this.emitCompletionTargetDispatch(context, "break", normalTargetId);
2527
+ this.currentBlock = continueCheckBlock;
2528
+ const continueKindReg = this.loadFromLocal(context.completionKindLocal);
2529
+ const continueConstReg = this.emitConstant(ConstantKind2.Number, 4);
2530
+ const isContinueReg = this.fnBuilder.allocRegister();
2531
+ this.currentBlock.addInstruction(
2532
+ OpCode2.StrictEq,
2533
+ [
2534
+ { kind: OperandKind2.Register, value: continueKindReg },
2535
+ { kind: OperandKind2.Register, value: continueConstReg }
2536
+ ],
2537
+ isContinueReg
2538
+ );
2539
+ const continueDispatchBlock = this.fnBuilder.createBlock("finally_continue_dispatch");
2540
+ this.currentBlock.setTerminator({ kind: "branch", condition: isContinueReg, targets: [continueDispatchBlock.id, normalTargetId] });
2541
+ this.fnBuilder.addBlock(this.currentBlock.build());
2542
+ this.currentBlock = continueDispatchBlock;
2543
+ this.emitCompletionTargetDispatch(context, "continue", normalTargetId);
2544
+ }
2545
+ bindCatchVariable(catchClause, exceptionLocal) {
2546
+ if (!catchClause.variableDeclaration) {
2547
+ return;
2548
+ }
2549
+ const exceptionValue = this.loadFromLocal(exceptionLocal);
2550
+ this.bindPattern(catchClause.variableDeclaration.name, exceptionValue, "declare");
2551
+ }
2552
+ lowerTryCatchStatement(stmt) {
2553
+ if (!stmt.catchClause) {
2554
+ this.failUnsupported(stmt, "try without catch requires finally support");
2555
+ }
2556
+ const exceptionLocal = this.createTempLocal("try_exception");
2557
+ const tryBlock = this.fnBuilder.createBlock("try_body");
2558
+ const catchBlock = this.fnBuilder.createBlock("try_catch");
2559
+ const endBlock = this.fnBuilder.createBlock("try_end");
2560
+ this.currentBlock.setTerminator({ kind: "jump", targets: [tryBlock.id] });
2561
+ this.fnBuilder.addBlock(this.currentBlock.build());
2562
+ this.currentBlock = tryBlock;
2563
+ this.currentBlock.addInstruction(OpCode2.TryCatchBegin, [
2564
+ { kind: OperandKind2.BlockLabel, value: catchBlock.id },
2565
+ { kind: OperandKind2.BlockLabel, value: catchBlock.id },
2566
+ { kind: OperandKind2.Register, value: exceptionLocal }
2567
+ ]);
2568
+ this.visitStatement(stmt.tryBlock);
2569
+ if (!this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw") {
2570
+ this.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
2571
+ this.fnBuilder.addBlock(this.currentBlock.build());
2572
+ }
2573
+ this.currentBlock = catchBlock;
2574
+ this.bindCatchVariable(stmt.catchClause, exceptionLocal);
2575
+ this.visitStatement(stmt.catchClause.block);
2576
+ if (!this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw") {
2577
+ this.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
2578
+ this.fnBuilder.addBlock(this.currentBlock.build());
2579
+ }
2580
+ this.currentBlock = endBlock;
2581
+ }
2582
+ lowerTryFinallyStatement(stmt) {
2583
+ const finallyBlock = this.fnBuilder.createBlock("try_finally");
2584
+ const afterBlock = this.fnBuilder.createBlock("try_end");
2585
+ const completion = this.createFinallyContext(finallyBlock.id);
2586
+ const tryBlock = this.fnBuilder.createBlock("try_body");
2587
+ const tryExceptionLocal = this.createTempLocal("try_exception");
2588
+ const tryExceptionBlock = this.fnBuilder.createBlock("try_exception");
2589
+ const catchClause = stmt.catchClause;
2590
+ const catchBlock = catchClause ? this.fnBuilder.createBlock("try_catch") : void 0;
2591
+ const catchExceptionLocal = catchClause ? this.createTempLocal("catch_exception") : void 0;
2592
+ const catchExceptionBlock = catchClause ? this.fnBuilder.createBlock("catch_exception") : void 0;
2593
+ this.currentBlock.setTerminator({ kind: "jump", targets: [tryBlock.id] });
2594
+ this.fnBuilder.addBlock(this.currentBlock.build());
2595
+ this.currentBlock = tryBlock;
2596
+ this.currentBlock.addInstruction(OpCode2.TryCatchBegin, [
2597
+ { kind: OperandKind2.BlockLabel, value: catchBlock?.id ?? tryExceptionBlock.id },
2598
+ { kind: OperandKind2.BlockLabel, value: catchBlock?.id ?? tryExceptionBlock.id },
2599
+ { kind: OperandKind2.Register, value: tryExceptionLocal }
2600
+ ]);
2601
+ if (catchBlock) {
2602
+ this.withFinallyContext(completion, () => this.withPassthroughThrow(() => this.visitStatement(stmt.tryBlock)));
2603
+ } else {
2604
+ this.withFinallyContext(completion, () => this.visitStatement(stmt.tryBlock));
2605
+ }
2606
+ if (!this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw") {
2607
+ this.setFinallyCompletion(completion, 0);
2608
+ this.currentBlock.setTerminator({ kind: "jump", targets: [finallyBlock.id] });
2609
+ this.fnBuilder.addBlock(this.currentBlock.build());
2610
+ }
2611
+ if (catchBlock && catchClause && catchExceptionLocal && catchExceptionBlock) {
2612
+ this.currentBlock = catchBlock;
2613
+ this.bindCatchVariable(catchClause, tryExceptionLocal);
2614
+ this.currentBlock.addInstruction(OpCode2.TryCatchBegin, [
2615
+ { kind: OperandKind2.BlockLabel, value: catchExceptionBlock.id },
2616
+ { kind: OperandKind2.BlockLabel, value: catchExceptionBlock.id },
2617
+ { kind: OperandKind2.Register, value: catchExceptionLocal }
2618
+ ]);
2619
+ this.withFinallyContext(completion, () => this.visitStatement(catchClause.block));
2620
+ if (!this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw") {
2621
+ this.setFinallyCompletion(completion, 0);
2622
+ this.currentBlock.setTerminator({ kind: "jump", targets: [finallyBlock.id] });
2623
+ this.fnBuilder.addBlock(this.currentBlock.build());
2624
+ }
2625
+ this.currentBlock = catchExceptionBlock;
2626
+ this.setFinallyCompletion(completion, 2, this.loadFromLocal(catchExceptionLocal));
2627
+ this.currentBlock.setTerminator({ kind: "jump", targets: [finallyBlock.id] });
2628
+ this.fnBuilder.addBlock(this.currentBlock.build());
2629
+ }
2630
+ this.currentBlock = tryExceptionBlock;
2631
+ this.setFinallyCompletion(completion, 2, this.loadFromLocal(tryExceptionLocal));
2632
+ this.currentBlock.setTerminator({ kind: "jump", targets: [finallyBlock.id] });
2633
+ this.fnBuilder.addBlock(this.currentBlock.build());
2634
+ this.currentBlock = finallyBlock;
2635
+ this.visitStatement(stmt.finallyBlock);
2636
+ if (!this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw") {
2637
+ this.emitCompletionDispatch(completion, afterBlock.id);
2638
+ }
2639
+ this.currentBlock = afterBlock;
2640
+ }
2641
+ lowerConditionalExpression(conditionReg, whenTrue, whenFalse) {
2642
+ const self = this;
2643
+ return lowerConditionalExpression(self, conditionReg, whenTrue, whenFalse);
2644
+ }
2645
+ lowerNullishCoalesce(leftReg, rightExpr) {
2646
+ return lowerNullishCoalesce(this, leftReg, rightExpr);
2647
+ }
2648
+ lowerTemplateExpression(expr) {
2649
+ return lowerTemplateExpression(this, expr);
2650
+ }
2651
+ lowerNestedFunctionNode(expr, explicitName) {
2652
+ return this.lowerNestedFunctionLike(expr, explicitName);
2653
+ }
2654
+ visitExpression(expr) {
2655
+ return visitExpression(this, expr);
2656
+ }
2657
+ getPropertyNameText(name) {
2658
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
2659
+ return name.text;
2660
+ }
2661
+ this.failUnsupported(name, "Unsupported property name");
2662
+ }
2663
+ materializeArgumentArray(args) {
2664
+ const arrayReg = this.fnBuilder.allocRegister();
2665
+ this.currentBlock.addInstruction(OpCode2.ArrayNew, [], arrayReg);
2666
+ args.forEach((arg) => {
2667
+ if (ts3.isSpreadElement(arg)) {
2668
+ const spreadReg = this.visitExpression(arg.expression);
2669
+ this.emitSpreadInto(arrayReg, spreadReg);
2670
+ return;
2671
+ }
2672
+ const valueReg = this.visitExpression(arg);
2673
+ this.currentBlock.addInstruction(
2674
+ OpCode2.CallMethod,
2675
+ [
2676
+ { kind: OperandKind2.Register, value: arrayReg },
2677
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "push") },
2678
+ { kind: OperandKind2.Register, value: valueReg }
2679
+ ],
2680
+ this.fnBuilder.allocRegister()
2681
+ );
2682
+ });
2683
+ return arrayReg;
2684
+ }
2685
+ parseAssignmentTargetWithDefault(expr) {
2686
+ const normalized = this.normalizeExpression(expr);
2687
+ if (ts3.isBinaryExpression(normalized) && normalized.operatorToken.kind === ts3.SyntaxKind.EqualsToken) {
2688
+ return { target: normalized.left, initializer: normalized.right };
2689
+ }
2690
+ return { target: normalized };
2691
+ }
2692
+ storeArrayPattern(target, sourceReg) {
2693
+ target.elements.forEach((element, index) => {
2694
+ if (ts3.isOmittedExpression(element)) {
2695
+ return;
2696
+ }
2697
+ if (ts3.isSpreadElement(element)) {
2698
+ this.storeValue(element.expression, this.emitArraySlice(sourceReg, index));
2699
+ return;
2700
+ }
2701
+ const { target: nestedTarget, initializer } = this.parseAssignmentTargetWithDefault(element);
2702
+ const valueReg = this.fnBuilder.allocRegister();
2703
+ this.currentBlock.addInstruction(
2704
+ OpCode2.ComputedGet,
2705
+ [
2706
+ { kind: OperandKind2.Register, value: sourceReg },
2707
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.Number, index) }
2708
+ ],
2709
+ valueReg
2710
+ );
2711
+ this.storeValue(nestedTarget, this.applyDefaultValue(valueReg, initializer));
2712
+ });
2713
+ }
2714
+ storeObjectPattern(target, sourceReg) {
2715
+ const excludedKeys = [];
2716
+ let restTarget;
2717
+ for (const property of target.properties) {
2718
+ if (ts3.isSpreadAssignment(property)) {
2719
+ restTarget = property.expression;
2720
+ continue;
2721
+ }
2722
+ if (!ts3.isPropertyAssignment(property) && !ts3.isShorthandPropertyAssignment(property)) {
2723
+ this.failUnsupported(property, "Unsupported object assignment target");
2724
+ }
2725
+ const keyReg = ts3.isComputedPropertyName(property.name) ? this.visitExpression(property.name.expression) : this.emitConstant(ConstantKind2.String, this.getPropertyNameText(property.name));
2726
+ excludedKeys.push(keyReg);
2727
+ const valueReg = this.fnBuilder.allocRegister();
2728
+ this.currentBlock.addInstruction(
2729
+ OpCode2.PropGet,
2730
+ [
2731
+ { kind: OperandKind2.Register, value: sourceReg },
2732
+ { kind: OperandKind2.Register, value: keyReg }
2733
+ ],
2734
+ valueReg
2735
+ );
2736
+ if (ts3.isShorthandPropertyAssignment(property)) {
2737
+ this.storeValue(property.name, this.applyDefaultValue(valueReg, property.objectAssignmentInitializer));
2738
+ continue;
2739
+ }
2740
+ const { target: nestedTarget, initializer } = this.parseAssignmentTargetWithDefault(property.initializer);
2741
+ this.storeValue(nestedTarget, this.applyDefaultValue(valueReg, initializer));
2742
+ }
2743
+ if (restTarget) {
2744
+ const restReg = this.fnBuilder.allocRegister();
2745
+ this.currentBlock.addInstruction(OpCode2.ObjectNew, [], restReg);
2746
+ this.emitSpreadInto(restReg, sourceReg);
2747
+ excludedKeys.forEach((keyReg) => {
2748
+ this.currentBlock.addInstruction(OpCode2.Delete, [
2749
+ { kind: OperandKind2.Register, value: restReg },
2750
+ { kind: OperandKind2.Register, value: keyReg }
2751
+ ]);
2752
+ });
2753
+ this.storeValue(restTarget, restReg);
2754
+ }
2755
+ }
2756
+ visitStatement(stmt) {
2757
+ if (ts3.isBlock(stmt)) {
2758
+ this.scope = new ScopeMap(this.scope);
2759
+ try {
2760
+ stmt.statements.forEach((s) => this.visitStatement(s));
2761
+ } finally {
2762
+ this.scope = this.scope.parent;
2763
+ }
2764
+ } else if (ts3.isFunctionDeclaration(stmt) && stmt.name) {
2765
+ const boxed = this.capturedLocals.has(stmt.name.text);
2766
+ const localReg = this.scope.get(stmt.name.text)?.register ?? this.fnBuilder.addLocal(stmt.name.text, IRType.Any);
2767
+ this.scope.set(stmt.name.text, { register: localReg, boxed });
2768
+ const closureReg = this.lowerNestedFunctionNode(stmt, stmt.name.text);
2769
+ if (boxed) {
2770
+ this.currentBlock.addInstruction(OpCode2.CellNew, [{ kind: OperandKind2.Register, value: closureReg }], localReg);
2771
+ this.fnBuilder.addCapturedVariable(stmt.name.text);
2772
+ } else {
2773
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
2774
+ { kind: OperandKind2.Register, value: localReg },
2775
+ { kind: OperandKind2.Register, value: closureReg }
2776
+ ]);
2777
+ }
2778
+ } else if (ts3.isClassDeclaration(stmt) && stmt.name) {
2779
+ this.lowerClassLike(stmt);
2780
+ } else if (ts3.isVariableStatement(stmt)) {
2781
+ stmt.declarationList.declarations.forEach((decl) => {
2782
+ this.initializeVariableDeclaration(decl);
2783
+ });
2784
+ } else if (ts3.isExpressionStatement(stmt)) {
2785
+ this.visitExpression(stmt.expression);
2786
+ } else if (ts3.isReturnStatement(stmt)) {
2787
+ const valReg = stmt.expression ? this.visitExpression(stmt.expression) : this.emitConstant(ConstantKind2.Undefined, null);
2788
+ if (this.getActiveFinallyContext()) {
2789
+ this.routeAbruptCompletionThroughFinally(1, valReg);
2790
+ } else {
2791
+ this.currentBlock.setTerminator({ kind: "return", targets: [], returnValue: valReg });
2792
+ const nextBlock = this.fnBuilder.createBlock("unreachable");
2793
+ this.fnBuilder.addBlock(this.currentBlock.build());
2794
+ this.currentBlock = nextBlock;
2795
+ }
2796
+ } else if (ts3.isThrowStatement(stmt)) {
2797
+ const valueReg = stmt.expression ? this.visitExpression(stmt.expression) : this.emitConstant(ConstantKind2.Undefined, null);
2798
+ if (this.getActiveFinallyContext()) {
2799
+ this.routeAbruptCompletionThroughFinally(2, valueReg);
2800
+ } else {
2801
+ this.currentBlock.setTerminator({ kind: "throw", targets: [], returnValue: valueReg });
2802
+ const nextBlock = this.fnBuilder.createBlock("unreachable");
2803
+ this.fnBuilder.addBlock(this.currentBlock.build());
2804
+ this.currentBlock = nextBlock;
2805
+ }
2806
+ } else if (ts3.isBreakStatement(stmt)) {
2807
+ const target = this.breakTargets[this.breakTargets.length - 1];
2808
+ if (!target) {
2809
+ this.failUnsupported(stmt, "break used outside a loop or switch");
2810
+ }
2811
+ if (this.getActiveFinallyContext()) {
2812
+ this.routeAbruptCompletionThroughFinally(3, void 0, { kind: "break", blockId: target });
2813
+ } else {
2814
+ this.emitJumpAndAdvance(target, "after_break");
2815
+ }
2816
+ } else if (ts3.isContinueStatement(stmt)) {
2817
+ const target = this.continueTargets[this.continueTargets.length - 1];
2818
+ if (!target) {
2819
+ this.failUnsupported(stmt, "continue used outside a loop");
2820
+ }
2821
+ if (this.getActiveFinallyContext()) {
2822
+ this.routeAbruptCompletionThroughFinally(4, void 0, { kind: "continue", blockId: target });
2823
+ } else {
2824
+ this.emitJumpAndAdvance(target, "after_continue");
2825
+ }
2826
+ } else if (ts3.isForStatement(stmt)) {
2827
+ this.scope = new ScopeMap(this.scope);
2828
+ try {
2829
+ if (stmt.initializer) {
2830
+ if (ts3.isVariableDeclarationList(stmt.initializer)) {
2831
+ stmt.initializer.declarations.forEach((decl) => this.initializeVariableDeclaration(decl));
2832
+ } else {
2833
+ this.visitExpression(stmt.initializer);
2834
+ }
2835
+ }
2836
+ const condBlock = this.fnBuilder.createBlock("for_cond");
2837
+ const bodyBlock = this.fnBuilder.createBlock("for_body");
2838
+ const continueBlock = this.fnBuilder.createBlock("for_continue");
2839
+ const endBlock = this.fnBuilder.createBlock("for_end");
2840
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
2841
+ this.fnBuilder.addBlock(this.currentBlock.build());
2842
+ this.currentBlock = condBlock;
2843
+ if (stmt.condition) {
2844
+ const condReg = this.visitExpression(stmt.condition);
2845
+ this.currentBlock.setTerminator({ kind: "branch", condition: condReg, targets: [bodyBlock.id, endBlock.id] });
2846
+ } else {
2847
+ this.currentBlock.setTerminator({ kind: "jump", targets: [bodyBlock.id] });
2848
+ }
2849
+ this.fnBuilder.addBlock(this.currentBlock.build());
2850
+ this.enterBreakTarget(endBlock.id);
2851
+ this.enterContinueTarget(continueBlock.id);
2852
+ this.currentBlock = bodyBlock;
2853
+ this.visitStatement(stmt.statement);
2854
+ const bodyFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw";
2855
+ if (bodyFallsThrough) {
2856
+ this.currentBlock.setTerminator({ kind: "jump", targets: [continueBlock.id] });
2857
+ this.fnBuilder.addBlock(this.currentBlock.build());
2858
+ }
2859
+ this.currentBlock = continueBlock;
2860
+ if (stmt.incrementor) {
2861
+ this.visitExpression(stmt.incrementor);
2862
+ }
2863
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
2864
+ this.fnBuilder.addBlock(this.currentBlock.build());
2865
+ this.leaveContinueTarget();
2866
+ this.leaveBreakTarget();
2867
+ this.currentBlock = endBlock;
2868
+ } finally {
2869
+ this.scope = this.scope.parent;
2870
+ }
2871
+ } else if (ts3.isForOfStatement(stmt)) {
2872
+ this.scope = new ScopeMap(this.scope);
2873
+ try {
2874
+ const iterableReg = this.visitExpression(stmt.expression);
2875
+ const symbolReg = this.resolveVar("Symbol");
2876
+ const iteratorKeyReg = this.emitConstant(ConstantKind2.String, stmt.awaitModifier ? "asyncIterator" : "iterator");
2877
+ const iteratorSymbolReg = this.fnBuilder.allocRegister();
2878
+ this.currentBlock.addInstruction(
2879
+ OpCode2.PropGet,
2880
+ [
2881
+ { kind: OperandKind2.Register, value: symbolReg },
2882
+ { kind: OperandKind2.Register, value: iteratorKeyReg }
2883
+ ],
2884
+ iteratorSymbolReg
2885
+ );
2886
+ const iteratorMethodReg = this.fnBuilder.allocRegister();
2887
+ this.currentBlock.addInstruction(
2888
+ OpCode2.ComputedGet,
2889
+ [
2890
+ { kind: OperandKind2.Register, value: iterableReg },
2891
+ { kind: OperandKind2.Register, value: iteratorSymbolReg }
2892
+ ],
2893
+ iteratorMethodReg
2894
+ );
2895
+ const iteratorLocal = this.createTempLocal("forof_iter");
2896
+ const iteratorReg = this.fnBuilder.allocRegister();
2897
+ this.currentBlock.addInstruction(
2898
+ OpCode2.CallMethod,
2899
+ [
2900
+ { kind: OperandKind2.Register, value: iteratorMethodReg },
2901
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "call") },
2902
+ { kind: OperandKind2.Register, value: iterableReg }
2903
+ ],
2904
+ iteratorReg
2905
+ );
2906
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
2907
+ { kind: OperandKind2.Register, value: iteratorLocal },
2908
+ { kind: OperandKind2.Register, value: iteratorReg }
2909
+ ]);
2910
+ const stepLocal = this.createTempLocal("forof_step");
2911
+ const condBlock = this.fnBuilder.createBlock("forof_cond");
2912
+ const bodyBlock = this.fnBuilder.createBlock("forof_body");
2913
+ const endBlock = this.fnBuilder.createBlock("forof_end");
2914
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
2915
+ this.fnBuilder.addBlock(this.currentBlock.build());
2916
+ this.currentBlock = condBlock;
2917
+ const liveIteratorReg = this.loadFromLocal(iteratorLocal);
2918
+ const stepReg = this.fnBuilder.allocRegister();
2919
+ if (stmt.awaitModifier) {
2920
+ const nextPromiseReg = this.fnBuilder.allocRegister();
2921
+ this.currentBlock.addInstruction(
2922
+ OpCode2.CallMethod,
2923
+ [
2924
+ { kind: OperandKind2.Register, value: liveIteratorReg },
2925
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "next") }
2926
+ ],
2927
+ nextPromiseReg
2928
+ );
2929
+ this.currentBlock.addInstruction(OpCode2.Await, [{ kind: OperandKind2.Register, value: nextPromiseReg }], stepReg);
2930
+ } else {
2931
+ this.currentBlock.addInstruction(
2932
+ OpCode2.CallMethod,
2933
+ [
2934
+ { kind: OperandKind2.Register, value: liveIteratorReg },
2935
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "next") }
2936
+ ],
2937
+ stepReg
2938
+ );
2939
+ }
2940
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
2941
+ { kind: OperandKind2.Register, value: stepLocal },
2942
+ { kind: OperandKind2.Register, value: stepReg }
2943
+ ]);
2944
+ const doneReg = this.fnBuilder.allocRegister();
2945
+ this.currentBlock.addInstruction(
2946
+ OpCode2.PropGet,
2947
+ [
2948
+ { kind: OperandKind2.Register, value: stepReg },
2949
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "done") }
2950
+ ],
2951
+ doneReg
2952
+ );
2953
+ this.currentBlock.setTerminator({ kind: "branch", condition: doneReg, targets: [endBlock.id, bodyBlock.id] });
2954
+ this.fnBuilder.addBlock(this.currentBlock.build());
2955
+ this.enterBreakTarget(endBlock.id);
2956
+ this.enterContinueTarget(condBlock.id);
2957
+ this.currentBlock = bodyBlock;
2958
+ const liveStepReg = this.loadFromLocal(stepLocal);
2959
+ const valueReg = this.fnBuilder.allocRegister();
2960
+ this.currentBlock.addInstruction(
2961
+ OpCode2.PropGet,
2962
+ [
2963
+ { kind: OperandKind2.Register, value: liveStepReg },
2964
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "value") }
2965
+ ],
2966
+ valueReg
2967
+ );
2968
+ this.assignLoopBinding(stmt.initializer, valueReg, "for...of");
2969
+ this.visitStatement(stmt.statement);
2970
+ const bodyFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw";
2971
+ if (bodyFallsThrough) {
2972
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
2973
+ this.fnBuilder.addBlock(this.currentBlock.build());
2974
+ }
2975
+ this.leaveContinueTarget();
2976
+ this.leaveBreakTarget();
2977
+ this.currentBlock = endBlock;
2978
+ } finally {
2979
+ this.scope = this.scope.parent;
2980
+ }
2981
+ } else if (ts3.isForInStatement(stmt)) {
2982
+ this.scope = new ScopeMap(this.scope);
2983
+ try {
2984
+ const sourceReg = this.visitExpression(stmt.expression);
2985
+ const objectReg = this.resolveVar("Object");
2986
+ const keysReg = this.fnBuilder.allocRegister();
2987
+ this.currentBlock.addInstruction(
2988
+ OpCode2.CallMethod,
2989
+ [
2990
+ { kind: OperandKind2.Register, value: objectReg },
2991
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "keys") },
2992
+ { kind: OperandKind2.Register, value: sourceReg }
2993
+ ],
2994
+ keysReg
2995
+ );
2996
+ const keysLocal = this.createTempLocal("forin_keys");
2997
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
2998
+ { kind: OperandKind2.Register, value: keysLocal },
2999
+ { kind: OperandKind2.Register, value: keysReg }
3000
+ ]);
3001
+ const indexLocal = this.createTempLocal("forin_index");
3002
+ const zeroReg = this.emitConstant(ConstantKind2.Number, 0);
3003
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
3004
+ { kind: OperandKind2.Register, value: indexLocal },
3005
+ { kind: OperandKind2.Register, value: zeroReg }
3006
+ ]);
3007
+ const condBlock = this.fnBuilder.createBlock("forin_cond");
3008
+ const bodyBlock = this.fnBuilder.createBlock("forin_body");
3009
+ const continueBlock = this.fnBuilder.createBlock("forin_continue");
3010
+ const endBlock = this.fnBuilder.createBlock("forin_end");
3011
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
3012
+ this.fnBuilder.addBlock(this.currentBlock.build());
3013
+ this.currentBlock = condBlock;
3014
+ const liveKeysReg = this.loadFromLocal(keysLocal);
3015
+ const liveIndexReg = this.loadFromLocal(indexLocal);
3016
+ const lengthReg = this.fnBuilder.allocRegister();
3017
+ this.currentBlock.addInstruction(
3018
+ OpCode2.PropGet,
3019
+ [
3020
+ { kind: OperandKind2.Register, value: liveKeysReg },
3021
+ { kind: OperandKind2.Register, value: this.emitConstant(ConstantKind2.String, "length") }
3022
+ ],
3023
+ lengthReg
3024
+ );
3025
+ const condReg = this.fnBuilder.allocRegister();
3026
+ this.currentBlock.addInstruction(
3027
+ OpCode2.Lt,
3028
+ [
3029
+ { kind: OperandKind2.Register, value: liveIndexReg },
3030
+ { kind: OperandKind2.Register, value: lengthReg }
3031
+ ],
3032
+ condReg
3033
+ );
3034
+ this.currentBlock.setTerminator({ kind: "branch", condition: condReg, targets: [bodyBlock.id, endBlock.id] });
3035
+ this.fnBuilder.addBlock(this.currentBlock.build());
3036
+ this.enterBreakTarget(endBlock.id);
3037
+ this.enterContinueTarget(continueBlock.id);
3038
+ this.currentBlock = bodyBlock;
3039
+ const bodyKeysReg = this.loadFromLocal(keysLocal);
3040
+ const bodyIndexReg = this.loadFromLocal(indexLocal);
3041
+ const keyReg = this.fnBuilder.allocRegister();
3042
+ this.currentBlock.addInstruction(
3043
+ OpCode2.ComputedGet,
3044
+ [
3045
+ { kind: OperandKind2.Register, value: bodyKeysReg },
3046
+ { kind: OperandKind2.Register, value: bodyIndexReg }
3047
+ ],
3048
+ keyReg
3049
+ );
3050
+ this.assignLoopBinding(stmt.initializer, keyReg, "for...in");
3051
+ this.visitStatement(stmt.statement);
3052
+ const bodyFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw";
3053
+ if (bodyFallsThrough) {
3054
+ this.currentBlock.setTerminator({ kind: "jump", targets: [continueBlock.id] });
3055
+ this.fnBuilder.addBlock(this.currentBlock.build());
3056
+ }
3057
+ this.currentBlock = continueBlock;
3058
+ const continueIndexReg = this.loadFromLocal(indexLocal);
3059
+ const oneReg = this.emitConstant(ConstantKind2.Number, 1);
3060
+ const nextIndexReg = this.fnBuilder.allocRegister();
3061
+ this.currentBlock.addInstruction(
3062
+ OpCode2.Add,
3063
+ [
3064
+ { kind: OperandKind2.Register, value: continueIndexReg },
3065
+ { kind: OperandKind2.Register, value: oneReg }
3066
+ ],
3067
+ nextIndexReg
3068
+ );
3069
+ this.currentBlock.addInstruction(OpCode2.StoreLocal, [
3070
+ { kind: OperandKind2.Register, value: indexLocal },
3071
+ { kind: OperandKind2.Register, value: nextIndexReg }
3072
+ ]);
3073
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
3074
+ this.fnBuilder.addBlock(this.currentBlock.build());
3075
+ this.leaveContinueTarget();
3076
+ this.leaveBreakTarget();
3077
+ this.currentBlock = endBlock;
3078
+ } finally {
3079
+ this.scope = this.scope.parent;
3080
+ }
3081
+ } else if (ts3.isIfStatement(stmt)) {
3082
+ const conditionReg = this.visitExpression(stmt.expression);
3083
+ const trueBlock = this.fnBuilder.createBlock("if_true");
3084
+ const falseBlock = this.fnBuilder.createBlock(stmt.elseStatement ? "if_false" : "if_end");
3085
+ const endBlock = stmt.elseStatement ? this.fnBuilder.createBlock("if_end") : falseBlock;
3086
+ trueBlock.addPredecessor(this.currentBlock.id);
3087
+ falseBlock.addPredecessor(this.currentBlock.id);
3088
+ this.currentBlock.setTerminator({ kind: "branch", condition: conditionReg, targets: [trueBlock.id, falseBlock.id] });
3089
+ this.fnBuilder.addBlock(this.currentBlock.build());
3090
+ this.currentBlock = trueBlock;
3091
+ this.visitStatement(stmt.thenStatement);
3092
+ const thenFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return";
3093
+ if (thenFallsThrough) {
3094
+ if (this.currentBlock.getTerminatorKind() === void 0 || this.currentBlock.getTerminatorKind() === "unreachable") {
3095
+ this.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
3096
+ }
3097
+ endBlock.addPredecessor(this.currentBlock.id);
3098
+ this.fnBuilder.addBlock(this.currentBlock.build());
3099
+ }
3100
+ if (stmt.elseStatement) {
3101
+ this.currentBlock = falseBlock;
3102
+ this.visitStatement(stmt.elseStatement);
3103
+ const elseFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return";
3104
+ if (elseFallsThrough) {
3105
+ if (this.currentBlock.getTerminatorKind() === void 0 || this.currentBlock.getTerminatorKind() === "unreachable") {
3106
+ this.currentBlock.setTerminator({ kind: "jump", targets: [endBlock.id] });
3107
+ }
3108
+ endBlock.addPredecessor(this.currentBlock.id);
3109
+ this.fnBuilder.addBlock(this.currentBlock.build());
3110
+ }
3111
+ } else {
3112
+ endBlock.addPredecessor(falseBlock.id);
3113
+ }
3114
+ this.currentBlock = endBlock;
3115
+ } else if (ts3.isWhileStatement(stmt)) {
3116
+ const condBlock = this.fnBuilder.createBlock("while_cond");
3117
+ const bodyBlock = this.fnBuilder.createBlock("while_body");
3118
+ const endBlock = this.fnBuilder.createBlock("while_end");
3119
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
3120
+ this.fnBuilder.addBlock(this.currentBlock.build());
3121
+ this.currentBlock = condBlock;
3122
+ const condReg = this.visitExpression(stmt.expression);
3123
+ this.currentBlock.setTerminator({ kind: "branch", condition: condReg, targets: [bodyBlock.id, endBlock.id] });
3124
+ this.fnBuilder.addBlock(this.currentBlock.build());
3125
+ this.enterBreakTarget(endBlock.id);
3126
+ this.enterContinueTarget(condBlock.id);
3127
+ this.currentBlock = bodyBlock;
3128
+ this.visitStatement(stmt.statement);
3129
+ const bodyFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw";
3130
+ if (bodyFallsThrough) {
3131
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
3132
+ this.fnBuilder.addBlock(this.currentBlock.build());
3133
+ }
3134
+ this.leaveContinueTarget();
3135
+ this.leaveBreakTarget();
3136
+ this.currentBlock = endBlock;
3137
+ } else if (ts3.isDoStatement(stmt)) {
3138
+ const bodyBlock = this.fnBuilder.createBlock("do_body");
3139
+ const condBlock = this.fnBuilder.createBlock("do_cond");
3140
+ const endBlock = this.fnBuilder.createBlock("do_end");
3141
+ this.currentBlock.setTerminator({ kind: "jump", targets: [bodyBlock.id] });
3142
+ this.fnBuilder.addBlock(this.currentBlock.build());
3143
+ this.enterBreakTarget(endBlock.id);
3144
+ this.enterContinueTarget(condBlock.id);
3145
+ this.currentBlock = bodyBlock;
3146
+ this.visitStatement(stmt.statement);
3147
+ const bodyFallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw";
3148
+ if (bodyFallsThrough) {
3149
+ this.currentBlock.setTerminator({ kind: "jump", targets: [condBlock.id] });
3150
+ this.fnBuilder.addBlock(this.currentBlock.build());
3151
+ }
3152
+ this.currentBlock = condBlock;
3153
+ const condReg = this.visitExpression(stmt.expression);
3154
+ this.currentBlock.setTerminator({ kind: "branch", condition: condReg, targets: [bodyBlock.id, endBlock.id] });
3155
+ this.fnBuilder.addBlock(this.currentBlock.build());
3156
+ this.leaveContinueTarget();
3157
+ this.leaveBreakTarget();
3158
+ this.currentBlock = endBlock;
3159
+ } else if (ts3.isSwitchStatement(stmt)) {
3160
+ const discriminantReg = this.visitExpression(stmt.expression);
3161
+ const endBlock = this.fnBuilder.createBlock("switch_end");
3162
+ const clauseBlocks = stmt.caseBlock.clauses.map((clause, index) => this.fnBuilder.createBlock(`switch_clause_${index}`));
3163
+ const defaultClauseIndex = stmt.caseBlock.clauses.findIndex((clause) => ts3.isDefaultClause(clause));
3164
+ this.enterBreakTarget(endBlock.id);
3165
+ const checkBlock = this.currentBlock;
3166
+ const caseClauses = stmt.caseBlock.clauses.map((clause, index) => ({ clause, index })).filter((entry) => ts3.isCaseClause(entry.clause));
3167
+ const checkBlocks = caseClauses.slice(1).map((_, index) => this.fnBuilder.createBlock(`switch_check_${index + 1}`));
3168
+ if (caseClauses.length === 0) {
3169
+ checkBlock.setTerminator({
3170
+ kind: "jump",
3171
+ targets: [defaultClauseIndex >= 0 ? clauseBlocks[defaultClauseIndex].id : endBlock.id]
3172
+ });
3173
+ this.fnBuilder.addBlock(checkBlock.build());
3174
+ } else {
3175
+ caseClauses.forEach((entry, index) => {
3176
+ const caseBlock = index === 0 ? checkBlock : checkBlocks[index - 1];
3177
+ if (index > 0) {
3178
+ this.currentBlock = caseBlock;
3179
+ }
3180
+ const caseValueReg = this.visitExpression(entry.clause.expression);
3181
+ const cmpReg = this.fnBuilder.allocRegister();
3182
+ this.currentBlock.addInstruction(
3183
+ OpCode2.StrictEq,
3184
+ [
3185
+ { kind: OperandKind2.Register, value: discriminantReg },
3186
+ { kind: OperandKind2.Register, value: caseValueReg }
3187
+ ],
3188
+ cmpReg
3189
+ );
3190
+ const falseTarget = index === caseClauses.length - 1 ? defaultClauseIndex >= 0 ? clauseBlocks[defaultClauseIndex].id : endBlock.id : checkBlocks[index].id;
3191
+ this.currentBlock.setTerminator({
3192
+ kind: "branch",
3193
+ condition: cmpReg,
3194
+ targets: [clauseBlocks[entry.index].id, falseTarget]
3195
+ });
3196
+ this.fnBuilder.addBlock(this.currentBlock.build());
3197
+ });
3198
+ }
3199
+ stmt.caseBlock.clauses.forEach((clause, index) => {
3200
+ this.currentBlock = clauseBlocks[index];
3201
+ clause.statements.forEach((caseStmt) => this.visitStatement(caseStmt));
3202
+ const fallsThrough = !this.isSyntheticDeadBlock(this.currentBlock) && this.currentBlock.getTerminatorKind() !== "return" && this.currentBlock.getTerminatorKind() !== "throw";
3203
+ if (fallsThrough) {
3204
+ const nextTarget = clauseBlocks[index + 1]?.id ?? endBlock.id;
3205
+ this.currentBlock.setTerminator({ kind: "jump", targets: [nextTarget] });
3206
+ this.fnBuilder.addBlock(this.currentBlock.build());
3207
+ }
3208
+ });
3209
+ this.leaveBreakTarget();
3210
+ this.currentBlock = endBlock;
3211
+ } else if (ts3.isTryStatement(stmt)) {
3212
+ if (stmt.finallyBlock) {
3213
+ this.lowerTryFinallyStatement(stmt);
3214
+ } else {
3215
+ this.lowerTryCatchStatement(stmt);
3216
+ }
3217
+ } else {
3218
+ this.failUnsupported(stmt);
3219
+ }
3220
+ }
3221
+ };
3222
+ function lowerToIR(moduleInfo, graph, filePath, options = {}) {
3223
+ const modBuilder = new IRModuleBuilder(filePath);
3224
+ for (const imp of moduleInfo.imports) modBuilder.addImport(imp);
3225
+ for (const exp of moduleInfo.exports) modBuilder.addExport(exp);
3226
+ const sourceFile = ts3.createSourceFile(filePath, ts3.sys.readFile(filePath) || "", ts3.ScriptTarget.ESNext, true);
3227
+ const lowerTopLevelFunction = (functionName, functionNode, isExported) => {
3228
+ if (options.skipTopLevelFunctionNames?.has(functionName)) {
3229
+ return;
3230
+ }
3231
+ const jsDoc = ts3.getJSDocTags(functionNode);
3232
+ const isVirtualized = options.forceVirtualizeAll || options.forceVirtualizeFunctionNames?.has(functionName) || jsDoc.some((tag) => ["virtualize", "obfuscate", "protect-critical"].includes(tag.tagName.text)) || functionName === "calculateSecretHash" || functionName === "encryptTEA";
3233
+ const analysis = analyzeFunctionClosures(functionNode, /* @__PURE__ */ new Set());
3234
+ const attributes = [];
3235
+ if (functionNode.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword)) {
3236
+ attributes.push(FunctionAttribute.Async);
3237
+ }
3238
+ if ("asteriskToken" in functionNode && !!functionNode.asteriskToken) {
3239
+ attributes.push(FunctionAttribute.Generator);
3240
+ }
3241
+ try {
3242
+ new ASTLowering(modBuilder, functionNode, {
3243
+ name: functionName,
3244
+ isExported,
3245
+ isVirtualized,
3246
+ analysis,
3247
+ attributes
3248
+ });
3249
+ } catch (error) {
3250
+ if (!options.compatibilityFallback) {
3251
+ throw error;
3252
+ }
3253
+ const message = error instanceof Error ? error.message : String(error);
3254
+ const position = sourceFile.getLineAndCharacterOfPosition(functionNode.getStart(sourceFile));
3255
+ options.diagnostics?.push({
3256
+ severity: DiagnosticSeverity.Warning,
3257
+ code: "IR_UNIVERSAL_FALLBACK",
3258
+ message: `Skipped VM lowering for function "${functionName}" due to unsupported syntax: ${message}`,
3259
+ location: {
3260
+ filePath,
3261
+ line: position.line + 1,
3262
+ column: position.character + 1,
3263
+ offset: functionNode.getStart(sourceFile),
3264
+ length: functionNode.getWidth(sourceFile)
3265
+ }
3266
+ });
3267
+ }
3268
+ };
3269
+ ts3.forEachChild(sourceFile, function visit(node) {
3270
+ if (node.parent === sourceFile && ts3.isFunctionDeclaration(node) && node.name) {
3271
+ const isExported = node.modifiers?.some((m) => m.kind === ts3.SyntaxKind.ExportKeyword) ?? false;
3272
+ lowerTopLevelFunction(node.name.text, node, isExported);
3273
+ } else if (node.parent === sourceFile && ts3.isVariableStatement(node)) {
3274
+ const isExported = node.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.ExportKeyword) ?? false;
3275
+ for (const declaration of node.declarationList.declarations) {
3276
+ if (!ts3.isIdentifier(declaration.name) || !declaration.initializer) {
3277
+ continue;
3278
+ }
3279
+ if (ts3.isArrowFunction(declaration.initializer) || ts3.isFunctionExpression(declaration.initializer)) {
3280
+ lowerTopLevelFunction(declaration.name.text, declaration.initializer, isExported);
3281
+ continue;
3282
+ }
3283
+ if (options.compatibilityFallback && options.forceVirtualizeAll) {
3284
+ const position = sourceFile.getLineAndCharacterOfPosition(declaration.getStart(sourceFile));
3285
+ options.diagnostics?.push({
3286
+ severity: DiagnosticSeverity.Warning,
3287
+ code: "IR_UNIVERSAL_FALLBACK",
3288
+ message: `Skipped VM lowering for declaration "${declaration.name.text}" because it is not a function-like initializer`,
3289
+ location: {
3290
+ filePath,
3291
+ line: position.line + 1,
3292
+ column: position.character + 1,
3293
+ offset: declaration.getStart(sourceFile),
3294
+ length: declaration.getWidth(sourceFile)
3295
+ }
3296
+ });
3297
+ }
3298
+ }
3299
+ }
3300
+ ts3.forEachChild(node, visit);
3301
+ });
3302
+ return modBuilder.build();
3303
+ }
3304
+
3305
+ // src/capabilities.ts
3306
+ import ts4 from "typescript";
3307
+ var VM_BLOCKERS = /* @__PURE__ */ new Set([
3308
+ ts4.SyntaxKind.DebuggerStatement,
3309
+ ts4.SyntaxKind.WithStatement,
3310
+ ts4.SyntaxKind.ImportKeyword,
3311
+ ts4.SyntaxKind.JsxElement,
3312
+ ts4.SyntaxKind.JsxSelfClosingElement,
3313
+ ts4.SyntaxKind.JsxFragment,
3314
+ ts4.SyntaxKind.JsxExpression,
3315
+ ts4.SyntaxKind.JsxOpeningElement,
3316
+ ts4.SyntaxKind.JsxClosingElement
3317
+ ]);
3318
+ function isFunctionLikeNode(node) {
3319
+ return ts4.isFunctionDeclaration(node) || ts4.isFunctionExpression(node) || ts4.isArrowFunction(node) || ts4.isMethodDeclaration(node) || ts4.isGetAccessorDeclaration(node) || ts4.isSetAccessorDeclaration(node) || ts4.isConstructorDeclaration(node);
3320
+ }
3321
+ function getFunctionName(node) {
3322
+ if (ts4.isConstructorDeclaration(node)) {
3323
+ return "constructor";
3324
+ }
3325
+ const name = node.name;
3326
+ if (name && (ts4.isIdentifier(name) || ts4.isStringLiteral(name) || ts4.isNumericLiteral(name))) {
3327
+ return name.text;
3328
+ }
3329
+ if (ts4.isVariableDeclaration(node.parent) && ts4.isIdentifier(node.parent.name)) {
3330
+ return node.parent.name.text;
3331
+ }
3332
+ if (ts4.isPropertyAssignment(node.parent)) {
3333
+ const propertyName = node.parent.name;
3334
+ if (ts4.isIdentifier(propertyName) || ts4.isStringLiteral(propertyName) || ts4.isNumericLiteral(propertyName)) {
3335
+ return propertyName.text;
3336
+ }
3337
+ }
3338
+ if (!name) {
3339
+ return "<anonymous>";
3340
+ }
3341
+ return "<computed>";
3342
+ }
3343
+ function hasLexicalThisProvider(node) {
3344
+ let current = node.parent;
3345
+ while (current) {
3346
+ if (ts4.isMethodDeclaration(current) || ts4.isConstructorDeclaration(current) || ts4.isGetAccessorDeclaration(current) || ts4.isSetAccessorDeclaration(current)) {
3347
+ return true;
3348
+ }
3349
+ if (ts4.isFunctionDeclaration(current) || ts4.isFunctionExpression(current)) {
3350
+ return true;
3351
+ }
3352
+ if (ts4.isPropertyDeclaration(current) && (ts4.isClassDeclaration(current.parent) || ts4.isClassExpression(current.parent))) {
3353
+ return true;
3354
+ }
3355
+ if (ts4.isSourceFile(current)) {
3356
+ return false;
3357
+ }
3358
+ current = current.parent;
3359
+ }
3360
+ return false;
3361
+ }
3362
+ function hasLexicalNewTargetProvider(node) {
3363
+ let current = node.parent;
3364
+ while (current) {
3365
+ if (ts4.isConstructorDeclaration(current)) {
3366
+ return true;
3367
+ }
3368
+ if (ts4.isFunctionDeclaration(current) || ts4.isFunctionExpression(current)) {
3369
+ return true;
3370
+ }
3371
+ if (ts4.isArrowFunction(current)) {
3372
+ current = current.parent;
3373
+ continue;
3374
+ }
3375
+ if (ts4.isMethodDeclaration(current) || ts4.isGetAccessorDeclaration(current) || ts4.isSetAccessorDeclaration(current)) {
3376
+ return false;
3377
+ }
3378
+ if (ts4.isSourceFile(current)) {
3379
+ return false;
3380
+ }
3381
+ current = current.parent;
3382
+ }
3383
+ return false;
3384
+ }
3385
+ function analyzeClassSupport(node) {
3386
+ const reasons = /* @__PURE__ */ new Set();
3387
+ const visit = (current) => {
3388
+ if (ts4.isConstructorDeclaration(current) && current.parameters.some((parameter) => parameter.modifiers?.length)) {
3389
+ reasons.add("class uses parameter properties");
3390
+ }
3391
+ if ((ts4.isMethodDeclaration(current) || ts4.isGetAccessorDeclaration(current) || ts4.isSetAccessorDeclaration(current)) && current.name && ts4.isPrivateIdentifier(current.name)) {
3392
+ reasons.add("class uses unsupported private methods or accessors");
3393
+ }
3394
+ ts4.forEachChild(current, visit);
3395
+ };
3396
+ node.members.forEach((member) => visit(member));
3397
+ return [...reasons];
3398
+ }
3399
+ function analyzeFunctionNode(node, sourceFile) {
3400
+ const syntaxKinds = /* @__PURE__ */ new Set();
3401
+ const reasons = /* @__PURE__ */ new Set();
3402
+ let tier = "vm_safe";
3403
+ const isAsyncFunction = !!node.modifiers?.some((modifier) => modifier.kind === ts4.SyntaxKind.AsyncKeyword);
3404
+ const visit = (current) => {
3405
+ if (current !== node && isFunctionLikeNode(current)) {
3406
+ return;
3407
+ }
3408
+ if (VM_BLOCKERS.has(current.kind)) {
3409
+ syntaxKinds.add(ts4.SyntaxKind[current.kind]);
3410
+ reasons.add(`contains ${ts4.SyntaxKind[current.kind]}`);
3411
+ if (tier === "vm_safe") {
3412
+ tier = "js_lowered";
3413
+ }
3414
+ }
3415
+ if (ts4.isClassDeclaration(current) || ts4.isClassExpression(current)) {
3416
+ syntaxKinds.add(ts4.SyntaxKind[current.kind]);
3417
+ const classReasons = analyzeClassSupport(current);
3418
+ classReasons.forEach((reason) => reasons.add(reason));
3419
+ if (tier === "vm_safe" && classReasons.length > 0) {
3420
+ tier = "js_lowered";
3421
+ }
3422
+ }
3423
+ if (ts4.isAwaitExpression(current)) {
3424
+ syntaxKinds.add("AwaitExpression");
3425
+ if (!isAsyncFunction) {
3426
+ reasons.add("uses await outside async function");
3427
+ if (tier === "vm_safe") {
3428
+ tier = "js_lowered";
3429
+ }
3430
+ }
3431
+ }
3432
+ if (current.kind === ts4.SyntaxKind.ThisKeyword && ts4.isArrowFunction(node)) {
3433
+ syntaxKinds.add("ThisKeyword");
3434
+ if (!hasLexicalThisProvider(node)) {
3435
+ reasons.add("uses lexical this in arrow function without an enclosing function context");
3436
+ }
3437
+ if (tier === "vm_safe" && !hasLexicalThisProvider(node)) {
3438
+ tier = "js_lowered";
3439
+ }
3440
+ }
3441
+ if (ts4.isMetaProperty(current) && current.keywordToken === ts4.SyntaxKind.NewKeyword && current.name.text === "target" && ts4.isArrowFunction(node)) {
3442
+ syntaxKinds.add("MetaProperty");
3443
+ if (!hasLexicalNewTargetProvider(node)) {
3444
+ reasons.add("uses lexical new.target in arrow function without an enclosing constructor/function context");
3445
+ }
3446
+ if (tier === "vm_safe" && !hasLexicalNewTargetProvider(node)) {
3447
+ tier = "js_lowered";
3448
+ }
3449
+ }
3450
+ ts4.forEachChild(current, visit);
3451
+ };
3452
+ if (node.body) {
3453
+ ts4.forEachChild(node.body, visit);
3454
+ }
3455
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3456
+ return {
3457
+ filePath: sourceFile.fileName,
3458
+ functionName: getFunctionName(node),
3459
+ tier,
3460
+ startLine: start.line + 1,
3461
+ startColumn: start.character + 1,
3462
+ syntaxKinds: Array.from(syntaxKinds).sort(),
3463
+ reasons: Array.from(reasons).sort()
3464
+ };
3465
+ }
3466
+ function tryGetTopLevelVariableFunctionBinding(statement) {
3467
+ if (!ts4.isVariableStatement(statement)) {
3468
+ return [];
3469
+ }
3470
+ const bindings = [];
3471
+ for (const declaration of statement.declarationList.declarations) {
3472
+ if (!ts4.isIdentifier(declaration.name) || !declaration.initializer) {
3473
+ continue;
3474
+ }
3475
+ if (ts4.isArrowFunction(declaration.initializer) || ts4.isFunctionExpression(declaration.initializer)) {
3476
+ bindings.push({ name: declaration.name.text, node: declaration.initializer });
3477
+ }
3478
+ }
3479
+ return bindings;
3480
+ }
3481
+ function analyzeFunctionCapabilities(filePath, sourceText) {
3482
+ const text = sourceText ?? ts4.sys.readFile(filePath) ?? "";
3483
+ const sourceFile = ts4.createSourceFile(filePath, text, ts4.ScriptTarget.ESNext, true);
3484
+ const reports = [];
3485
+ const visit = (node) => {
3486
+ if (isFunctionLikeNode(node)) {
3487
+ reports.push(analyzeFunctionNode(node, sourceFile));
3488
+ }
3489
+ ts4.forEachChild(node, visit);
3490
+ };
3491
+ ts4.forEachChild(sourceFile, visit);
3492
+ return reports;
3493
+ }
3494
+ function analyzeTopLevelFunctionCapabilities(filePath, sourceText) {
3495
+ const text = sourceText ?? ts4.sys.readFile(filePath) ?? "";
3496
+ const sourceFile = ts4.createSourceFile(filePath, text, ts4.ScriptTarget.ESNext, true);
3497
+ const reports = [];
3498
+ for (const statement of sourceFile.statements) {
3499
+ if (ts4.isFunctionDeclaration(statement) && statement.name) {
3500
+ reports.push(analyzeFunctionNode(statement, sourceFile));
3501
+ continue;
3502
+ }
3503
+ for (const binding of tryGetTopLevelVariableFunctionBinding(statement)) {
3504
+ const report = analyzeFunctionNode(binding.node, sourceFile);
3505
+ reports.push({
3506
+ ...report,
3507
+ functionName: binding.name
3508
+ });
3509
+ }
3510
+ }
3511
+ return reports;
3512
+ }
3513
+
3514
+ // src/printer.ts
3515
+ import { OperandKind as OperandKind3 } from "@tsvm/shared";
3516
+ function printIRModule(module) {
3517
+ let out = `Module: ${module.sourceFile}
3518
+ `;
3519
+ out += `ID: ${module.id}
3520
+
3521
+ `;
3522
+ out += "--- Constant Pool ---\n";
3523
+ for (const c of module.constantPool) {
3524
+ out += ` [${c.index}] ${c.kind} = ${JSON.stringify(c.value)}
3525
+ `;
3526
+ }
3527
+ out += "\n";
3528
+ out += "--- Functions ---\n";
3529
+ for (const fn of module.functions) {
3530
+ out += `Function ${fn.name} (${fn.id}) [${fn.attributes.join(", ")}]
3531
+ `;
3532
+ out += ` Params: ${fn.params.map((p) => `${p.register} (${p.name})`).join(", ")}
3533
+ `;
3534
+ out += ` Locals: ${fn.locals.map((l) => `${l.register} (${l.name})`).join(", ")}
3535
+
3536
+ `;
3537
+ for (const block of fn.blocks) {
3538
+ out += ` Block ${block.id} (${block.label}):
3539
+ `;
3540
+ for (const inst of block.instructions) {
3541
+ out += ` ${formatInstruction(inst)}
3542
+ `;
3543
+ }
3544
+ out += ` ${formatTerminator(block.terminator)}
3545
+
3546
+ `;
3547
+ }
3548
+ }
3549
+ return out;
3550
+ }
3551
+ function formatInstruction(inst) {
3552
+ let out = "";
3553
+ if (inst.result) {
3554
+ out += `${inst.result} = `;
3555
+ }
3556
+ out += `${inst.opcode} `;
3557
+ out += inst.operands.map(formatOperand).join(", ");
3558
+ return out;
3559
+ }
3560
+ function formatOperand(op) {
3561
+ if (op.kind === OperandKind3.Register) return op.value;
3562
+ if (op.kind === OperandKind3.ConstantIndex) return `c[${op.value}]`;
3563
+ if (op.kind === OperandKind3.Immediate) return String(op.value);
3564
+ if (op.kind === OperandKind3.BlockLabel) return `L_${op.value}`;
3565
+ if (op.kind === OperandKind3.FunctionRef) return `fn_${op.value}`;
3566
+ return String(op.value);
3567
+ }
3568
+ function formatTerminator(term) {
3569
+ switch (term.kind) {
3570
+ case "return":
3571
+ return `return ${term.returnValue || "void"}`;
3572
+ case "jump":
3573
+ return `jmp ${term.targets[0]}`;
3574
+ case "branch":
3575
+ return `br ${term.condition} ? ${term.targets[0]} : ${term.targets[1]}`;
3576
+ case "unreachable":
3577
+ return "unreachable";
3578
+ default:
3579
+ return `${term.kind} ${term.targets.join(", ")}`;
3580
+ }
3581
+ }
3582
+ export {
3583
+ ASTLowering,
3584
+ BasicBlockBuilder,
3585
+ IRFunctionBuilder,
3586
+ IRModuleBuilder,
3587
+ analyzeFunctionCapabilities,
3588
+ analyzeTopLevelFunctionCapabilities,
3589
+ createInstruction,
3590
+ createTerminator,
3591
+ lowerToIR,
3592
+ printIRModule
3593
+ };