@tsvm/transforms 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 +130 -0
  2. package/dist/index.js +2854 -0
  3. package/package.json +35 -0
package/dist/index.js ADDED
@@ -0,0 +1,2854 @@
1
+ // src/passes/preserve-type-illusions.ts
2
+ import { OpCode, IRType, OperandKind as OperandKind2, ConstantKind } from "@tsvm/shared";
3
+
4
+ // src/utils.ts
5
+ import { OperandKind } from "@tsvm/shared";
6
+ function getMaxRegister(func) {
7
+ let maxReg = -1;
8
+ const consider = (value) => {
9
+ if (!value) return;
10
+ const m = /^r(\d+)$/.exec(value);
11
+ if (m) {
12
+ maxReg = Math.max(maxReg, Number.parseInt(m[1], 10));
13
+ }
14
+ };
15
+ for (const param of func.params) consider(param.register);
16
+ for (const local of func.locals) consider(local.register);
17
+ for (const block of func.blocks) {
18
+ for (const phi of block.phiNodes ?? []) {
19
+ consider(phi.result);
20
+ for (const incoming of phi.incoming) consider(incoming.register);
21
+ }
22
+ for (const inst of block.instructions) {
23
+ consider(inst.result);
24
+ for (const op of inst.operands) {
25
+ if (op.kind === OperandKind.Register) consider(op.value);
26
+ }
27
+ }
28
+ consider(block.terminator.condition);
29
+ consider(block.terminator.returnValue);
30
+ }
31
+ return maxReg + 1;
32
+ }
33
+
34
+ // src/passes/preserve-type-illusions.ts
35
+ var PreserveTypeIllusionsPass = class {
36
+ name = "PreserveTypeIllusionsPass";
37
+ priority = 10;
38
+ execute(ctx) {
39
+ let nodesTransformed = 0;
40
+ const newCP = [...ctx.module.constantPool];
41
+ const newFunctions = ctx.module.functions.map((func) => {
42
+ if (!func.isVirtualized) return func;
43
+ if (func.params.length === 0) return func;
44
+ if (!func.blocks || func.blocks.length === 0) return func;
45
+ if (ctx.rng.nextFloat() >= 0.3) {
46
+ return func;
47
+ }
48
+ nodesTransformed++;
49
+ const nextReg = getMaxRegister(func);
50
+ const tempReg1 = `r${nextReg}`;
51
+ const tempReg2 = `r${nextReg + 1}`;
52
+ const tempReg3 = `r${nextReg + 2}`;
53
+ let functionConstIdx = newCP.findIndex((cp) => cp.kind === ConstantKind.String && cp.value === "function");
54
+ if (functionConstIdx === -1) {
55
+ functionConstIdx = newCP.length;
56
+ newCP.push({ index: functionConstIdx, kind: ConstantKind.String, value: "function" });
57
+ }
58
+ const paramReg = func.params[0].register;
59
+ const newInsts = [
60
+ {
61
+ opcode: OpCode.TypeOf,
62
+ operands: [{ kind: OperandKind2.Register, value: paramReg }],
63
+ result: tempReg1
64
+ },
65
+ {
66
+ opcode: OpCode.LoadConst,
67
+ operands: [{ kind: OperandKind2.ConstantIndex, value: functionConstIdx }],
68
+ result: tempReg2
69
+ },
70
+ {
71
+ opcode: OpCode.Eq,
72
+ operands: [
73
+ { kind: OperandKind2.Register, value: tempReg1 },
74
+ { kind: OperandKind2.Register, value: tempReg2 }
75
+ ],
76
+ result: tempReg3
77
+ }
78
+ ];
79
+ const newBlocks = func.blocks.map((block, idx) => {
80
+ if (idx === 0) {
81
+ return {
82
+ ...block,
83
+ instructions: [...newInsts, ...block.instructions]
84
+ };
85
+ }
86
+ return block;
87
+ });
88
+ const addedLocals = [
89
+ {
90
+ name: `type_illusion_temp_${tempReg1}`,
91
+ register: tempReg1,
92
+ type: IRType.String,
93
+ isCaptured: false
94
+ },
95
+ {
96
+ name: `type_illusion_temp_${tempReg2}`,
97
+ register: tempReg2,
98
+ type: IRType.String,
99
+ isCaptured: false
100
+ },
101
+ {
102
+ name: `type_illusion_temp_${tempReg3}`,
103
+ register: tempReg3,
104
+ type: IRType.Boolean,
105
+ isCaptured: false
106
+ }
107
+ ];
108
+ return {
109
+ ...func,
110
+ locals: [...func.locals, ...addedLocals],
111
+ blocks: newBlocks
112
+ };
113
+ });
114
+ return {
115
+ module: { ...ctx.module, functions: newFunctions, constantPool: newCP },
116
+ symbolsRenamed: 0,
117
+ nodesTransformed,
118
+ diagnostics: []
119
+ };
120
+ }
121
+ };
122
+
123
+ // src/passes/generic-confusion.ts
124
+ import { OpCode as OpCode2, OperandKind as OperandKind3, IRType as IRType2 } from "@tsvm/shared";
125
+ var GenericConfusionPass = class {
126
+ name = "GenericConfusionPass";
127
+ priority = 20;
128
+ execute(ctx) {
129
+ let nodesTransformed = 0;
130
+ const newFunctions = ctx.module.functions.map((func) => {
131
+ let changed = false;
132
+ const nextReg = getMaxRegister(func);
133
+ let tempIndex = 0;
134
+ const addedLocals = [];
135
+ const newBlocks = func.blocks.map((block) => {
136
+ const newInstructions = [];
137
+ for (const inst of block.instructions) {
138
+ if (inst.opcode === OpCode2.Call || inst.opcode === OpCode2.CallMethod) {
139
+ if (ctx.rng.nextFloat() < 0.1) {
140
+ changed = true;
141
+ nodesTransformed++;
142
+ const tempReg1 = `r${nextReg + tempIndex++}`;
143
+ const tempReg2 = `r${nextReg + tempIndex++}`;
144
+ const tempReg3 = `r${nextReg + tempIndex++}`;
145
+ addedLocals.push(
146
+ { name: `generic_conf_temp_${tempReg1}`, register: tempReg1, type: IRType2.Any, isCaptured: false },
147
+ { name: `generic_conf_temp_${tempReg2}`, register: tempReg2, type: IRType2.String, isCaptured: false },
148
+ { name: `generic_conf_temp_${tempReg3}`, register: tempReg3, type: IRType2.Boolean, isCaptured: false }
149
+ );
150
+ newInstructions.push({
151
+ opcode: OpCode2.Move,
152
+ operands: [inst.operands[0], { kind: OperandKind3.Register, value: tempReg1 }],
153
+ metadata: { genericConfusion: true }
154
+ });
155
+ newInstructions.push({
156
+ opcode: OpCode2.TypeOf,
157
+ operands: [
158
+ { kind: OperandKind3.Register, value: tempReg1 },
159
+ { kind: OperandKind3.Register, value: tempReg2 }
160
+ ]
161
+ });
162
+ newInstructions.push({
163
+ opcode: OpCode2.Eq,
164
+ operands: [
165
+ { kind: OperandKind3.Register, value: tempReg1 },
166
+ { kind: OperandKind3.Register, value: tempReg2 }
167
+ ],
168
+ result: tempReg3
169
+ });
170
+ newInstructions.push(inst);
171
+ } else {
172
+ newInstructions.push(inst);
173
+ }
174
+ } else {
175
+ newInstructions.push(inst);
176
+ }
177
+ }
178
+ return changed ? { ...block, instructions: newInstructions } : block;
179
+ });
180
+ return changed ? {
181
+ ...func,
182
+ locals: [...func.locals, ...addedLocals],
183
+ blocks: newBlocks
184
+ } : func;
185
+ });
186
+ const newModule = {
187
+ ...ctx.module,
188
+ functions: newFunctions
189
+ };
190
+ return {
191
+ module: newModule,
192
+ symbolsRenamed: 0,
193
+ nodesTransformed,
194
+ diagnostics: []
195
+ };
196
+ }
197
+ };
198
+
199
+ // src/passes/decorator-aware-lowering.ts
200
+ import { OpCode as OpCode3, OperandKind as OperandKind4, IRType as IRType3 } from "@tsvm/shared";
201
+ var DecoratorAwareLoweringPass = class {
202
+ name = "DecoratorAwareLoweringPass";
203
+ priority = 30;
204
+ execute(ctx) {
205
+ let nodesTransformed = 0;
206
+ const newFunctions = ctx.module.functions.map((func) => {
207
+ if (!func.isVirtualized) return func;
208
+ const nextReg = getMaxRegister(func);
209
+ let tempIndex = 0;
210
+ const addedLocals = [];
211
+ const newBlocks = func.blocks.map((block) => {
212
+ const newInsts = block.instructions.flatMap((inst) => {
213
+ if ((inst.opcode === OpCode3.Call || inst.opcode === OpCode3.CallMethod) && inst.operands.length > 0) {
214
+ const firstOp = inst.operands[0];
215
+ if (firstOp.kind === OperandKind4.Register && ctx.rng.nextFloat() < 0.15) {
216
+ const tempReg = `r${nextReg + tempIndex}`;
217
+ tempIndex++;
218
+ nodesTransformed++;
219
+ addedLocals.push({
220
+ name: `decorator_temp_${tempReg}`,
221
+ register: tempReg,
222
+ type: IRType3.Any,
223
+ isCaptured: false
224
+ });
225
+ const moveInst = {
226
+ opcode: OpCode3.Move,
227
+ operands: [
228
+ { kind: OperandKind4.Register, value: firstOp.value },
229
+ { kind: OperandKind4.Register, value: tempReg }
230
+ ]
231
+ };
232
+ const updatedInst = {
233
+ ...inst,
234
+ operands: [{ kind: OperandKind4.Register, value: tempReg }, ...inst.operands.slice(1)]
235
+ };
236
+ return [moveInst, updatedInst];
237
+ }
238
+ }
239
+ return [inst];
240
+ });
241
+ return {
242
+ ...block,
243
+ instructions: newInsts
244
+ };
245
+ });
246
+ return {
247
+ ...func,
248
+ locals: [...func.locals, ...addedLocals],
249
+ blocks: newBlocks
250
+ };
251
+ });
252
+ return {
253
+ module: { ...ctx.module, functions: newFunctions },
254
+ symbolsRenamed: 0,
255
+ nodesTransformed,
256
+ diagnostics: []
257
+ };
258
+ }
259
+ };
260
+
261
+ // src/passes/namespace-virtualization.ts
262
+ import { OpCode as OpCode4, ConstantKind as ConstantKind3, OperandKind as OperandKind5, IRType as IRType4, FunctionAttribute } from "@tsvm/shared";
263
+ var NamespaceVirtualizationPass = class {
264
+ name = "NamespaceVirtualizationPass";
265
+ priority = 40;
266
+ execute(ctx) {
267
+ let nodesTransformed = 0;
268
+ const constantPool = [...ctx.module.constantPool];
269
+ function isThisRegister(func, reg) {
270
+ for (const block of func.blocks) {
271
+ for (const inst of block.instructions) {
272
+ if (inst.result === reg && inst.opcode === OpCode4.LoadThis) {
273
+ return true;
274
+ }
275
+ }
276
+ }
277
+ return false;
278
+ }
279
+ function getPropertyNameOfRegister(func, reg, cp) {
280
+ for (const block of func.blocks) {
281
+ for (const inst of block.instructions) {
282
+ if (inst.result === reg && inst.opcode === OpCode4.LoadConst) {
283
+ const op = inst.operands[0];
284
+ if (op && op.kind === OperandKind5.ConstantIndex) {
285
+ const entry = cp[op.value];
286
+ if (entry && entry.kind === ConstantKind3.String) {
287
+ return entry.value;
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ return void 0;
294
+ }
295
+ const standardBuiltins = /* @__PURE__ */ new Set([
296
+ "prototype",
297
+ "constructor",
298
+ "length",
299
+ "name",
300
+ // Array & Object methods
301
+ "push",
302
+ "pop",
303
+ "shift",
304
+ "unshift",
305
+ "splice",
306
+ "slice",
307
+ "concat",
308
+ "join",
309
+ "forEach",
310
+ "map",
311
+ "filter",
312
+ "reduce",
313
+ "indexOf",
314
+ "includes",
315
+ "find",
316
+ "findIndex",
317
+ "keys",
318
+ "values",
319
+ "entries",
320
+ "toString",
321
+ "valueOf",
322
+ "toLocaleString",
323
+ "hasOwnProperty",
324
+ "isPrototypeOf",
325
+ "propertyIsEnumerable",
326
+ "apply",
327
+ "call",
328
+ "bind",
329
+ // Promise & Async / Iterator
330
+ "then",
331
+ "catch",
332
+ "finally",
333
+ "resolve",
334
+ "reject",
335
+ "next",
336
+ "throw",
337
+ "return",
338
+ "value",
339
+ "done",
340
+ // Error standard properties
341
+ "message",
342
+ "stack",
343
+ "cause",
344
+ // Console & System
345
+ "log",
346
+ "error",
347
+ "warn",
348
+ "info",
349
+ "dir",
350
+ "clear",
351
+ // NodeJS/Browser standard & VM
352
+ "exports",
353
+ "module",
354
+ "require",
355
+ "global",
356
+ "window",
357
+ "document",
358
+ "process",
359
+ "readFileSync",
360
+ "writeFileSync",
361
+ "readdirSync",
362
+ "statSync",
363
+ "mtime",
364
+ "getTime",
365
+ "exec",
366
+ "test",
367
+ "match",
368
+ "replace",
369
+ "split",
370
+ "trim",
371
+ "toLowerCase",
372
+ "toUpperCase",
373
+ // Symbol properties or other standard ones
374
+ "Symbol",
375
+ "iterator",
376
+ "asyncIterator",
377
+ "toStringTag",
378
+ // Reflect and Object proxy trap builtins
379
+ "setPrototypeOf",
380
+ "getPrototypeOf",
381
+ "defineProperty",
382
+ "defineProperties",
383
+ "getOwnPropertyDescriptor",
384
+ "getOwnPropertyNames",
385
+ "getOwnPropertySymbols",
386
+ "create",
387
+ "assign",
388
+ "freeze",
389
+ "seal",
390
+ "preventExtensions",
391
+ "isExtensible",
392
+ "isFrozen",
393
+ "isSealed",
394
+ "construct",
395
+ "has",
396
+ "get",
397
+ "set",
398
+ "deleteProperty",
399
+ "ownKeys"
400
+ ]);
401
+ function hashString(str, seed) {
402
+ let hash = (seed ^ 2166136261) >>> 0;
403
+ const prime = 16777619;
404
+ for (let i = 0; i < str.length; i++) {
405
+ hash ^= str.charCodeAt(i);
406
+ hash = Math.imul(hash, prime) >>> 0;
407
+ }
408
+ const folded = hash >>> 16 ^ hash & 65535 ^ seed >>> 8 & 65535;
409
+ return folded.toString(16).padStart(4, "0") + ((hash ^ seed) >>> 0).toString(16);
410
+ }
411
+ const newFunctions = ctx.module.functions.map((func) => {
412
+ let changed = false;
413
+ const nextReg = getMaxRegister(func);
414
+ let tempIndex = 0;
415
+ const addedLocals = [];
416
+ const paramRegs = /* @__PURE__ */ new Set();
417
+ const paramLocals = /* @__PURE__ */ new Set();
418
+ for (const p of func.params) {
419
+ paramRegs.add(p.register);
420
+ }
421
+ let sizeChanged = true;
422
+ while (sizeChanged) {
423
+ const oldRegSize = paramRegs.size;
424
+ const oldLocalSize = paramLocals.size;
425
+ for (const block of func.blocks) {
426
+ for (const inst of block.instructions) {
427
+ if (inst.opcode === OpCode4.Move) {
428
+ const srcOp = inst.operands[0];
429
+ const destOp = inst.operands[1];
430
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && paramRegs.has(srcOp.value)) {
431
+ if (destOp && destOp.kind === OperandKind5.Register && typeof destOp.value === "string") {
432
+ paramRegs.add(destOp.value);
433
+ }
434
+ }
435
+ } else if (inst.opcode === OpCode4.StoreLocal) {
436
+ const localOp = inst.operands[0];
437
+ const srcOp = inst.operands[1];
438
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && paramRegs.has(srcOp.value)) {
439
+ if (localOp && localOp.kind === OperandKind5.Register && typeof localOp.value === "string") {
440
+ paramLocals.add(localOp.value);
441
+ }
442
+ }
443
+ } else if (inst.opcode === OpCode4.LoadLocal && inst.result) {
444
+ const localOp = inst.operands[0];
445
+ if (localOp && localOp.kind === OperandKind5.Register && typeof localOp.value === "string") {
446
+ if (paramLocals.has(localOp.value) || paramRegs.has(localOp.value)) {
447
+ paramRegs.add(inst.result);
448
+ }
449
+ }
450
+ } else if (inst.result) {
451
+ const srcOp = inst.operands[0];
452
+ const isSrcReg = srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string";
453
+ if ((inst.opcode === OpCode4.PropGet || inst.opcode === OpCode4.ComputedGet) && isSrcReg && paramRegs.has(srcOp.value)) {
454
+ paramRegs.add(inst.result);
455
+ } else if (inst.opcode === OpCode4.Call || inst.opcode === OpCode4.CallMethod || inst.opcode === OpCode4.CallWithArray || inst.opcode === OpCode4.CallMethodWithArray) {
456
+ paramRegs.add(inst.result);
457
+ } else if (inst.opcode === OpCode4.RestArgs) {
458
+ paramRegs.add(inst.result);
459
+ }
460
+ }
461
+ }
462
+ }
463
+ sizeChanged = paramRegs.size !== oldRegSize || paramLocals.size !== oldLocalSize;
464
+ }
465
+ const lexicalThisRegs = /* @__PURE__ */ new Set();
466
+ const lexicalThisLocals = /* @__PURE__ */ new Set();
467
+ for (const block of func.blocks) {
468
+ for (const inst of block.instructions) {
469
+ if (inst.opcode === OpCode4.EnvGet && inst.result) {
470
+ const op = inst.operands[0];
471
+ if (op && op.kind === OperandKind5.Immediate && typeof op.value === "number") {
472
+ const capVar = func.capturedVariables[op.value];
473
+ if (capVar === "$$vm_lexical_this") {
474
+ lexicalThisRegs.add(inst.result);
475
+ }
476
+ }
477
+ }
478
+ }
479
+ }
480
+ let lexicalSizeChanged = true;
481
+ while (lexicalSizeChanged) {
482
+ const oldRegSize = lexicalThisRegs.size;
483
+ const oldLocalSize = lexicalThisLocals.size;
484
+ for (const block of func.blocks) {
485
+ for (const inst of block.instructions) {
486
+ if (inst.opcode === OpCode4.Move) {
487
+ const srcOp = inst.operands[0];
488
+ const destOp = inst.operands[1];
489
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && lexicalThisRegs.has(srcOp.value)) {
490
+ if (destOp && destOp.kind === OperandKind5.Register && typeof destOp.value === "string") {
491
+ lexicalThisRegs.add(destOp.value);
492
+ }
493
+ }
494
+ } else if (inst.opcode === OpCode4.StoreLocal) {
495
+ const localOp = inst.operands[0];
496
+ const srcOp = inst.operands[1];
497
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && lexicalThisRegs.has(srcOp.value)) {
498
+ if (localOp && localOp.kind === OperandKind5.Register && typeof localOp.value === "string") {
499
+ lexicalThisLocals.add(localOp.value);
500
+ }
501
+ }
502
+ } else if (inst.opcode === OpCode4.LoadLocal && inst.result) {
503
+ const localOp = inst.operands[0];
504
+ if (localOp && localOp.kind === OperandKind5.Register && typeof localOp.value === "string") {
505
+ if (lexicalThisLocals.has(localOp.value) || lexicalThisRegs.has(localOp.value)) {
506
+ lexicalThisRegs.add(inst.result);
507
+ }
508
+ }
509
+ } else if (inst.result) {
510
+ if (inst.opcode === OpCode4.CellGet || inst.opcode === OpCode4.CellNew) {
511
+ const srcOp = inst.operands[0];
512
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && lexicalThisRegs.has(srcOp.value)) {
513
+ lexicalThisRegs.add(inst.result);
514
+ }
515
+ }
516
+ }
517
+ }
518
+ }
519
+ lexicalSizeChanged = lexicalThisRegs.size !== oldRegSize || lexicalThisLocals.size !== oldLocalSize;
520
+ }
521
+ const localObjects = /* @__PURE__ */ new Set();
522
+ const localObjectLocals = /* @__PURE__ */ new Set();
523
+ let localObjSizeChanged = true;
524
+ while (localObjSizeChanged) {
525
+ const oldRegSize = localObjects.size;
526
+ const oldLocalSize = localObjectLocals.size;
527
+ for (const block of func.blocks) {
528
+ for (const inst of block.instructions) {
529
+ if (inst.opcode === OpCode4.ObjectNew && inst.result) {
530
+ localObjects.add(inst.result);
531
+ } else if (inst.opcode === OpCode4.Move) {
532
+ const srcOp = inst.operands[0];
533
+ const destOp = inst.operands[1];
534
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && localObjects.has(srcOp.value)) {
535
+ if (destOp && destOp.kind === OperandKind5.Register && typeof destOp.value === "string") {
536
+ localObjects.add(destOp.value);
537
+ }
538
+ }
539
+ } else if (inst.opcode === OpCode4.StoreLocal) {
540
+ const localOp = inst.operands[0];
541
+ const srcOp = inst.operands[1];
542
+ if (srcOp && srcOp.kind === OperandKind5.Register && typeof srcOp.value === "string" && localObjects.has(srcOp.value)) {
543
+ if (localOp && localOp.kind === OperandKind5.Register && typeof localOp.value === "string") {
544
+ localObjectLocals.add(localOp.value);
545
+ }
546
+ }
547
+ } else if (inst.opcode === OpCode4.LoadLocal && inst.result) {
548
+ const localOp = inst.operands[0];
549
+ if (localOp && localOp.kind === OperandKind5.Register && typeof localOp.value === "string") {
550
+ if (localObjectLocals.has(localOp.value) || localObjects.has(localOp.value)) {
551
+ localObjects.add(inst.result);
552
+ }
553
+ }
554
+ }
555
+ }
556
+ }
557
+ localObjSizeChanged = localObjects.size !== oldRegSize || localObjectLocals.size !== oldLocalSize;
558
+ }
559
+ const newBlocks = func.blocks.map((block) => {
560
+ const newInstructions = [];
561
+ for (const inst of block.instructions) {
562
+ if (inst.opcode === OpCode4.PropGet || inst.opcode === OpCode4.PropSet) {
563
+ const objOp = inst.operands[0];
564
+ const keyOp = inst.operands[1];
565
+ const isStaticContext = func.attributes && func.attributes.indexOf(FunctionAttribute.Static) >= 0;
566
+ const isObjThis = !isStaticContext && objOp && objOp.kind === OperandKind5.Register && typeof objOp.value === "string" && (isThisRegister(func, objOp.value) || lexicalThisRegs.has(objOp.value));
567
+ const isObjParam = objOp && objOp.kind === OperandKind5.Register && typeof objOp.value === "string" && paramRegs.has(objOp.value);
568
+ const isLocalObj = objOp && objOp.kind === OperandKind5.Register && typeof objOp.value === "string" && localObjects.has(objOp.value);
569
+ const propName = keyOp && keyOp.kind === OperandKind5.Register && typeof keyOp.value === "string" ? getPropertyNameOfRegister(func, keyOp.value, constantPool) : void 0;
570
+ const isBuiltin = propName && (standardBuiltins.has(propName) || ctx.module.exports.some((e) => e.exportedName === propName || e.localName === propName) || ctx.module.imports.some((i) => i.localName === propName || i.importedName === propName));
571
+ if (!isObjThis && !isObjParam && !isLocalObj && propName && !isBuiltin) {
572
+ changed = true;
573
+ nodesTransformed++;
574
+ const propHash = hashString(propName, ctx.profile.seed);
575
+ const hashedName = `hash_${propHash}`;
576
+ let cpIndex = constantPool.findIndex((c) => c.kind === ConstantKind3.String && c.value === hashedName);
577
+ if (cpIndex === -1) {
578
+ cpIndex = constantPool.length;
579
+ constantPool.push({
580
+ index: cpIndex,
581
+ kind: ConstantKind3.String,
582
+ value: hashedName
583
+ });
584
+ }
585
+ const tempReg = `r${nextReg + tempIndex++}`;
586
+ addedLocals.push({
587
+ name: `ns_virt_temp_${tempReg}`,
588
+ register: tempReg,
589
+ type: IRType4.String,
590
+ isCaptured: false
591
+ });
592
+ newInstructions.push({
593
+ opcode: OpCode4.LoadConst,
594
+ operands: [{ kind: OperandKind5.ConstantIndex, value: cpIndex }],
595
+ result: tempReg,
596
+ sourceLocation: inst.sourceLocation
597
+ });
598
+ const ops = [...inst.operands];
599
+ if (ops.length >= 2) {
600
+ ops[1] = { kind: OperandKind5.Register, value: tempReg };
601
+ }
602
+ newInstructions.push({
603
+ ...inst,
604
+ opcode: inst.opcode === OpCode4.PropGet ? OpCode4.ComputedGet : OpCode4.ComputedSet,
605
+ operands: ops,
606
+ metadata: { namespaceVirtualization: true }
607
+ });
608
+ } else {
609
+ newInstructions.push(inst);
610
+ }
611
+ } else {
612
+ newInstructions.push(inst);
613
+ }
614
+ }
615
+ return changed ? { ...block, instructions: newInstructions } : block;
616
+ });
617
+ return changed ? {
618
+ ...func,
619
+ locals: [...func.locals, ...addedLocals],
620
+ blocks: newBlocks
621
+ } : func;
622
+ });
623
+ const newModule = {
624
+ ...ctx.module,
625
+ functions: newFunctions,
626
+ constantPool
627
+ };
628
+ return {
629
+ module: newModule,
630
+ symbolsRenamed: 0,
631
+ nodesTransformed,
632
+ diagnostics: []
633
+ };
634
+ }
635
+ };
636
+
637
+ // src/passes/type-level-fake-path.ts
638
+ import { OpCode as OpCode5, ConstantKind as ConstantKind4, OperandKind as OperandKind6, IRType as IRType5 } from "@tsvm/shared";
639
+ var TypeLevelFakePathPass = class {
640
+ name = "TypeLevelFakePathPass";
641
+ priority = 30;
642
+ execute(ctx) {
643
+ let nodesTransformed = 0;
644
+ const newCP = [...ctx.module.constantPool];
645
+ function collectDecoyInstructions(func, junkRegs, rng) {
646
+ const realBlocks = func.blocks.filter((b) => !b.id.startsWith("__fake_path_") && b.instructions.length >= 2);
647
+ if (realBlocks.length === 0) return [];
648
+ const sourceBlock = rng.pick(realBlocks);
649
+ const count = Math.min(rng.nextRange(2, 3), sourceBlock.instructions.length);
650
+ const startIdx = rng.nextRange(0, Math.max(0, sourceBlock.instructions.length - count));
651
+ const decoys = [];
652
+ for (let d = 0; d < count; d++) {
653
+ const origInst = sourceBlock.instructions[startIdx + d];
654
+ if (!origInst) break;
655
+ if (origInst.opcode === OpCode5.Jmp || origInst.opcode === OpCode5.JmpIf || origInst.opcode === OpCode5.JmpIfNot || origInst.opcode === OpCode5.Return || origInst.opcode === OpCode5.ReturnVoid || origInst.opcode === OpCode5.Throw || origInst.opcode === OpCode5.Trap || origInst.opcode === OpCode5.Halt || origInst.opcode === OpCode5.Call || origInst.opcode === OpCode5.CallMethod || origInst.opcode === OpCode5.New) {
656
+ continue;
657
+ }
658
+ const junkReg = junkRegs[d % junkRegs.length];
659
+ decoys.push({
660
+ ...origInst,
661
+ result: origInst.result ? junkReg : origInst.result,
662
+ sourceLocation: void 0,
663
+ // Strip source location from decoys
664
+ metadata: void 0
665
+ });
666
+ }
667
+ return decoys;
668
+ }
669
+ function buildFakeBlockEnding(rng, junkTemp1, junkTemp2, regX, fortyTwoIdx, undefinedIdx) {
670
+ const endingType = rng.nextRange(0, 2);
671
+ if (endingType === 0) {
672
+ return {
673
+ instructions: [{ opcode: OpCode5.Trap, operands: [] }],
674
+ terminatorKind: "unreachable"
675
+ };
676
+ }
677
+ if (endingType === 1) {
678
+ return {
679
+ instructions: [
680
+ {
681
+ opcode: OpCode5.LoadConst,
682
+ operands: [{ kind: OperandKind6.ConstantIndex, value: undefinedIdx }],
683
+ result: junkTemp1
684
+ }
685
+ ],
686
+ terminatorKind: "return"
687
+ };
688
+ }
689
+ return {
690
+ instructions: [
691
+ {
692
+ opcode: OpCode5.LoadConst,
693
+ operands: [{ kind: OperandKind6.ConstantIndex, value: fortyTwoIdx }],
694
+ result: junkTemp1
695
+ },
696
+ {
697
+ opcode: OpCode5.Add,
698
+ operands: [
699
+ { kind: OperandKind6.Register, value: junkTemp1 },
700
+ { kind: OperandKind6.Register, value: regX }
701
+ ],
702
+ result: junkTemp2
703
+ },
704
+ {
705
+ opcode: OpCode5.Move,
706
+ operands: [
707
+ { kind: OperandKind6.Register, value: junkTemp2 },
708
+ { kind: OperandKind6.Register, value: junkTemp1 }
709
+ ]
710
+ }
711
+ ],
712
+ terminatorKind: "return"
713
+ };
714
+ }
715
+ const newFunctions = ctx.module.functions.map((func) => {
716
+ if (!func.isVirtualized) return func;
717
+ if (func.blocks.length < 3) return func;
718
+ const isParanoid = ctx.profile.vm?.runtimeHardening === "paranoid";
719
+ if (!isParanoid && ctx.rng.nextFloat() >= 0.25) {
720
+ return func;
721
+ }
722
+ let eligibleBlocks = func.blocks.filter((b) => b.terminator.kind === "jump");
723
+ if (eligibleBlocks.length === 0) {
724
+ return func;
725
+ }
726
+ const numFakes = isParanoid ? ctx.rng.nextRange(1, 3) : 1;
727
+ let currentFunc = { ...func };
728
+ for (let f = 0; f < numFakes; f++) {
729
+ eligibleBlocks = currentFunc.blocks.filter((b) => b.terminator.kind === "jump" && !b.id.startsWith("__fake_path_"));
730
+ if (eligibleBlocks.length === 0) break;
731
+ const blockToModify = ctx.rng.pick(eligibleBlocks);
732
+ if (!blockToModify.terminator.targets || blockToModify.terminator.targets.length === 0) {
733
+ continue;
734
+ }
735
+ nodesTransformed++;
736
+ const nextReg = getMaxRegister(currentFunc);
737
+ const tempA = `r${nextReg}`;
738
+ const tempB = `r${nextReg + 1}`;
739
+ const tempC = `r${nextReg + 2}`;
740
+ const tempD = `r${nextReg + 3}`;
741
+ const tempE = `r${nextReg + 4}`;
742
+ const tempF = `r${nextReg + 5}`;
743
+ const tempG = `r${nextReg + 6}`;
744
+ const tempP = `r${nextReg + 7}`;
745
+ const tempQ = `r${nextReg + 8}`;
746
+ const junkTemp1 = `r${nextReg + 9}`;
747
+ const junkTemp2 = `r${nextReg + 10}`;
748
+ const junkTemp3 = `r${nextReg + 11}`;
749
+ const regX = currentFunc.params.length > 0 ? currentFunc.params[0].register : "r0";
750
+ let zeroIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 0);
751
+ if (zeroIdx === -1) {
752
+ zeroIdx = newCP.length;
753
+ newCP.push({ index: zeroIdx, kind: ConstantKind4.Number, value: 0 });
754
+ }
755
+ let threeIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 3);
756
+ if (threeIdx === -1) {
757
+ threeIdx = newCP.length;
758
+ newCP.push({ index: threeIdx, kind: ConstantKind4.Number, value: 3 });
759
+ }
760
+ let fiveIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 5);
761
+ if (fiveIdx === -1) {
762
+ fiveIdx = newCP.length;
763
+ newCP.push({ index: fiveIdx, kind: ConstantKind4.Number, value: 5 });
764
+ }
765
+ let sevenIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 7);
766
+ if (sevenIdx === -1) {
767
+ sevenIdx = newCP.length;
768
+ newCP.push({ index: sevenIdx, kind: ConstantKind4.Number, value: 7 });
769
+ }
770
+ let fourIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 4);
771
+ if (fourIdx === -1) {
772
+ fourIdx = newCP.length;
773
+ newCP.push({ index: fourIdx, kind: ConstantKind4.Number, value: 4 });
774
+ }
775
+ let fortyTwoIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 42);
776
+ if (fortyTwoIdx === -1) {
777
+ fortyTwoIdx = newCP.length;
778
+ newCP.push({ index: fortyTwoIdx, kind: ConstantKind4.Number, value: 42 });
779
+ }
780
+ let undefinedIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Undefined);
781
+ if (undefinedIdx === -1) {
782
+ undefinedIdx = newCP.length;
783
+ newCP.push({ index: undefinedIdx, kind: ConstantKind4.Undefined, value: null });
784
+ }
785
+ let processIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.String && cp.value === "process");
786
+ if (processIdx === -1) {
787
+ processIdx = newCP.length;
788
+ newCP.push({ index: processIdx, kind: ConstantKind4.String, value: "process" });
789
+ }
790
+ let windowIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.String && cp.value === "window");
791
+ if (windowIdx === -1) {
792
+ windowIdx = newCP.length;
793
+ newCP.push({ index: windowIdx, kind: ConstantKind4.String, value: "window" });
794
+ }
795
+ let lengthIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.String && cp.value === "length");
796
+ if (lengthIdx === -1) {
797
+ lengthIdx = newCP.length;
798
+ newCP.push({ index: lengthIdx, kind: ConstantKind4.String, value: "length" });
799
+ }
800
+ let thirtyOneIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 31);
801
+ if (thirtyOneIdx === -1) {
802
+ thirtyOneIdx = newCP.length;
803
+ newCP.push({ index: thirtyOneIdx, kind: ConstantKind4.Number, value: 31 });
804
+ }
805
+ let fifteenIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 15);
806
+ if (fifteenIdx === -1) {
807
+ fifteenIdx = newCP.length;
808
+ newCP.push({ index: fifteenIdx, kind: ConstantKind4.Number, value: 15 });
809
+ }
810
+ let eightIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 8);
811
+ if (eightIdx === -1) {
812
+ eightIdx = newCP.length;
813
+ newCP.push({ index: eightIdx, kind: ConstantKind4.Number, value: 8 });
814
+ }
815
+ let errorIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.String && cp.value === "Error");
816
+ if (errorIdx === -1) {
817
+ errorIdx = newCP.length;
818
+ newCP.push({ index: errorIdx, kind: ConstantKind4.String, value: "Error" });
819
+ }
820
+ let stackIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.String && cp.value === "stack");
821
+ if (stackIdx === -1) {
822
+ stackIdx = newCP.length;
823
+ newCP.push({ index: stackIdx, kind: ConstantKind4.String, value: "stack" });
824
+ }
825
+ let stringIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.String && cp.value === "string");
826
+ if (stringIdx === -1) {
827
+ stringIdx = newCP.length;
828
+ newCP.push({ index: stringIdx, kind: ConstantKind4.String, value: "string" });
829
+ }
830
+ const templateId = isParanoid ? ctx.rng.nextRange(0, 4) : 0;
831
+ let opaqueInsts = [];
832
+ if (templateId === 0) {
833
+ opaqueInsts = [
834
+ // 1. typeof process length
835
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: processIdx }], result: tempA },
836
+ { opcode: OpCode5.LoadGlobal, operands: [{ kind: OperandKind6.Register, value: tempA }], result: tempA },
837
+ { opcode: OpCode5.TypeOf, operands: [{ kind: OperandKind6.Register, value: tempA }], result: tempB },
838
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: lengthIdx }], result: tempC },
839
+ {
840
+ opcode: OpCode5.PropGet,
841
+ operands: [
842
+ { kind: OperandKind6.Register, value: tempB },
843
+ { kind: OperandKind6.Register, value: tempC }
844
+ ],
845
+ result: tempD
846
+ },
847
+ // len1
848
+ // 2. typeof window length
849
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: windowIdx }], result: tempA },
850
+ { opcode: OpCode5.LoadGlobal, operands: [{ kind: OperandKind6.Register, value: tempA }], result: tempA },
851
+ { opcode: OpCode5.TypeOf, operands: [{ kind: OperandKind6.Register, value: tempA }], result: tempE },
852
+ {
853
+ opcode: OpCode5.PropGet,
854
+ operands: [
855
+ { kind: OperandKind6.Register, value: tempE },
856
+ { kind: OperandKind6.Register, value: tempC }
857
+ ],
858
+ result: tempF
859
+ },
860
+ // len2
861
+ // 3. hash = (len1 * 31 + len2) & 0xF
862
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: thirtyOneIdx }], result: tempA },
863
+ {
864
+ opcode: OpCode5.Mul,
865
+ operands: [
866
+ { kind: OperandKind6.Register, value: tempD },
867
+ { kind: OperandKind6.Register, value: tempA }
868
+ ],
869
+ result: tempG
870
+ },
871
+ // len1 * 31
872
+ {
873
+ opcode: OpCode5.Add,
874
+ operands: [
875
+ { kind: OperandKind6.Register, value: tempG },
876
+ { kind: OperandKind6.Register, value: tempF }
877
+ ],
878
+ result: tempG
879
+ },
880
+ // len1 * 31 + len2
881
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: fifteenIdx }], result: tempA },
882
+ {
883
+ opcode: OpCode5.BitAnd,
884
+ operands: [
885
+ { kind: OperandKind6.Register, value: tempG },
886
+ { kind: OperandKind6.Register, value: tempA }
887
+ ],
888
+ result: tempG
889
+ },
890
+ // hash = tempG & 15
891
+ // 4. (hash * hash + 5) % 8 !== 0
892
+ {
893
+ opcode: OpCode5.Mul,
894
+ operands: [
895
+ { kind: OperandKind6.Register, value: tempG },
896
+ { kind: OperandKind6.Register, value: tempG }
897
+ ],
898
+ result: tempE
899
+ },
900
+ // hash * hash
901
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: fiveIdx }], result: tempA },
902
+ {
903
+ opcode: OpCode5.Add,
904
+ operands: [
905
+ { kind: OperandKind6.Register, value: tempE },
906
+ { kind: OperandKind6.Register, value: tempA }
907
+ ],
908
+ result: tempE
909
+ },
910
+ // hash * hash + 5
911
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: eightIdx }], result: tempA },
912
+ {
913
+ opcode: OpCode5.Mod,
914
+ operands: [
915
+ { kind: OperandKind6.Register, value: tempE },
916
+ { kind: OperandKind6.Register, value: tempA }
917
+ ],
918
+ result: tempE
919
+ },
920
+ // (hash*hash+5) % 8
921
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: zeroIdx }], result: tempA },
922
+ {
923
+ opcode: OpCode5.StrictEq,
924
+ operands: [
925
+ { kind: OperandKind6.Register, value: tempE },
926
+ { kind: OperandKind6.Register, value: tempA }
927
+ ],
928
+ result: tempE
929
+ },
930
+ // hashMod === 0
931
+ { opcode: OpCode5.Not, operands: [{ kind: OperandKind6.Register, value: tempE }], result: tempP },
932
+ // envOpaque = !hashModEqualsZero
933
+ // 5. stack = new Error().stack, typeof stack === 'string'
934
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: errorIdx }], result: tempA },
935
+ { opcode: OpCode5.LoadGlobal, operands: [{ kind: OperandKind6.Register, value: tempA }], result: tempA },
936
+ { opcode: OpCode5.New, operands: [{ kind: OperandKind6.Register, value: tempA }], result: tempB },
937
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: stackIdx }], result: tempC },
938
+ {
939
+ opcode: OpCode5.PropGet,
940
+ operands: [
941
+ { kind: OperandKind6.Register, value: tempB },
942
+ { kind: OperandKind6.Register, value: tempC }
943
+ ],
944
+ result: tempD
945
+ },
946
+ // stack
947
+ { opcode: OpCode5.TypeOf, operands: [{ kind: OperandKind6.Register, value: tempD }], result: tempE },
948
+ // typeof stack
949
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: stringIdx }], result: tempA },
950
+ {
951
+ opcode: OpCode5.StrictEq,
952
+ operands: [
953
+ { kind: OperandKind6.Register, value: tempE },
954
+ { kind: OperandKind6.Register, value: tempA }
955
+ ],
956
+ result: tempF
957
+ },
958
+ // typeof stack === 'string'
959
+ // 6. envPredicate = (rEnvOpaque === rIsString)
960
+ {
961
+ opcode: OpCode5.StrictEq,
962
+ operands: [
963
+ { kind: OperandKind6.Register, value: tempP },
964
+ { kind: OperandKind6.Register, value: tempF }
965
+ ],
966
+ result: tempQ
967
+ }
968
+ // tempQ is envPredicate (always true)
969
+ ];
970
+ } else if (templateId === 1) {
971
+ opaqueInsts = [
972
+ { opcode: OpCode5.GetEntropy, operands: [], result: tempA },
973
+ // tempA = x
974
+ {
975
+ opcode: OpCode5.Mul,
976
+ operands: [
977
+ { kind: OperandKind6.Register, value: tempA },
978
+ { kind: OperandKind6.Register, value: tempA }
979
+ ],
980
+ result: tempB
981
+ },
982
+ // tempB = x^2
983
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: threeIdx }], result: tempC },
984
+ // tempC = 3
985
+ {
986
+ opcode: OpCode5.BitAnd,
987
+ operands: [
988
+ { kind: OperandKind6.Register, value: tempB },
989
+ { kind: OperandKind6.Register, value: tempC }
990
+ ],
991
+ result: tempD
992
+ },
993
+ // tempD = x^2 & 3
994
+ {
995
+ opcode: OpCode5.StrictEq,
996
+ operands: [
997
+ { kind: OperandKind6.Register, value: tempD },
998
+ { kind: OperandKind6.Register, value: tempC }
999
+ ],
1000
+ result: tempP
1001
+ },
1002
+ // tempP = (x^2 & 3 === 3), always false
1003
+ { opcode: OpCode5.Not, operands: [{ kind: OperandKind6.Register, value: tempP }], result: tempQ }
1004
+ // tempQ = true
1005
+ ];
1006
+ } else if (templateId === 2) {
1007
+ let thirtyOneIdx2 = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 31);
1008
+ if (thirtyOneIdx2 === -1) {
1009
+ thirtyOneIdx2 = newCP.length;
1010
+ newCP.push({ index: thirtyOneIdx2, kind: ConstantKind4.Number, value: 31 });
1011
+ }
1012
+ let twoIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 2);
1013
+ if (twoIdx === -1) {
1014
+ twoIdx = newCP.length;
1015
+ newCP.push({ index: twoIdx, kind: ConstantKind4.Number, value: 2 });
1016
+ }
1017
+ opaqueInsts = [
1018
+ { opcode: OpCode5.GetEntropy, operands: [], result: tempA },
1019
+ // tempA = x
1020
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: thirtyOneIdx2 }], result: tempC },
1021
+ // tempC = 31
1022
+ {
1023
+ opcode: OpCode5.Mul,
1024
+ operands: [
1025
+ { kind: OperandKind6.Register, value: tempC },
1026
+ { kind: OperandKind6.Register, value: tempA }
1027
+ ],
1028
+ result: tempB
1029
+ },
1030
+ // tempB = 31*x
1031
+ {
1032
+ opcode: OpCode5.Mul,
1033
+ operands: [
1034
+ { kind: OperandKind6.Register, value: tempB },
1035
+ { kind: OperandKind6.Register, value: tempB }
1036
+ ],
1037
+ result: tempD
1038
+ },
1039
+ // tempD = (31*x)^2
1040
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: threeIdx }], result: tempC },
1041
+ // tempC = 3
1042
+ {
1043
+ opcode: OpCode5.Mod,
1044
+ operands: [
1045
+ { kind: OperandKind6.Register, value: tempD },
1046
+ { kind: OperandKind6.Register, value: tempC }
1047
+ ],
1048
+ result: tempE
1049
+ },
1050
+ // tempE = (31*x)^2 % 3
1051
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: twoIdx }], result: tempC },
1052
+ // tempC = 2
1053
+ {
1054
+ opcode: OpCode5.StrictEq,
1055
+ operands: [
1056
+ { kind: OperandKind6.Register, value: tempE },
1057
+ { kind: OperandKind6.Register, value: tempC }
1058
+ ],
1059
+ result: tempP
1060
+ },
1061
+ // tempP = false always
1062
+ { opcode: OpCode5.Not, operands: [{ kind: OperandKind6.Register, value: tempP }], result: tempQ }
1063
+ // tempQ = true
1064
+ ];
1065
+ } else if (templateId === 3) {
1066
+ let twoIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 2);
1067
+ if (twoIdx === -1) {
1068
+ twoIdx = newCP.length;
1069
+ newCP.push({ index: twoIdx, kind: ConstantKind4.Number, value: 2 });
1070
+ }
1071
+ opaqueInsts = [
1072
+ { opcode: OpCode5.GetEntropy, operands: [], result: tempA },
1073
+ // tempA = x
1074
+ {
1075
+ opcode: OpCode5.Mul,
1076
+ operands: [
1077
+ { kind: OperandKind6.Register, value: tempA },
1078
+ { kind: OperandKind6.Register, value: tempA }
1079
+ ],
1080
+ result: tempB
1081
+ },
1082
+ // tempB = x^2
1083
+ {
1084
+ opcode: OpCode5.Add,
1085
+ operands: [
1086
+ { kind: OperandKind6.Register, value: tempB },
1087
+ { kind: OperandKind6.Register, value: tempA }
1088
+ ],
1089
+ result: tempC
1090
+ },
1091
+ // tempC = x^2 + x
1092
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: twoIdx }], result: tempD },
1093
+ // tempD = 2
1094
+ {
1095
+ opcode: OpCode5.Mod,
1096
+ operands: [
1097
+ { kind: OperandKind6.Register, value: tempC },
1098
+ { kind: OperandKind6.Register, value: tempD }
1099
+ ],
1100
+ result: tempE
1101
+ },
1102
+ // tempE = (x^2+x) % 2
1103
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: zeroIdx }], result: tempD },
1104
+ // tempD = 0
1105
+ {
1106
+ opcode: OpCode5.StrictEq,
1107
+ operands: [
1108
+ { kind: OperandKind6.Register, value: tempE },
1109
+ { kind: OperandKind6.Register, value: tempD }
1110
+ ],
1111
+ result: tempP
1112
+ },
1113
+ // always true
1114
+ { opcode: OpCode5.Move, operands: [{ kind: OperandKind6.Register, value: tempP }], result: tempQ }
1115
+ ];
1116
+ } else {
1117
+ let twoIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 2);
1118
+ if (twoIdx === -1) {
1119
+ twoIdx = newCP.length;
1120
+ newCP.push({ index: twoIdx, kind: ConstantKind4.Number, value: 2 });
1121
+ }
1122
+ let oneIdx = newCP.findIndex((cp) => cp.kind === ConstantKind4.Number && cp.value === 1);
1123
+ if (oneIdx === -1) {
1124
+ oneIdx = newCP.length;
1125
+ newCP.push({ index: oneIdx, kind: ConstantKind4.Number, value: 1 });
1126
+ }
1127
+ opaqueInsts = [
1128
+ { opcode: OpCode5.GetEntropy, operands: [], result: tempA },
1129
+ // tempA = x
1130
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: oneIdx }], result: tempC },
1131
+ // tempC = 1
1132
+ {
1133
+ opcode: OpCode5.Add,
1134
+ operands: [
1135
+ { kind: OperandKind6.Register, value: tempA },
1136
+ { kind: OperandKind6.Register, value: tempC }
1137
+ ],
1138
+ result: tempB
1139
+ },
1140
+ // tempB = x+1
1141
+ {
1142
+ opcode: OpCode5.Mul,
1143
+ operands: [
1144
+ { kind: OperandKind6.Register, value: tempA },
1145
+ { kind: OperandKind6.Register, value: tempB }
1146
+ ],
1147
+ result: tempD
1148
+ },
1149
+ // tempD = x*(x+1)
1150
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: twoIdx }], result: tempC },
1151
+ // tempC = 2
1152
+ {
1153
+ opcode: OpCode5.Mod,
1154
+ operands: [
1155
+ { kind: OperandKind6.Register, value: tempD },
1156
+ { kind: OperandKind6.Register, value: tempC }
1157
+ ],
1158
+ result: tempE
1159
+ },
1160
+ // tempE = x*(x+1) % 2
1161
+ { opcode: OpCode5.LoadConst, operands: [{ kind: OperandKind6.ConstantIndex, value: zeroIdx }], result: tempC },
1162
+ // tempC = 0
1163
+ {
1164
+ opcode: OpCode5.StrictEq,
1165
+ operands: [
1166
+ { kind: OperandKind6.Register, value: tempE },
1167
+ { kind: OperandKind6.Register, value: tempC }
1168
+ ],
1169
+ result: tempP
1170
+ },
1171
+ // always true
1172
+ { opcode: OpCode5.Move, operands: [{ kind: OperandKind6.Register, value: tempP }], result: tempQ }
1173
+ ];
1174
+ }
1175
+ const fakeBlockId = `__fake_path_${ctx.rng.identifier(6)}`;
1176
+ const decoyInsts = collectDecoyInstructions(currentFunc, [junkTemp1, junkTemp2, junkTemp3], ctx.rng);
1177
+ const ending = buildFakeBlockEnding(ctx.rng, junkTemp1, junkTemp2, regX, fortyTwoIdx, undefinedIdx);
1178
+ const fakeBlock = {
1179
+ id: fakeBlockId,
1180
+ label: "fake_path",
1181
+ instructions: [...decoyInsts, ...ending.instructions],
1182
+ terminator: {
1183
+ kind: ending.terminatorKind,
1184
+ targets: []
1185
+ },
1186
+ predecessors: [blockToModify.id],
1187
+ successors: [],
1188
+ phiNodes: []
1189
+ };
1190
+ const originalTarget = blockToModify.terminator.targets[0];
1191
+ const newBlocks = currentFunc.blocks.flatMap((block) => {
1192
+ if (block.id === blockToModify.id) {
1193
+ const updatedBlock = {
1194
+ ...block,
1195
+ instructions: [...block.instructions, ...opaqueInsts],
1196
+ terminator: {
1197
+ kind: "branch",
1198
+ targets: [originalTarget, fakeBlock.id],
1199
+ condition: tempQ
1200
+ },
1201
+ successors: [originalTarget, fakeBlock.id]
1202
+ };
1203
+ return [updatedBlock, fakeBlock];
1204
+ }
1205
+ return [block];
1206
+ });
1207
+ const addedLocals = [
1208
+ { name: `fake_path_entropy_${tempA}`, register: tempA, type: IRType5.Number, isCaptured: false },
1209
+ { name: `fake_path_temp_${tempB}`, register: tempB, type: IRType5.Number, isCaptured: false },
1210
+ { name: `fake_path_temp_${tempC}`, register: tempC, type: IRType5.Number, isCaptured: false },
1211
+ { name: `fake_path_temp_${tempD}`, register: tempD, type: IRType5.Number, isCaptured: false },
1212
+ { name: `fake_path_temp_${tempE}`, register: tempE, type: IRType5.Number, isCaptured: false },
1213
+ { name: `fake_path_temp_${tempF}`, register: tempF, type: IRType5.Number, isCaptured: false },
1214
+ { name: `fake_path_temp_${tempG}`, register: tempG, type: IRType5.Number, isCaptured: false },
1215
+ { name: `fake_path_pred_${tempP}`, register: tempP, type: IRType5.Boolean, isCaptured: false },
1216
+ { name: `fake_path_cond_${tempQ}`, register: tempQ, type: IRType5.Boolean, isCaptured: false },
1217
+ { name: `fake_path_junk_${junkTemp1}`, register: junkTemp1, type: IRType5.Number, isCaptured: false },
1218
+ { name: `fake_path_junk_${junkTemp2}`, register: junkTemp2, type: IRType5.Number, isCaptured: false },
1219
+ { name: `fake_path_junk_${junkTemp3}`, register: junkTemp3, type: IRType5.Any, isCaptured: false }
1220
+ ];
1221
+ currentFunc = {
1222
+ ...currentFunc,
1223
+ locals: [...currentFunc.locals, ...addedLocals],
1224
+ blocks: newBlocks
1225
+ };
1226
+ }
1227
+ return currentFunc;
1228
+ });
1229
+ return {
1230
+ module: { ...ctx.module, functions: newFunctions, constantPool: newCP },
1231
+ symbolsRenamed: 0,
1232
+ nodesTransformed,
1233
+ diagnostics: []
1234
+ };
1235
+ }
1236
+ };
1237
+
1238
+ // src/passes/symbol-indirection.ts
1239
+ var SymbolIndirectionPass = class {
1240
+ name = "SymbolIndirectionPass";
1241
+ priority = 50;
1242
+ execute(ctx) {
1243
+ let symbolsRenamed = 0;
1244
+ const newFunctions = ctx.module.functions.map((func) => {
1245
+ if (func.isExported) {
1246
+ return func;
1247
+ }
1248
+ if (ctx.profile.reactSafe && func.name.startsWith("use")) {
1249
+ return func;
1250
+ }
1251
+ const newName = `fn_${ctx.rng.identifier(8)}`;
1252
+ symbolsRenamed++;
1253
+ const newLocals = func.locals.map((local) => {
1254
+ symbolsRenamed++;
1255
+ return {
1256
+ ...local,
1257
+ name: `v_${ctx.rng.identifier(6)}`
1258
+ };
1259
+ });
1260
+ return {
1261
+ ...func,
1262
+ name: newName,
1263
+ locals: newLocals
1264
+ };
1265
+ });
1266
+ const newGlobals = ctx.module.globals.map((g) => {
1267
+ if (g.isExported && ctx.profile.preserveExports) return g;
1268
+ symbolsRenamed++;
1269
+ return {
1270
+ ...g,
1271
+ name: `g_${ctx.rng.identifier(8)}`
1272
+ };
1273
+ });
1274
+ const newModule = {
1275
+ ...ctx.module,
1276
+ functions: newFunctions,
1277
+ globals: newGlobals
1278
+ };
1279
+ return {
1280
+ module: newModule,
1281
+ symbolsRenamed,
1282
+ nodesTransformed: 0,
1283
+ diagnostics: []
1284
+ };
1285
+ }
1286
+ };
1287
+
1288
+ // src/passes/string-pool-encoding.ts
1289
+ import { OpCode as OpCode6, ConstantKind as ConstantKind5, OperandKind as OperandKind7, IRType as IRType6 } from "@tsvm/shared";
1290
+ var StringPoolEncodingPass = class {
1291
+ name = "StringPoolEncodingPass";
1292
+ priority = 70;
1293
+ execute(ctx) {
1294
+ const config = ctx.profile.transforms.find((t) => t.name === this.name);
1295
+ const fragmentStrings = config?.options?.["fragmentStrings"] ?? true;
1296
+ const minLength = config?.options?.["minLength"] ?? 3;
1297
+ if (!fragmentStrings) {
1298
+ return {
1299
+ module: ctx.module,
1300
+ symbolsRenamed: 0,
1301
+ nodesTransformed: 0,
1302
+ diagnostics: []
1303
+ };
1304
+ }
1305
+ let nextConstantIndex = ctx.module.constantPool.length;
1306
+ const newConstantPool = [...ctx.module.constantPool];
1307
+ const getOrAddStringConstant = (val) => {
1308
+ const existing = newConstantPool.find((c) => c.kind === ConstantKind5.String && c.value === val);
1309
+ if (existing) {
1310
+ return existing.index;
1311
+ }
1312
+ const newIdx = nextConstantIndex++;
1313
+ newConstantPool.push({
1314
+ index: newIdx,
1315
+ kind: ConstantKind5.String,
1316
+ value: val
1317
+ });
1318
+ return newIdx;
1319
+ };
1320
+ const getOrAddNumberConstant = (val) => {
1321
+ const existing = newConstantPool.find((c) => c.kind === ConstantKind5.Number && c.value === val);
1322
+ if (existing) {
1323
+ return existing.index;
1324
+ }
1325
+ const newIdx = nextConstantIndex++;
1326
+ newConstantPool.push({
1327
+ index: newIdx,
1328
+ kind: ConstantKind5.Number,
1329
+ value: val
1330
+ });
1331
+ return newIdx;
1332
+ };
1333
+ let nodesTransformed = 0;
1334
+ const newFunctions = ctx.module.functions.map((fn) => {
1335
+ let nextTempRegId = getMaxRegister(fn);
1336
+ const addedLocals = [];
1337
+ const newBlocks = fn.blocks.map((block) => {
1338
+ const newInstructions = [];
1339
+ block.instructions.forEach((inst) => {
1340
+ if (inst.opcode === OpCode6.LoadConst && inst.operands.length > 0 && inst.result) {
1341
+ const op = inst.operands[0];
1342
+ if (op.kind === OperandKind7.ConstantIndex) {
1343
+ const constIndex = op.value;
1344
+ const constant = ctx.module.constantPool[constIndex];
1345
+ if (constant && constant.kind === ConstantKind5.String && typeof constant.value === "string" && constant.value.length >= minLength) {
1346
+ const fullStr = constant.value;
1347
+ const baseKey = ctx.rng.nextRange(1, 255);
1348
+ const r_arr = `r${nextTempRegId++}`;
1349
+ const r_val = `r${nextTempRegId++}`;
1350
+ const r_key = `r${nextTempRegId++}`;
1351
+ const r_dec = `r${nextTempRegId++}`;
1352
+ const r_idx = `r${nextTempRegId++}`;
1353
+ const r_string_str = `r${nextTempRegId++}`;
1354
+ const r_string = `r${nextTempRegId++}`;
1355
+ const r_from_char_code_str = `r${nextTempRegId++}`;
1356
+ const r_from_char_code = `r${nextTempRegId++}`;
1357
+ addedLocals.push(
1358
+ { name: `str_pool_temp_${r_arr}`, register: r_arr, type: IRType6.Any, isCaptured: false },
1359
+ { name: `str_pool_temp_${r_val}`, register: r_val, type: IRType6.Number, isCaptured: false },
1360
+ { name: `str_pool_temp_${r_key}`, register: r_key, type: IRType6.Number, isCaptured: false },
1361
+ { name: `str_pool_temp_${r_dec}`, register: r_dec, type: IRType6.Number, isCaptured: false },
1362
+ { name: `str_pool_temp_${r_idx}`, register: r_idx, type: IRType6.Number, isCaptured: false },
1363
+ { name: `str_pool_temp_${r_string_str}`, register: r_string_str, type: IRType6.String, isCaptured: false },
1364
+ { name: `str_pool_temp_${r_string}`, register: r_string, type: IRType6.Any, isCaptured: false },
1365
+ { name: `str_pool_temp_${r_from_char_code_str}`, register: r_from_char_code_str, type: IRType6.String, isCaptured: false },
1366
+ { name: `str_pool_temp_${r_from_char_code}`, register: r_from_char_code, type: IRType6.Any, isCaptured: false }
1367
+ );
1368
+ const stringNameIdx = getOrAddStringConstant("String");
1369
+ const fromCharCodeIdx = getOrAddStringConstant("fromCharCode");
1370
+ const stringPoolInsts = [
1371
+ {
1372
+ opcode: OpCode6.ArrayNew,
1373
+ operands: [],
1374
+ result: r_arr,
1375
+ sourceLocation: inst.sourceLocation
1376
+ }
1377
+ ];
1378
+ let prevEncoded = baseKey;
1379
+ for (let i = 0; i < fullStr.length; i++) {
1380
+ const charKey = (baseKey * (i + 1) ^ prevEncoded ^ i * 37) & 255;
1381
+ const effectiveKey = charKey === 0 ? 1 : charKey;
1382
+ const encoded = fullStr.charCodeAt(i) ^ effectiveKey;
1383
+ prevEncoded = encoded & 255;
1384
+ const valIdx = getOrAddNumberConstant(encoded);
1385
+ const keyIdx = getOrAddNumberConstant(effectiveKey);
1386
+ const idxIdx = getOrAddNumberConstant(i);
1387
+ stringPoolInsts.push(
1388
+ {
1389
+ opcode: OpCode6.LoadConst,
1390
+ operands: [{ kind: OperandKind7.ConstantIndex, value: valIdx }],
1391
+ result: r_val,
1392
+ sourceLocation: inst.sourceLocation
1393
+ },
1394
+ {
1395
+ opcode: OpCode6.LoadConst,
1396
+ operands: [{ kind: OperandKind7.ConstantIndex, value: keyIdx }],
1397
+ result: r_key,
1398
+ sourceLocation: inst.sourceLocation
1399
+ },
1400
+ {
1401
+ opcode: OpCode6.BitXor,
1402
+ operands: [
1403
+ { kind: OperandKind7.Register, value: r_val },
1404
+ { kind: OperandKind7.Register, value: r_key }
1405
+ ],
1406
+ result: r_dec,
1407
+ sourceLocation: inst.sourceLocation
1408
+ },
1409
+ {
1410
+ opcode: OpCode6.LoadConst,
1411
+ operands: [{ kind: OperandKind7.ConstantIndex, value: idxIdx }],
1412
+ result: r_idx,
1413
+ sourceLocation: inst.sourceLocation
1414
+ },
1415
+ {
1416
+ opcode: OpCode6.ComputedSet,
1417
+ operands: [
1418
+ { kind: OperandKind7.Register, value: r_arr },
1419
+ { kind: OperandKind7.Register, value: r_idx },
1420
+ { kind: OperandKind7.Register, value: r_dec }
1421
+ ],
1422
+ sourceLocation: inst.sourceLocation
1423
+ }
1424
+ );
1425
+ }
1426
+ stringPoolInsts.push(
1427
+ {
1428
+ opcode: OpCode6.LoadConst,
1429
+ operands: [{ kind: OperandKind7.ConstantIndex, value: stringNameIdx }],
1430
+ result: r_string_str,
1431
+ sourceLocation: inst.sourceLocation
1432
+ },
1433
+ {
1434
+ opcode: OpCode6.LoadGlobal,
1435
+ operands: [{ kind: OperandKind7.Register, value: r_string_str }],
1436
+ result: r_string,
1437
+ sourceLocation: inst.sourceLocation
1438
+ },
1439
+ {
1440
+ opcode: OpCode6.LoadConst,
1441
+ operands: [{ kind: OperandKind7.ConstantIndex, value: fromCharCodeIdx }],
1442
+ result: r_from_char_code_str,
1443
+ sourceLocation: inst.sourceLocation
1444
+ },
1445
+ {
1446
+ opcode: OpCode6.PropGet,
1447
+ operands: [
1448
+ { kind: OperandKind7.Register, value: r_string },
1449
+ { kind: OperandKind7.Register, value: r_from_char_code_str }
1450
+ ],
1451
+ result: r_from_char_code,
1452
+ sourceLocation: inst.sourceLocation
1453
+ },
1454
+ {
1455
+ opcode: OpCode6.CallWithArray,
1456
+ operands: [
1457
+ { kind: OperandKind7.Register, value: r_from_char_code },
1458
+ { kind: OperandKind7.Register, value: r_arr }
1459
+ ],
1460
+ result: inst.result,
1461
+ sourceLocation: inst.sourceLocation
1462
+ }
1463
+ );
1464
+ newInstructions.push(...stringPoolInsts);
1465
+ nodesTransformed++;
1466
+ return;
1467
+ }
1468
+ }
1469
+ }
1470
+ newInstructions.push(inst);
1471
+ });
1472
+ return {
1473
+ ...block,
1474
+ instructions: newInstructions
1475
+ };
1476
+ });
1477
+ return {
1478
+ ...fn,
1479
+ locals: [...fn.locals, ...addedLocals],
1480
+ blocks: newBlocks
1481
+ };
1482
+ });
1483
+ const newModule = {
1484
+ ...ctx.module,
1485
+ constantPool: newConstantPool,
1486
+ functions: newFunctions
1487
+ };
1488
+ return {
1489
+ module: newModule,
1490
+ symbolsRenamed: 0,
1491
+ nodesTransformed,
1492
+ diagnostics: []
1493
+ };
1494
+ }
1495
+ };
1496
+
1497
+ // src/passes/function-virtualization.ts
1498
+ import { FunctionAttribute as FunctionAttribute2 } from "@tsvm/shared";
1499
+ var FunctionVirtualizationPass = class {
1500
+ name = "FunctionVirtualizationPass";
1501
+ priority = 80;
1502
+ execute(ctx) {
1503
+ const newModule = {
1504
+ ...ctx.module,
1505
+ functions: ctx.module.functions.map((fn) => {
1506
+ if (fn.attributes.includes(FunctionAttribute2.ReactComponent)) {
1507
+ return { ...fn, isVirtualized: false };
1508
+ }
1509
+ return fn;
1510
+ })
1511
+ };
1512
+ return {
1513
+ module: newModule,
1514
+ symbolsRenamed: 0,
1515
+ nodesTransformed: 0,
1516
+ diagnostics: []
1517
+ };
1518
+ }
1519
+ };
1520
+
1521
+ // src/passes/dead-code-injection.ts
1522
+ import { OpCode as OpCode7, OperandKind as OperandKind8, ConstantKind as ConstantKind6 } from "@tsvm/shared";
1523
+ var DeadCodeInjectionPass = class {
1524
+ name = "DeadCodeInjectionPass";
1525
+ priority = 25;
1526
+ execute(ctx) {
1527
+ let nodesTransformed = 0;
1528
+ const newCP = [...ctx.module.constantPool];
1529
+ const newFunctions = ctx.module.functions.map((func) => {
1530
+ if (!func.isVirtualized) return func;
1531
+ let changed = false;
1532
+ const newBlocks = [];
1533
+ const maxReg = getMaxRegister(func) - 1;
1534
+ for (const block of func.blocks) {
1535
+ let currentBlockId = block.id;
1536
+ let currentLabel = block.label;
1537
+ let currentInstructions = [];
1538
+ let currentPredecessors = [...block.predecessors];
1539
+ let isFirstSubBlock = true;
1540
+ for (const inst of block.instructions) {
1541
+ if (ctx.rng.nextFloat() < 0.2) {
1542
+ changed = true;
1543
+ nodesTransformed++;
1544
+ const rStart = maxReg + 5;
1545
+ const junkReg1 = `r${ctx.rng.nextRange(rStart, rStart + 3)}`;
1546
+ const junkReg2 = `r${ctx.rng.nextRange(rStart + 4, rStart + 7)}`;
1547
+ const junkReg3 = `r${ctx.rng.nextRange(rStart + 8, rStart + 11)}`;
1548
+ if (newCP.length === 0) {
1549
+ newCP.push({ index: 0, kind: ConstantKind6.Number, value: 0 });
1550
+ }
1551
+ const cpMax = newCP.length - 1;
1552
+ const randCpIdx = ctx.rng.nextRange(0, cpMax);
1553
+ const patternChoice = ctx.rng.nextRange(0, 3);
1554
+ const deadCodeInsts = [];
1555
+ if (patternChoice === 0) {
1556
+ deadCodeInsts.push({
1557
+ opcode: OpCode7.LoadConst,
1558
+ operands: [{ kind: OperandKind8.ConstantIndex, value: randCpIdx }],
1559
+ result: junkReg1
1560
+ });
1561
+ deadCodeInsts.push({
1562
+ opcode: OpCode7.Move,
1563
+ operands: [
1564
+ { kind: OperandKind8.Register, value: junkReg1 },
1565
+ { kind: OperandKind8.Register, value: junkReg2 }
1566
+ ]
1567
+ });
1568
+ } else if (patternChoice === 1) {
1569
+ deadCodeInsts.push({
1570
+ opcode: OpCode7.LoadConst,
1571
+ operands: [{ kind: OperandKind8.ConstantIndex, value: randCpIdx }],
1572
+ result: junkReg1
1573
+ });
1574
+ deadCodeInsts.push({
1575
+ opcode: OpCode7.LoadConst,
1576
+ operands: [{ kind: OperandKind8.ConstantIndex, value: ctx.rng.nextRange(0, cpMax) }],
1577
+ result: junkReg2
1578
+ });
1579
+ const mathOps = [OpCode7.Add, OpCode7.Sub, OpCode7.Mul, OpCode7.BitXor, OpCode7.BitAnd];
1580
+ const randomOp = mathOps[ctx.rng.nextRange(0, mathOps.length - 1)];
1581
+ deadCodeInsts.push({
1582
+ opcode: randomOp,
1583
+ operands: [
1584
+ { kind: OperandKind8.Register, value: junkReg1 },
1585
+ { kind: OperandKind8.Register, value: junkReg2 }
1586
+ ],
1587
+ result: junkReg3
1588
+ });
1589
+ } else if (patternChoice === 2) {
1590
+ deadCodeInsts.push({
1591
+ opcode: OpCode7.LoadConst,
1592
+ operands: [{ kind: OperandKind8.ConstantIndex, value: randCpIdx }],
1593
+ result: junkReg1
1594
+ });
1595
+ deadCodeInsts.push({
1596
+ opcode: OpCode7.Move,
1597
+ operands: [
1598
+ { kind: OperandKind8.Register, value: junkReg1 },
1599
+ { kind: OperandKind8.Register, value: junkReg2 }
1600
+ ]
1601
+ });
1602
+ deadCodeInsts.push({
1603
+ opcode: OpCode7.TypeOf,
1604
+ operands: [{ kind: OperandKind8.Register, value: junkReg2 }],
1605
+ result: junkReg3
1606
+ });
1607
+ deadCodeInsts.push({
1608
+ opcode: OpCode7.Move,
1609
+ operands: [
1610
+ { kind: OperandKind8.Register, value: junkReg3 },
1611
+ { kind: OperandKind8.Register, value: junkReg1 }
1612
+ ]
1613
+ });
1614
+ } else {
1615
+ deadCodeInsts.push({
1616
+ opcode: OpCode7.LoadConst,
1617
+ operands: [{ kind: OperandKind8.ConstantIndex, value: randCpIdx }],
1618
+ result: junkReg1
1619
+ });
1620
+ deadCodeInsts.push({
1621
+ opcode: OpCode7.LoadConst,
1622
+ operands: [{ kind: OperandKind8.ConstantIndex, value: ctx.rng.nextRange(0, cpMax) }],
1623
+ result: junkReg2
1624
+ });
1625
+ const compOps = [OpCode7.Lt, OpCode7.Gt, OpCode7.Eq, OpCode7.StrictEq, OpCode7.LtEq, OpCode7.GtEq];
1626
+ const compOp = compOps[ctx.rng.nextRange(0, compOps.length - 1)];
1627
+ deadCodeInsts.push({
1628
+ opcode: compOp,
1629
+ operands: [
1630
+ { kind: OperandKind8.Register, value: junkReg1 },
1631
+ { kind: OperandKind8.Register, value: junkReg2 }
1632
+ ],
1633
+ result: junkReg3
1634
+ });
1635
+ }
1636
+ if (ctx.rng.nextFloat() < 0.1) {
1637
+ let zeroIdx = newCP.findIndex((cp) => cp.kind === ConstantKind6.Number && cp.value === 0);
1638
+ if (zeroIdx === -1) {
1639
+ zeroIdx = newCP.length;
1640
+ newCP.push({ index: zeroIdx, kind: ConstantKind6.Number, value: 0 });
1641
+ }
1642
+ const randIdx = ctx.rng.nextRange(0, cpMax);
1643
+ const condReg = `r${maxReg + 12}`;
1644
+ currentInstructions.push({
1645
+ opcode: OpCode7.LoadConst,
1646
+ operands: [{ kind: OperandKind8.ConstantIndex, value: randIdx }],
1647
+ result: junkReg1
1648
+ });
1649
+ currentInstructions.push({
1650
+ opcode: OpCode7.Mul,
1651
+ operands: [
1652
+ { kind: OperandKind8.Register, value: junkReg1 },
1653
+ { kind: OperandKind8.Register, value: junkReg1 }
1654
+ ],
1655
+ result: junkReg2
1656
+ });
1657
+ currentInstructions.push({
1658
+ opcode: OpCode7.LoadConst,
1659
+ operands: [{ kind: OperandKind8.ConstantIndex, value: zeroIdx }],
1660
+ result: junkReg3
1661
+ });
1662
+ currentInstructions.push({
1663
+ opcode: OpCode7.Lt,
1664
+ operands: [
1665
+ { kind: OperandKind8.Register, value: junkReg2 },
1666
+ { kind: OperandKind8.Register, value: junkReg3 }
1667
+ ],
1668
+ result: condReg
1669
+ });
1670
+ const deadBlockId = `__dci_opaque_dead_${ctx.rng.identifier(6)}`;
1671
+ const nextBlockId = `__dci_opaque_next_${ctx.rng.identifier(6)}`;
1672
+ newBlocks.push({
1673
+ id: currentBlockId,
1674
+ label: currentLabel,
1675
+ instructions: currentInstructions,
1676
+ terminator: {
1677
+ kind: "branch",
1678
+ targets: [deadBlockId, nextBlockId],
1679
+ condition: condReg
1680
+ },
1681
+ predecessors: currentPredecessors,
1682
+ successors: [deadBlockId, nextBlockId],
1683
+ phiNodes: isFirstSubBlock ? block.phiNodes : []
1684
+ });
1685
+ isFirstSubBlock = false;
1686
+ newBlocks.push({
1687
+ id: deadBlockId,
1688
+ label: "opaque_dead",
1689
+ instructions: deadCodeInsts,
1690
+ terminator: { kind: "jump", targets: [nextBlockId] },
1691
+ predecessors: [currentBlockId],
1692
+ successors: [nextBlockId],
1693
+ phiNodes: []
1694
+ });
1695
+ const origBlockId = currentBlockId;
1696
+ currentBlockId = nextBlockId;
1697
+ currentLabel = `${currentLabel}_opaque`;
1698
+ currentInstructions = [];
1699
+ currentPredecessors = [origBlockId, deadBlockId];
1700
+ } else {
1701
+ currentInstructions.push(...deadCodeInsts);
1702
+ }
1703
+ }
1704
+ currentInstructions.push(inst);
1705
+ }
1706
+ newBlocks.push({
1707
+ id: currentBlockId,
1708
+ label: currentLabel,
1709
+ instructions: currentInstructions,
1710
+ terminator: block.terminator,
1711
+ predecessors: currentPredecessors,
1712
+ successors: block.successors,
1713
+ phiNodes: isFirstSubBlock ? block.phiNodes : []
1714
+ });
1715
+ }
1716
+ return changed ? { ...func, blocks: newBlocks } : func;
1717
+ });
1718
+ return {
1719
+ module: { ...ctx.module, functions: newFunctions, constantPool: newCP },
1720
+ symbolsRenamed: 0,
1721
+ nodesTransformed,
1722
+ diagnostics: []
1723
+ };
1724
+ }
1725
+ };
1726
+
1727
+ // src/passes/control-flow-flattening.ts
1728
+ import { OpCode as OpCode8, OperandKind as OperandKind9, ConstantKind as ConstantKind7 } from "@tsvm/shared";
1729
+ var ControlFlowFlatteningPass = class {
1730
+ name = "ControlFlowFlatteningPass";
1731
+ priority = 26;
1732
+ execute(ctx) {
1733
+ let nodesTransformed = 0;
1734
+ const constantPool = [...ctx.module.constantPool];
1735
+ const newFunctions = ctx.module.functions.map((func) => {
1736
+ if (!func.isVirtualized || func.blocks.length < 3) return func;
1737
+ const hasTryCatch = func.blocks.some(
1738
+ (block) => block.instructions.some((inst) => inst.opcode === OpCode8.TryCatchBegin || inst.opcode === OpCode8.TryCatchEnd)
1739
+ );
1740
+ if (hasTryCatch) return func;
1741
+ if (ctx.rng.nextFloat() < 0.4) return func;
1742
+ nodesTransformed++;
1743
+ const stateMap = /* @__PURE__ */ new Map();
1744
+ const blockOrder = ctx.rng.shuffle([...func.blocks]);
1745
+ for (const block of func.blocks) {
1746
+ stateMap.set(block.id, ctx.rng.nextRange(1e3, 2147483647));
1747
+ }
1748
+ const maxReg = getMaxRegister(func) - 1;
1749
+ const stateReg = `r${maxReg + 1}`;
1750
+ const tempReg = `r${maxReg + 2}`;
1751
+ const tableReg = `r${maxReg + 3}`;
1752
+ const primeReg = `r${maxReg + 4}`;
1753
+ const sizeReg = `r${maxReg + 5}`;
1754
+ const mulReg = `r${maxReg + 6}`;
1755
+ const idxReg = `r${maxReg + 7}`;
1756
+ const targetReg = `r${maxReg + 8}`;
1757
+ const constIdxReg = `r${maxReg + 9}`;
1758
+ const constLblReg = `r${maxReg + 10}`;
1759
+ const stateConstants = /* @__PURE__ */ new Map();
1760
+ for (const [blockId, stateId] of stateMap) {
1761
+ const cpIdx = constantPool.length;
1762
+ constantPool.push({ index: cpIdx, kind: ConstantKind7.Number, value: stateId });
1763
+ stateConstants.set(stateId, cpIdx);
1764
+ }
1765
+ const getOrAddNumberConstant = (val) => {
1766
+ let idx = constantPool.findIndex((c) => c.kind === ConstantKind7.Number && c.value === val);
1767
+ if (idx === -1) {
1768
+ idx = constantPool.length;
1769
+ constantPool.push({ index: idx, kind: ConstantKind7.Number, value: val });
1770
+ }
1771
+ return idx;
1772
+ };
1773
+ const dispatcherId = `__cff_dispatch_${ctx.rng.identifier(6)}`;
1774
+ const exitId = `__cff_exit_${ctx.rng.identifier(4)}`;
1775
+ const entryStateId = stateMap.get(func.blocks[0].id);
1776
+ const caseBlocks = [];
1777
+ for (const block of blockOrder) {
1778
+ const stateId = stateMap.get(block.id);
1779
+ const caseLabelId = `__cff_case_${block.id}_${ctx.rng.identifier(4)}`;
1780
+ const caseInstructions = [...block.instructions];
1781
+ let caseTerminator;
1782
+ if (block.terminator.kind === "return" || block.terminator.kind === "throw") {
1783
+ caseTerminator = block.terminator;
1784
+ } else if (block.terminator.kind === "jump") {
1785
+ const targetState = stateMap.get(block.terminator.targets[0]);
1786
+ caseInstructions.push({
1787
+ opcode: OpCode8.LoadConst,
1788
+ operands: [{ kind: OperandKind9.ConstantIndex, value: stateConstants.get(targetState) }],
1789
+ result: stateReg
1790
+ });
1791
+ caseTerminator = { kind: "jump", targets: [dispatcherId] };
1792
+ } else if (block.terminator.kind === "branch") {
1793
+ const trueState = stateMap.get(block.terminator.targets[0]);
1794
+ const falseState = stateMap.get(block.terminator.targets[1]);
1795
+ const trueBlockId = `__cff_br_t_${ctx.rng.identifier(4)}`;
1796
+ const falseBlockId = `__cff_br_f_${ctx.rng.identifier(4)}`;
1797
+ caseBlocks.push({
1798
+ id: trueBlockId,
1799
+ label: "cff_br_true",
1800
+ instructions: [
1801
+ {
1802
+ opcode: OpCode8.LoadConst,
1803
+ operands: [{ kind: OperandKind9.ConstantIndex, value: stateConstants.get(trueState) }],
1804
+ result: stateReg
1805
+ }
1806
+ ],
1807
+ terminator: { kind: "jump", targets: [dispatcherId] },
1808
+ predecessors: [caseLabelId],
1809
+ successors: [dispatcherId],
1810
+ phiNodes: []
1811
+ });
1812
+ caseBlocks.push({
1813
+ id: falseBlockId,
1814
+ label: "cff_br_false",
1815
+ instructions: [
1816
+ {
1817
+ opcode: OpCode8.LoadConst,
1818
+ operands: [{ kind: OperandKind9.ConstantIndex, value: stateConstants.get(falseState) }],
1819
+ result: stateReg
1820
+ }
1821
+ ],
1822
+ terminator: { kind: "jump", targets: [dispatcherId] },
1823
+ predecessors: [caseLabelId],
1824
+ successors: [dispatcherId],
1825
+ phiNodes: []
1826
+ });
1827
+ caseTerminator = {
1828
+ kind: "branch",
1829
+ targets: [trueBlockId, falseBlockId],
1830
+ condition: block.terminator.condition
1831
+ };
1832
+ } else {
1833
+ caseTerminator = block.terminator;
1834
+ }
1835
+ caseBlocks.push({
1836
+ id: caseLabelId,
1837
+ label: `cff_case_${block.id}`,
1838
+ instructions: caseInstructions,
1839
+ terminator: caseTerminator,
1840
+ predecessors: [dispatcherId],
1841
+ successors: caseTerminator.targets || [],
1842
+ phiNodes: block.phiNodes || []
1843
+ });
1844
+ }
1845
+ const orderedCases = [...caseBlocks.filter((b) => b.label?.startsWith("cff_case_"))];
1846
+ let tableSize = 1;
1847
+ while (tableSize < orderedCases.length) {
1848
+ tableSize *= 2;
1849
+ }
1850
+ const primes = [
1851
+ 31,
1852
+ 37,
1853
+ 41,
1854
+ 43,
1855
+ 47,
1856
+ 53,
1857
+ 59,
1858
+ 61,
1859
+ 67,
1860
+ 71,
1861
+ 73,
1862
+ 79,
1863
+ 83,
1864
+ 89,
1865
+ 97,
1866
+ 101,
1867
+ 103,
1868
+ 107,
1869
+ 109,
1870
+ 113,
1871
+ 127,
1872
+ 131,
1873
+ 137,
1874
+ 139,
1875
+ 149,
1876
+ 151,
1877
+ 157,
1878
+ 163,
1879
+ 167,
1880
+ 173,
1881
+ 179,
1882
+ 181,
1883
+ 191,
1884
+ 193,
1885
+ 197,
1886
+ 199,
1887
+ 211,
1888
+ 223,
1889
+ 227,
1890
+ 229,
1891
+ 233,
1892
+ 239,
1893
+ 241,
1894
+ 251,
1895
+ 257,
1896
+ 263,
1897
+ 269,
1898
+ 271,
1899
+ 277,
1900
+ 281,
1901
+ 283,
1902
+ 293,
1903
+ 307,
1904
+ 311,
1905
+ 313,
1906
+ 317,
1907
+ 331,
1908
+ 337,
1909
+ 347,
1910
+ 349,
1911
+ 353,
1912
+ 359,
1913
+ 367,
1914
+ 373,
1915
+ 379,
1916
+ 383,
1917
+ 389,
1918
+ 397
1919
+ ];
1920
+ let selectedPrime = 31;
1921
+ let collisionFree = false;
1922
+ while (!collisionFree) {
1923
+ for (const prime of primes) {
1924
+ const seen = /* @__PURE__ */ new Set();
1925
+ let collision = false;
1926
+ for (const block of orderedCases) {
1927
+ const stateId = stateMap.get(block.label.replace("cff_case_", ""));
1928
+ const idx = stateId * prime % tableSize;
1929
+ if (seen.has(idx)) {
1930
+ collision = true;
1931
+ break;
1932
+ }
1933
+ seen.add(idx);
1934
+ }
1935
+ if (!collision) {
1936
+ selectedPrime = prime;
1937
+ collisionFree = true;
1938
+ break;
1939
+ }
1940
+ }
1941
+ if (!collisionFree) {
1942
+ tableSize *= 2;
1943
+ }
1944
+ }
1945
+ const tableSlots = /* @__PURE__ */ new Map();
1946
+ for (const caseBlock of orderedCases) {
1947
+ const stateId = stateMap.get(caseBlock.label.replace("cff_case_", ""));
1948
+ const slotIdx = stateId * selectedPrime % tableSize;
1949
+ tableSlots.set(slotIdx, caseBlock.id);
1950
+ }
1951
+ const entryBlockId = `__cff_entry_${ctx.rng.identifier(4)}`;
1952
+ const entryBlockInstructions = [
1953
+ {
1954
+ opcode: OpCode8.LoadConst,
1955
+ operands: [{ kind: OperandKind9.ConstantIndex, value: stateConstants.get(entryStateId) }],
1956
+ result: stateReg
1957
+ },
1958
+ // Initialize jump table array
1959
+ {
1960
+ opcode: OpCode8.ArrayNew,
1961
+ operands: [],
1962
+ result: tableReg
1963
+ }
1964
+ ];
1965
+ for (let i = 0; i < tableSize; i++) {
1966
+ const targetBlockId = tableSlots.get(i) || exitId;
1967
+ const valIdx = getOrAddNumberConstant(i);
1968
+ entryBlockInstructions.push(
1969
+ {
1970
+ opcode: OpCode8.LoadConst,
1971
+ operands: [{ kind: OperandKind9.BlockLabel, value: targetBlockId }],
1972
+ result: constLblReg
1973
+ },
1974
+ {
1975
+ opcode: OpCode8.LoadConst,
1976
+ operands: [{ kind: OperandKind9.ConstantIndex, value: valIdx }],
1977
+ result: constIdxReg
1978
+ },
1979
+ {
1980
+ opcode: OpCode8.ComputedSet,
1981
+ operands: [
1982
+ { kind: OperandKind9.Register, value: tableReg },
1983
+ { kind: OperandKind9.Register, value: constIdxReg },
1984
+ { kind: OperandKind9.Register, value: constLblReg }
1985
+ ]
1986
+ }
1987
+ );
1988
+ }
1989
+ const entryBlock = {
1990
+ id: entryBlockId,
1991
+ label: "cff_entry",
1992
+ instructions: entryBlockInstructions,
1993
+ terminator: { kind: "jump", targets: [dispatcherId] },
1994
+ predecessors: [],
1995
+ successors: [dispatcherId],
1996
+ phiNodes: []
1997
+ };
1998
+ const dispatcherBlock = {
1999
+ id: dispatcherId,
2000
+ label: "cff_dispatcher",
2001
+ instructions: [
2002
+ // 1. Load prime
2003
+ {
2004
+ opcode: OpCode8.LoadConst,
2005
+ operands: [{ kind: OperandKind9.ConstantIndex, value: getOrAddNumberConstant(selectedPrime) }],
2006
+ result: primeReg
2007
+ },
2008
+ // 2. Mul: stateReg * primeReg
2009
+ {
2010
+ opcode: OpCode8.Mul,
2011
+ operands: [
2012
+ { kind: OperandKind9.Register, value: stateReg },
2013
+ { kind: OperandKind9.Register, value: primeReg }
2014
+ ],
2015
+ result: mulReg
2016
+ },
2017
+ // 3. Load table size
2018
+ {
2019
+ opcode: OpCode8.LoadConst,
2020
+ operands: [{ kind: OperandKind9.ConstantIndex, value: getOrAddNumberConstant(tableSize) }],
2021
+ result: sizeReg
2022
+ },
2023
+ // 4. Mod: mulReg % sizeReg
2024
+ {
2025
+ opcode: OpCode8.Mod,
2026
+ operands: [
2027
+ { kind: OperandKind9.Register, value: mulReg },
2028
+ { kind: OperandKind9.Register, value: sizeReg }
2029
+ ],
2030
+ result: idxReg
2031
+ },
2032
+ // 5. ComputedGet: tableReg[idxReg] -> targetReg
2033
+ {
2034
+ opcode: OpCode8.ComputedGet,
2035
+ operands: [
2036
+ { kind: OperandKind9.Register, value: tableReg },
2037
+ { kind: OperandKind9.Register, value: idxReg }
2038
+ ],
2039
+ result: targetReg
2040
+ },
2041
+ // 6. Jmp targetReg
2042
+ {
2043
+ opcode: OpCode8.Jmp,
2044
+ operands: [{ kind: OperandKind9.Register, value: targetReg }]
2045
+ }
2046
+ ],
2047
+ terminator: { kind: "dynamic_jmp", targets: [] },
2048
+ predecessors: [entryBlockId, ...caseBlocks.map((b) => b.id)],
2049
+ successors: [],
2050
+ phiNodes: []
2051
+ };
2052
+ const decoyBlocks = [];
2053
+ for (let i = 0; i < 2; i++) {
2054
+ const decoyCmpBlockId = `__cff_decoy_cmp_${i}_${ctx.rng.identifier(4)}`;
2055
+ const decoyCaseBlockId = `__cff_decoy_case_${i}_${ctx.rng.identifier(4)}`;
2056
+ const decoyNextBlockId = i === 0 ? `__cff_decoy_cmp_1_${ctx.rng.identifier(4)}` : exitId;
2057
+ const decoyStateId = ctx.rng.nextRange(1e3, 2147483647);
2058
+ const decoyStateIdx = getOrAddNumberConstant(decoyStateId);
2059
+ decoyBlocks.push(
2060
+ {
2061
+ id: decoyCmpBlockId,
2062
+ label: `cff_decoy_cmp_${i}`,
2063
+ instructions: [
2064
+ {
2065
+ opcode: OpCode8.LoadConst,
2066
+ operands: [{ kind: OperandKind9.ConstantIndex, value: decoyStateIdx }],
2067
+ result: tempReg
2068
+ },
2069
+ {
2070
+ opcode: OpCode8.Eq,
2071
+ operands: [
2072
+ { kind: OperandKind9.Register, value: stateReg },
2073
+ { kind: OperandKind9.Register, value: tempReg }
2074
+ ],
2075
+ result: tempReg
2076
+ }
2077
+ ],
2078
+ terminator: {
2079
+ kind: "branch",
2080
+ targets: [decoyCaseBlockId, decoyNextBlockId],
2081
+ condition: tempReg
2082
+ },
2083
+ predecessors: [],
2084
+ successors: [decoyCaseBlockId, decoyNextBlockId],
2085
+ phiNodes: []
2086
+ },
2087
+ {
2088
+ id: decoyCaseBlockId,
2089
+ label: `cff_decoy_case_${i}`,
2090
+ instructions: [
2091
+ {
2092
+ opcode: OpCode8.LoadConst,
2093
+ operands: [{ kind: OperandKind9.ConstantIndex, value: decoyStateIdx }],
2094
+ result: stateReg
2095
+ }
2096
+ ],
2097
+ terminator: {
2098
+ kind: "jump",
2099
+ targets: [dispatcherId]
2100
+ },
2101
+ predecessors: [decoyCmpBlockId],
2102
+ successors: [dispatcherId],
2103
+ phiNodes: []
2104
+ }
2105
+ );
2106
+ }
2107
+ const exitBlock = {
2108
+ id: exitId,
2109
+ label: "cff_exit",
2110
+ instructions: [],
2111
+ terminator: { kind: "return", targets: [] },
2112
+ predecessors: [],
2113
+ successors: [],
2114
+ phiNodes: []
2115
+ };
2116
+ const allBlocks = ctx.rng.shuffle([entryBlock, dispatcherBlock, ...decoyBlocks, ...caseBlocks, exitBlock]);
2117
+ const entryIdx = allBlocks.findIndex((b) => b.id === entryBlockId);
2118
+ if (entryIdx > 0) {
2119
+ [allBlocks[0], allBlocks[entryIdx]] = [allBlocks[entryIdx], allBlocks[0]];
2120
+ }
2121
+ return { ...func, blocks: allBlocks };
2122
+ });
2123
+ return {
2124
+ module: { ...ctx.module, functions: newFunctions, constantPool },
2125
+ symbolsRenamed: 0,
2126
+ nodesTransformed,
2127
+ diagnostics: []
2128
+ };
2129
+ }
2130
+ };
2131
+
2132
+ // src/passes/strip-debug.ts
2133
+ import { OpCode as OpCode9, ConstantKind as ConstantKind8 } from "@tsvm/shared";
2134
+ var StripDebugPass = class {
2135
+ name = "StripDebugPass";
2136
+ priority = 5;
2137
+ // Run very early
2138
+ execute(ctx) {
2139
+ let nodesTransformed = 0;
2140
+ const newFunctions = ctx.module.functions.map((func) => {
2141
+ if (!func.isVirtualized) return func;
2142
+ let changed = false;
2143
+ const newBlocks = func.blocks.map((block) => {
2144
+ const stringRegs = /* @__PURE__ */ new Map();
2145
+ const globalRegs = /* @__PURE__ */ new Map();
2146
+ const newInstructions = [];
2147
+ for (const inst of block.instructions) {
2148
+ if (inst.opcode === OpCode9.LoadConst) {
2149
+ const cpIdx = inst.operands[0].value;
2150
+ const cpEntry = ctx.module.constantPool.find((c) => c.index === cpIdx);
2151
+ if (cpEntry && cpEntry.kind === ConstantKind8.String) {
2152
+ stringRegs.set(inst.result, cpEntry.value);
2153
+ }
2154
+ } else if (inst.opcode === OpCode9.LoadGlobal) {
2155
+ const propReg = inst.operands[0].value;
2156
+ const propName = stringRegs.get(propReg);
2157
+ if (propName) {
2158
+ globalRegs.set(inst.result, propName);
2159
+ }
2160
+ } else if (inst.opcode === OpCode9.CallMethod) {
2161
+ const objReg = inst.operands[0].value;
2162
+ if (globalRegs.get(objReg) === "console") {
2163
+ changed = true;
2164
+ nodesTransformed++;
2165
+ if (inst.result) {
2166
+ newInstructions.push({
2167
+ ...inst,
2168
+ opcode: OpCode9.Nop,
2169
+ operands: [],
2170
+ result: void 0
2171
+ // drop result
2172
+ });
2173
+ } else {
2174
+ newInstructions.push({ ...inst, opcode: OpCode9.Nop, operands: [] });
2175
+ }
2176
+ continue;
2177
+ }
2178
+ }
2179
+ newInstructions.push(inst);
2180
+ }
2181
+ return changed ? { ...block, instructions: newInstructions } : block;
2182
+ });
2183
+ return changed ? { ...func, blocks: newBlocks } : func;
2184
+ });
2185
+ return {
2186
+ module: { ...ctx.module, functions: newFunctions },
2187
+ symbolsRenamed: 0,
2188
+ nodesTransformed,
2189
+ diagnostics: []
2190
+ };
2191
+ }
2192
+ };
2193
+
2194
+ // src/passes/ir-validation.ts
2195
+ import { DiagnosticSeverity, OperandKind as OperandKind10 } from "@tsvm/shared";
2196
+ function registerValue(val) {
2197
+ if (typeof val === "string" && /^r\d+$/.test(val)) return val;
2198
+ return void 0;
2199
+ }
2200
+ var IRValidationPass = class {
2201
+ name = "IRValidationPass";
2202
+ priority = 999;
2203
+ execute(ctx) {
2204
+ const diagnostics = [];
2205
+ let nodesTransformed = 0;
2206
+ for (const func of ctx.module.functions) {
2207
+ if (func.isVirtualized) {
2208
+ const fnDiagnostics = this.validateFunction(func, ctx.module);
2209
+ diagnostics.push(...fnDiagnostics);
2210
+ nodesTransformed += fnDiagnostics.length;
2211
+ }
2212
+ }
2213
+ return {
2214
+ module: ctx.module,
2215
+ symbolsRenamed: 0,
2216
+ nodesTransformed,
2217
+ diagnostics
2218
+ };
2219
+ }
2220
+ validateFunction(func, module) {
2221
+ const diagnostics = [];
2222
+ const blockIds = new Set(func.blocks.map((b) => b.id));
2223
+ const blockMap = new Map(func.blocks.map((b) => [b.id, b]));
2224
+ const maxReg = getMaxRegister(func);
2225
+ for (const block of func.blocks) {
2226
+ for (const pred of block.predecessors) {
2227
+ if (!blockIds.has(pred)) {
2228
+ diagnostics.push({
2229
+ severity: DiagnosticSeverity.Warning,
2230
+ code: "IR_INVALID_PREDECESSOR",
2231
+ message: `Block "${block.id}" has non-existent predecessor "${pred}"`
2232
+ });
2233
+ }
2234
+ }
2235
+ for (const succ of block.successors) {
2236
+ if (!blockIds.has(succ)) {
2237
+ diagnostics.push({
2238
+ severity: DiagnosticSeverity.Warning,
2239
+ code: "IR_INVALID_SUCCESSOR",
2240
+ message: `Block "${block.id}" has non-existent successor "${succ}"`
2241
+ });
2242
+ }
2243
+ }
2244
+ for (const target of block.terminator.targets) {
2245
+ if (!blockIds.has(target)) {
2246
+ diagnostics.push({
2247
+ severity: DiagnosticSeverity.Warning,
2248
+ code: "IR_INVALID_TARGET",
2249
+ message: `Block "${block.id}" references non-existent target block "${target}"`
2250
+ });
2251
+ }
2252
+ }
2253
+ for (const phi of block.phiNodes ?? []) {
2254
+ const regPhi = registerValue(phi.result);
2255
+ if (regPhi) {
2256
+ const idx = Number.parseInt(regPhi.substring(1), 10);
2257
+ if (idx >= maxReg) {
2258
+ diagnostics.push({
2259
+ severity: DiagnosticSeverity.Warning,
2260
+ code: "IR_PHI_REGISTER_BOUNDS",
2261
+ message: `Phi node result "${phi.result}" exceeds max register ${maxReg - 1} in function "${func.name}"`
2262
+ });
2263
+ }
2264
+ }
2265
+ for (const inc of phi.incoming) {
2266
+ if (!blockIds.has(inc.blockId)) {
2267
+ diagnostics.push({
2268
+ severity: DiagnosticSeverity.Warning,
2269
+ code: "IR_INVALID_PHI_BLOCK",
2270
+ message: `Phi node in "${block.id}" references non-existent incoming block "${inc.blockId}"`
2271
+ });
2272
+ }
2273
+ const regInc = registerValue(inc.register);
2274
+ if (regInc) {
2275
+ const idx = Number.parseInt(regInc.substring(1), 10);
2276
+ if (idx >= maxReg) {
2277
+ diagnostics.push({
2278
+ severity: DiagnosticSeverity.Warning,
2279
+ code: "IR_PHI_REGISTER_BOUNDS",
2280
+ message: `Phi node incoming register "${inc.register}" exceeds max register ${maxReg - 1} in function "${func.name}"`
2281
+ });
2282
+ }
2283
+ }
2284
+ }
2285
+ }
2286
+ for (const inst of block.instructions) {
2287
+ const regResult = registerValue(inst.result);
2288
+ if (regResult) {
2289
+ const idx = Number.parseInt(regResult.substring(1), 10);
2290
+ if (idx >= maxReg) {
2291
+ diagnostics.push({
2292
+ severity: DiagnosticSeverity.Warning,
2293
+ code: "IR_INSTRUCTION_REGISTER_BOUNDS",
2294
+ message: `Instruction result "${inst.result}" exceeds max register ${maxReg - 1} in function "${func.name}"`
2295
+ });
2296
+ }
2297
+ }
2298
+ for (const op of inst.operands) {
2299
+ if (op.kind === OperandKind10.Register) {
2300
+ const regVal = registerValue(op.value);
2301
+ if (regVal) {
2302
+ const idx = Number.parseInt(regVal.substring(1), 10);
2303
+ if (idx >= maxReg) {
2304
+ diagnostics.push({
2305
+ severity: DiagnosticSeverity.Warning,
2306
+ code: "IR_OPERAND_REGISTER_BOUNDS",
2307
+ message: `Operand "${op.value}" exceeds max register ${maxReg - 1} in function "${func.name}"`
2308
+ });
2309
+ }
2310
+ }
2311
+ }
2312
+ }
2313
+ }
2314
+ const regCond = registerValue(block.terminator.condition);
2315
+ if (regCond) {
2316
+ const idx = Number.parseInt(regCond.substring(1), 10);
2317
+ if (idx >= maxReg) {
2318
+ diagnostics.push({
2319
+ severity: DiagnosticSeverity.Warning,
2320
+ code: "IR_TERMINATOR_REGISTER_BOUNDS",
2321
+ message: `Terminator condition "${block.terminator.condition}" exceeds max register ${maxReg - 1} in function "${func.name}"`
2322
+ });
2323
+ }
2324
+ }
2325
+ const regRet = registerValue(block.terminator.returnValue);
2326
+ if (regRet) {
2327
+ const idx = Number.parseInt(regRet.substring(1), 10);
2328
+ if (idx >= maxReg) {
2329
+ diagnostics.push({
2330
+ severity: DiagnosticSeverity.Warning,
2331
+ code: "IR_TERMINATOR_REGISTER_BOUNDS",
2332
+ message: `Terminator returnValue "${block.terminator.returnValue}" exceeds max register ${maxReg - 1} in function "${func.name}"`
2333
+ });
2334
+ }
2335
+ }
2336
+ }
2337
+ for (const block of func.blocks) {
2338
+ for (const succ of block.successors) {
2339
+ const succBlock = blockMap.get(succ);
2340
+ if (succBlock && !succBlock.predecessors.includes(block.id)) {
2341
+ diagnostics.push({
2342
+ severity: DiagnosticSeverity.Warning,
2343
+ code: "IR_INCONSISTENT_EDGE",
2344
+ message: `Block "${block.id}" lists "${succ}" as successor but "${succ}" does not list it as predecessor`
2345
+ });
2346
+ }
2347
+ }
2348
+ for (const pred of block.predecessors) {
2349
+ const predBlock = blockMap.get(pred);
2350
+ if (predBlock && !predBlock.successors.includes(block.id)) {
2351
+ diagnostics.push({
2352
+ severity: DiagnosticSeverity.Warning,
2353
+ code: "IR_INCONSISTENT_EDGE",
2354
+ message: `Block "${block.id}" lists "${pred}" as predecessor but "${pred}" does not list it as successor`
2355
+ });
2356
+ }
2357
+ }
2358
+ }
2359
+ return diagnostics;
2360
+ }
2361
+ };
2362
+
2363
+ // src/passes/register-compacting.ts
2364
+ import { OperandKind as OperandKind11 } from "@tsvm/shared";
2365
+ function collectUsedRegisters(func) {
2366
+ const regs = /* @__PURE__ */ new Set();
2367
+ const add = (val) => {
2368
+ if (!val) return;
2369
+ const m = /^r(\d+)$/.exec(val);
2370
+ if (m) regs.add(Number.parseInt(m[1], 10));
2371
+ };
2372
+ for (const param of func.params) add(param.register);
2373
+ for (const local of func.locals) add(local.register);
2374
+ for (const block of func.blocks) {
2375
+ for (const phi of block.phiNodes ?? []) {
2376
+ add(phi.result);
2377
+ for (const inc of phi.incoming) add(inc.register);
2378
+ }
2379
+ for (const inst of block.instructions) {
2380
+ add(inst.result);
2381
+ for (const op of inst.operands) {
2382
+ if (op.kind === OperandKind11.Register) add(op.value);
2383
+ }
2384
+ }
2385
+ add(block.terminator.condition);
2386
+ add(block.terminator.returnValue);
2387
+ }
2388
+ return regs;
2389
+ }
2390
+ function buildCompactMap(usedRegs) {
2391
+ const sorted = Array.from(usedRegs).sort((a, b) => a - b);
2392
+ let hasGap = false;
2393
+ const map = /* @__PURE__ */ new Map();
2394
+ for (let i = 0; i < sorted.length; i++) {
2395
+ const oldReg = sorted[i];
2396
+ if (oldReg !== i) hasGap = true;
2397
+ map.set(`r${oldReg}`, `r${i}`);
2398
+ }
2399
+ return hasGap ? map : null;
2400
+ }
2401
+ function mapReg(val, map) {
2402
+ if (!val) return void 0;
2403
+ return map.get(val) ?? val;
2404
+ }
2405
+ function mapOperandValue(val, map) {
2406
+ if (typeof val === "string") return map.get(val) ?? val;
2407
+ return val;
2408
+ }
2409
+ var RegisterCompactingPass = class {
2410
+ name = "RegisterCompactingPass";
2411
+ priority = 90;
2412
+ execute(ctx) {
2413
+ const newFunctions = ctx.module.functions.map((func) => {
2414
+ if (!func.isVirtualized) return func;
2415
+ const usedRegs = collectUsedRegisters(func);
2416
+ const compactMap = buildCompactMap(usedRegs);
2417
+ if (!compactMap) return func;
2418
+ return this.rewriteRegisters(func, compactMap);
2419
+ });
2420
+ return {
2421
+ module: { ...ctx.module, functions: newFunctions },
2422
+ symbolsRenamed: 0,
2423
+ nodesTransformed: newFunctions.filter((f, i) => f !== ctx.module.functions[i]).length,
2424
+ diagnostics: []
2425
+ };
2426
+ }
2427
+ rewriteRegisters(func, map) {
2428
+ return {
2429
+ ...func,
2430
+ params: func.params.map((p) => ({
2431
+ ...p,
2432
+ register: mapReg(p.register, map)
2433
+ })),
2434
+ locals: func.locals.map((l) => ({
2435
+ ...l,
2436
+ register: mapReg(l.register, map)
2437
+ })),
2438
+ blocks: func.blocks.map((block) => ({
2439
+ ...block,
2440
+ phiNodes: (block.phiNodes ?? []).map((phi) => ({
2441
+ ...phi,
2442
+ result: mapReg(phi.result, map),
2443
+ incoming: phi.incoming.map((inc) => ({
2444
+ ...inc,
2445
+ register: mapReg(inc.register, map)
2446
+ }))
2447
+ })),
2448
+ instructions: block.instructions.map((inst) => ({
2449
+ ...inst,
2450
+ result: mapReg(inst.result, map),
2451
+ operands: inst.operands.map((op) => ({
2452
+ ...op,
2453
+ value: op.kind === OperandKind11.Register ? mapOperandValue(op.value, map) : op.value
2454
+ }))
2455
+ })),
2456
+ terminator: {
2457
+ ...block.terminator,
2458
+ condition: mapReg(block.terminator.condition, map),
2459
+ returnValue: mapReg(block.terminator.returnValue, map)
2460
+ }
2461
+ }))
2462
+ };
2463
+ }
2464
+ };
2465
+
2466
+ // src/passes/instruction-substitution.ts
2467
+ import { OpCode as OpCode10, OperandKind as OperandKind12, ConstantKind as ConstantKind9, IRType as IRType7 } from "@tsvm/shared";
2468
+ function inferRegisterTypes(blocks, params, locals, constantPool) {
2469
+ const types = /* @__PURE__ */ new Map();
2470
+ for (const param of params) {
2471
+ types.set(param.register, param.type);
2472
+ }
2473
+ for (const local of locals) {
2474
+ types.set(local.register, local.type);
2475
+ }
2476
+ const getConstantType = (idx) => {
2477
+ const entry = constantPool[idx];
2478
+ if (!entry) return IRType7.Any;
2479
+ switch (entry.kind) {
2480
+ case ConstantKind9.Number:
2481
+ return IRType7.Number;
2482
+ case ConstantKind9.String:
2483
+ case ConstantKind9.Template:
2484
+ return IRType7.String;
2485
+ case ConstantKind9.Boolean:
2486
+ return IRType7.Boolean;
2487
+ case ConstantKind9.Null:
2488
+ return IRType7.Null;
2489
+ case ConstantKind9.Undefined:
2490
+ return IRType7.Undefined;
2491
+ case ConstantKind9.BigInt:
2492
+ return IRType7.BigInt;
2493
+ default:
2494
+ return IRType7.Any;
2495
+ }
2496
+ };
2497
+ const getOperandType = (op) => {
2498
+ if (op.kind === OperandKind12.Register) {
2499
+ return types.get(op.value) ?? IRType7.Any;
2500
+ }
2501
+ if (op.kind === OperandKind12.ConstantIndex) {
2502
+ return getConstantType(op.value);
2503
+ }
2504
+ return IRType7.Any;
2505
+ };
2506
+ let changed = true;
2507
+ let iterations = 0;
2508
+ while (changed && iterations < 5) {
2509
+ changed = false;
2510
+ for (const block of blocks) {
2511
+ for (const inst of block.instructions) {
2512
+ if (!inst.result) continue;
2513
+ let newType = IRType7.Any;
2514
+ switch (inst.opcode) {
2515
+ case OpCode10.LoadConst: {
2516
+ const op = inst.operands[0];
2517
+ if (op && op.kind === OperandKind12.ConstantIndex) {
2518
+ newType = getConstantType(op.value);
2519
+ }
2520
+ break;
2521
+ }
2522
+ case OpCode10.Move: {
2523
+ const op = inst.operands[0];
2524
+ if (op) {
2525
+ newType = getOperandType(op);
2526
+ }
2527
+ break;
2528
+ }
2529
+ case OpCode10.Add: {
2530
+ const opA = inst.operands[0];
2531
+ const opB = inst.operands[1];
2532
+ if (opA && opB) {
2533
+ const typeA = getOperandType(opA);
2534
+ const typeB = getOperandType(opB);
2535
+ if (typeA === IRType7.Number && typeB === IRType7.Number) {
2536
+ newType = IRType7.Number;
2537
+ } else if (typeA === IRType7.String || typeB === IRType7.String) {
2538
+ newType = IRType7.String;
2539
+ } else {
2540
+ newType = IRType7.Any;
2541
+ }
2542
+ }
2543
+ break;
2544
+ }
2545
+ case OpCode10.Sub:
2546
+ case OpCode10.Mul:
2547
+ case OpCode10.Div:
2548
+ case OpCode10.Mod:
2549
+ case OpCode10.BitAnd:
2550
+ case OpCode10.BitOr:
2551
+ case OpCode10.BitXor:
2552
+ case OpCode10.Shl:
2553
+ case OpCode10.Shr:
2554
+ case OpCode10.UShr:
2555
+ case OpCode10.Neg: {
2556
+ newType = IRType7.Number;
2557
+ break;
2558
+ }
2559
+ case OpCode10.Lt:
2560
+ case OpCode10.Gt:
2561
+ case OpCode10.LtEq:
2562
+ case OpCode10.GtEq:
2563
+ case OpCode10.Eq:
2564
+ case OpCode10.StrictEq:
2565
+ case OpCode10.In:
2566
+ case OpCode10.InstanceOf:
2567
+ case OpCode10.Not: {
2568
+ newType = IRType7.Boolean;
2569
+ break;
2570
+ }
2571
+ case OpCode10.LoadLocal: {
2572
+ const op = inst.operands[0];
2573
+ if (op && op.kind === OperandKind12.Register) {
2574
+ newType = types.get(op.value) ?? IRType7.Any;
2575
+ }
2576
+ break;
2577
+ }
2578
+ default:
2579
+ newType = IRType7.Any;
2580
+ break;
2581
+ }
2582
+ const oldType = types.get(inst.result);
2583
+ if (oldType !== newType) {
2584
+ types.set(inst.result, newType);
2585
+ changed = true;
2586
+ }
2587
+ }
2588
+ }
2589
+ iterations++;
2590
+ }
2591
+ return types;
2592
+ }
2593
+ var InstructionSubstitutionPass = class {
2594
+ name = "InstructionSubstitutionPass";
2595
+ priority = 20;
2596
+ execute(ctx) {
2597
+ let nodesTransformed = 0;
2598
+ const constantPool = [...ctx.module.constantPool];
2599
+ const getOrAddNumberConstant = (val) => {
2600
+ let idx = constantPool.findIndex((c) => c.kind === ConstantKind9.Number && c.value === val);
2601
+ if (idx === -1) {
2602
+ idx = constantPool.length;
2603
+ constantPool.push({ index: idx, kind: ConstantKind9.Number, value: val });
2604
+ }
2605
+ return idx;
2606
+ };
2607
+ const newFunctions = ctx.module.functions.map((func) => {
2608
+ if (!func.isVirtualized) return func;
2609
+ const regTypes = inferRegisterTypes(func.blocks, func.params, func.locals, constantPool);
2610
+ let nextReg = getMaxRegister(func);
2611
+ let changed = false;
2612
+ const newBlocks = func.blocks.map((block) => {
2613
+ const newInstructions = [];
2614
+ for (const inst of block.instructions) {
2615
+ if (ctx.rng.nextFloat() >= 0.4) {
2616
+ newInstructions.push(inst);
2617
+ continue;
2618
+ }
2619
+ const getOperandType = (op) => {
2620
+ if (op.kind === OperandKind12.Register) {
2621
+ return regTypes.get(op.value) ?? IRType7.Any;
2622
+ }
2623
+ if (op.kind === OperandKind12.ConstantIndex) {
2624
+ const entry = constantPool[op.value];
2625
+ if (entry) {
2626
+ if (entry.kind === ConstantKind9.Number) return IRType7.Number;
2627
+ if (entry.kind === ConstantKind9.String || entry.kind === ConstantKind9.Template) return IRType7.String;
2628
+ }
2629
+ }
2630
+ return IRType7.Any;
2631
+ };
2632
+ if (inst.opcode === OpCode10.Add && inst.operands.length === 2 && inst.result) {
2633
+ const opA = inst.operands[0];
2634
+ const opB = inst.operands[1];
2635
+ const typeA = getOperandType(opA);
2636
+ const typeB = getOperandType(opB);
2637
+ if (typeA === IRType7.Number && typeB === IRType7.Number) {
2638
+ const temp1 = `r${nextReg++}`;
2639
+ const temp2 = `r${nextReg++}`;
2640
+ const tempConst2 = `r${nextReg++}`;
2641
+ const temp3 = `r${nextReg++}`;
2642
+ newInstructions.push(
2643
+ { opcode: OpCode10.BitXor, operands: [opA, opB], result: temp1 },
2644
+ { opcode: OpCode10.BitAnd, operands: [opA, opB], result: temp2 },
2645
+ {
2646
+ opcode: OpCode10.LoadConst,
2647
+ operands: [{ kind: OperandKind12.ConstantIndex, value: getOrAddNumberConstant(2) }],
2648
+ result: tempConst2
2649
+ },
2650
+ {
2651
+ opcode: OpCode10.Mul,
2652
+ operands: [
2653
+ { kind: OperandKind12.Register, value: temp2 },
2654
+ { kind: OperandKind12.Register, value: tempConst2 }
2655
+ ],
2656
+ result: temp3
2657
+ },
2658
+ {
2659
+ opcode: OpCode10.Add,
2660
+ operands: [
2661
+ { kind: OperandKind12.Register, value: temp1 },
2662
+ { kind: OperandKind12.Register, value: temp3 }
2663
+ ],
2664
+ result: inst.result
2665
+ }
2666
+ );
2667
+ nodesTransformed++;
2668
+ changed = true;
2669
+ } else {
2670
+ newInstructions.push(inst);
2671
+ }
2672
+ } else if (inst.opcode === OpCode10.BitAnd && inst.operands.length === 2 && inst.result) {
2673
+ const opA = inst.operands[0];
2674
+ const opB = inst.operands[1];
2675
+ const typeA = getOperandType(opA);
2676
+ const typeB = getOperandType(opB);
2677
+ if (typeA === IRType7.Number && typeB === IRType7.Number) {
2678
+ const temp1 = `r${nextReg++}`;
2679
+ const temp2 = `r${nextReg++}`;
2680
+ newInstructions.push(
2681
+ { opcode: OpCode10.BitOr, operands: [opA, opB], result: temp1 },
2682
+ { opcode: OpCode10.BitXor, operands: [opA, opB], result: temp2 },
2683
+ {
2684
+ opcode: OpCode10.Sub,
2685
+ operands: [
2686
+ { kind: OperandKind12.Register, value: temp1 },
2687
+ { kind: OperandKind12.Register, value: temp2 }
2688
+ ],
2689
+ result: inst.result
2690
+ }
2691
+ );
2692
+ nodesTransformed++;
2693
+ changed = true;
2694
+ } else {
2695
+ newInstructions.push(inst);
2696
+ }
2697
+ } else if (inst.opcode === OpCode10.BitOr && inst.operands.length === 2 && inst.result) {
2698
+ const opA = inst.operands[0];
2699
+ const opB = inst.operands[1];
2700
+ const typeA = getOperandType(opA);
2701
+ const typeB = getOperandType(opB);
2702
+ if (typeA === IRType7.Number && typeB === IRType7.Number) {
2703
+ const temp1 = `r${nextReg++}`;
2704
+ const temp2 = `r${nextReg++}`;
2705
+ newInstructions.push(
2706
+ { opcode: OpCode10.BitAnd, operands: [opA, opB], result: temp1 },
2707
+ { opcode: OpCode10.BitXor, operands: [opA, opB], result: temp2 },
2708
+ {
2709
+ opcode: OpCode10.Add,
2710
+ operands: [
2711
+ { kind: OperandKind12.Register, value: temp1 },
2712
+ { kind: OperandKind12.Register, value: temp2 }
2713
+ ],
2714
+ result: inst.result
2715
+ }
2716
+ );
2717
+ nodesTransformed++;
2718
+ changed = true;
2719
+ } else {
2720
+ newInstructions.push(inst);
2721
+ }
2722
+ } else if (inst.opcode === OpCode10.BitXor && inst.operands.length === 2 && inst.result) {
2723
+ const opA = inst.operands[0];
2724
+ const opB = inst.operands[1];
2725
+ const typeA = getOperandType(opA);
2726
+ const typeB = getOperandType(opB);
2727
+ if (typeA === IRType7.Number && typeB === IRType7.Number) {
2728
+ const temp1 = `r${nextReg++}`;
2729
+ const temp2 = `r${nextReg++}`;
2730
+ newInstructions.push(
2731
+ { opcode: OpCode10.BitOr, operands: [opA, opB], result: temp1 },
2732
+ { opcode: OpCode10.BitAnd, operands: [opA, opB], result: temp2 },
2733
+ {
2734
+ opcode: OpCode10.Sub,
2735
+ operands: [
2736
+ { kind: OperandKind12.Register, value: temp1 },
2737
+ { kind: OperandKind12.Register, value: temp2 }
2738
+ ],
2739
+ result: inst.result
2740
+ }
2741
+ );
2742
+ nodesTransformed++;
2743
+ changed = true;
2744
+ } else {
2745
+ newInstructions.push(inst);
2746
+ }
2747
+ } else if (inst.opcode === OpCode10.Neg && inst.operands.length === 1 && inst.result) {
2748
+ const opA = inst.operands[0];
2749
+ const typeA = getOperandType(opA);
2750
+ if (typeA === IRType7.Number) {
2751
+ const tempConstNeg1 = `r${nextReg++}`;
2752
+ newInstructions.push(
2753
+ {
2754
+ opcode: OpCode10.LoadConst,
2755
+ operands: [{ kind: OperandKind12.ConstantIndex, value: getOrAddNumberConstant(-1) }],
2756
+ result: tempConstNeg1
2757
+ },
2758
+ {
2759
+ opcode: OpCode10.Mul,
2760
+ operands: [opA, { kind: OperandKind12.Register, value: tempConstNeg1 }],
2761
+ result: inst.result
2762
+ }
2763
+ );
2764
+ nodesTransformed++;
2765
+ changed = true;
2766
+ } else {
2767
+ newInstructions.push(inst);
2768
+ }
2769
+ } else {
2770
+ newInstructions.push(inst);
2771
+ }
2772
+ }
2773
+ return { ...block, instructions: newInstructions };
2774
+ });
2775
+ if (!changed) return func;
2776
+ const newLocals = [...func.locals];
2777
+ const maxOriginalReg = getMaxRegister(func);
2778
+ for (let reg = maxOriginalReg; reg < nextReg; reg++) {
2779
+ newLocals.push({
2780
+ name: `isub_temp_r${reg}`,
2781
+ register: `r${reg}`,
2782
+ type: IRType7.Any,
2783
+ isCaptured: false
2784
+ });
2785
+ }
2786
+ return { ...func, blocks: newBlocks, locals: newLocals };
2787
+ });
2788
+ return {
2789
+ module: { ...ctx.module, functions: newFunctions, constantPool },
2790
+ symbolsRenamed: 0,
2791
+ nodesTransformed,
2792
+ diagnostics: []
2793
+ };
2794
+ }
2795
+ };
2796
+
2797
+ // src/registry.ts
2798
+ var TransformRegistry = class {
2799
+ passes = /* @__PURE__ */ new Map();
2800
+ constructor(profile) {
2801
+ const availablePasses = {
2802
+ PreserveTypeIllusionsPass,
2803
+ GenericConfusionPass,
2804
+ DecoratorAwareLoweringPass,
2805
+ NamespaceVirtualizationPass,
2806
+ TypeLevelFakePathPass,
2807
+ SymbolIndirectionPass,
2808
+ StringPoolEncodingPass,
2809
+ FunctionVirtualizationPass,
2810
+ DeadCodeInjectionPass,
2811
+ ControlFlowFlatteningPass,
2812
+ StripDebugPass,
2813
+ IRValidationPass,
2814
+ RegisterCompactingPass,
2815
+ InstructionSubstitutionPass
2816
+ };
2817
+ for (const passConfig of profile.transforms) {
2818
+ if (passConfig.enabled && availablePasses[passConfig.name]) {
2819
+ this.addPass(new availablePasses[passConfig.name]());
2820
+ }
2821
+ }
2822
+ }
2823
+ addPass(pass) {
2824
+ this.passes.set(pass.name, pass);
2825
+ }
2826
+ removePass(name) {
2827
+ this.passes.delete(name);
2828
+ }
2829
+ getPass(name) {
2830
+ return this.passes.get(name);
2831
+ }
2832
+ getOrderedPasses() {
2833
+ return Array.from(this.passes.values()).sort((a, b) => a.priority - b.priority);
2834
+ }
2835
+ };
2836
+ function createTransformRegistry(profile) {
2837
+ return new TransformRegistry(profile);
2838
+ }
2839
+ export {
2840
+ ControlFlowFlatteningPass,
2841
+ DeadCodeInjectionPass,
2842
+ DecoratorAwareLoweringPass,
2843
+ FunctionVirtualizationPass,
2844
+ GenericConfusionPass,
2845
+ InstructionSubstitutionPass,
2846
+ NamespaceVirtualizationPass,
2847
+ PreserveTypeIllusionsPass,
2848
+ StringPoolEncodingPass,
2849
+ StripDebugPass,
2850
+ SymbolIndirectionPass,
2851
+ TransformRegistry,
2852
+ TypeLevelFakePathPass,
2853
+ createTransformRegistry
2854
+ };