js-confuser-vm 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -2
- package/README.MD +186 -107
- package/dist/build-runtime.js +7 -1
- package/dist/compiler.js +801 -785
- package/dist/runtime.js +409 -332
- package/dist/transforms/bytecode/aliasedOpcodes.js +140 -0
- package/dist/transforms/bytecode/concealConstants.js +31 -0
- package/dist/transforms/bytecode/macroOpcodes.js +22 -10
- package/dist/transforms/bytecode/resolveContants.js +73 -10
- package/dist/transforms/bytecode/selfModifying.js +3 -2
- package/dist/transforms/bytecode/specializedOpcodes.js +38 -28
- package/dist/transforms/runtime/aliasedOpcodes.js +134 -0
- package/dist/transforms/runtime/shuffleOpcodes.js +1 -1
- package/dist/transforms/runtime/specializedOpcodes.js +21 -16
- package/dist/utils/op-utils.js +29 -0
- package/dist/utils/random-utils.js +27 -0
- package/index.ts +10 -8
- package/jest.config.js +10 -0
- package/package.json +1 -1
- package/src/build-runtime.ts +7 -1
- package/src/compiler.ts +2395 -2069
- package/src/options.ts +2 -0
- package/src/runtime.ts +838 -771
- package/src/transforms/bytecode/aliasedOpcodes.ts +158 -0
- package/src/transforms/bytecode/concealConstants.ts +52 -0
- package/src/transforms/bytecode/macroOpcodes.ts +32 -15
- package/src/transforms/bytecode/resolveContants.ts +87 -16
- package/src/transforms/bytecode/selfModifying.ts +3 -3
- package/src/transforms/bytecode/specializedOpcodes.ts +58 -29
- package/src/transforms/runtime/aliasedOpcodes.ts +191 -0
- package/src/transforms/runtime/shuffleOpcodes.ts +1 -1
- package/src/transforms/runtime/specializedOpcodes.ts +39 -24
- package/src/{transforms/utils → utils}/op-utils.ts +7 -0
- /package/src/{transforms/utils → utils}/random-utils.ts +0 -0
package/src/runtime.ts
CHANGED
|
@@ -1,771 +1,838 @@
|
|
|
1
|
-
import { OP_ORIGINAL as OP } from "./compiler.ts";
|
|
2
|
-
const BYTECODE = [];
|
|
3
|
-
const MAIN_START_PC = 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.
|
|
61
|
-
this.
|
|
62
|
-
this.
|
|
63
|
-
this.
|
|
64
|
-
this.
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
VM.prototype.
|
|
90
|
-
return this.
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
VM.prototype.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
case OP.
|
|
218
|
-
var
|
|
219
|
-
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
this.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
case OP.
|
|
243
|
-
|
|
244
|
-
this.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
var
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
var
|
|
266
|
-
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
var
|
|
286
|
-
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
var
|
|
292
|
-
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
var
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
this.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
case OP.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
var
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
var
|
|
389
|
-
var
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
break;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
var
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
this.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
break;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
case OP.
|
|
502
|
-
|
|
503
|
-
var
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
this.
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
this.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
var
|
|
639
|
-
var
|
|
640
|
-
|
|
641
|
-
for (var
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
case OP.
|
|
648
|
-
//
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
//
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1
|
+
import { OP_ORIGINAL as OP } from "./compiler.ts";
|
|
2
|
+
const BYTECODE = [];
|
|
3
|
+
const MAIN_START_PC = 0;
|
|
4
|
+
const MAIN_REG_COUNT = 0;
|
|
5
|
+
const CONSTANTS = [];
|
|
6
|
+
const ENCODE_BYTECODE = false;
|
|
7
|
+
const TIMING_CHECKS = false;
|
|
8
|
+
// The text above is not included in the compiled output - for type intellisense only
|
|
9
|
+
// @START
|
|
10
|
+
|
|
11
|
+
function decodeBytecode(s) {
|
|
12
|
+
if (!ENCODE_BYTECODE) return s;
|
|
13
|
+
|
|
14
|
+
var b =
|
|
15
|
+
typeof Buffer !== "undefined"
|
|
16
|
+
? Buffer.from(s, "base64")
|
|
17
|
+
: Uint8Array.from(atob(s), function (c) {
|
|
18
|
+
return c.charCodeAt(0);
|
|
19
|
+
});
|
|
20
|
+
// Each slot is a u16 stored as 2 little-endian bytes.
|
|
21
|
+
var r = new Uint16Array(b.length / 2);
|
|
22
|
+
for (var i = 0; i < r.length; i++) r[i] = b[i * 2] | (b[i * 2 + 1] << 8);
|
|
23
|
+
return r;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Closure symbol
|
|
27
|
+
// Used to tag shell functions so the VM can fast-path back to the
|
|
28
|
+
// inner Closure instead of going through a sub-VM on internal calls.
|
|
29
|
+
var CLOSURE_SYM = Symbol(); // Nameless for obfuscation
|
|
30
|
+
|
|
31
|
+
// Upvalue
|
|
32
|
+
// While the outer frame is alive: reads/writes go to frame.regs[slot].
|
|
33
|
+
// After the outer frame returns (closed): reads/writes hit this._value.
|
|
34
|
+
function Upvalue(frame, slot) {
|
|
35
|
+
this._frame = frame;
|
|
36
|
+
this._slot = slot;
|
|
37
|
+
this._closed = false;
|
|
38
|
+
this._value = undefined;
|
|
39
|
+
}
|
|
40
|
+
Upvalue.prototype._read = function () {
|
|
41
|
+
return this._closed ? this._value : this._frame.regs[this._slot];
|
|
42
|
+
};
|
|
43
|
+
Upvalue.prototype._write = function (v) {
|
|
44
|
+
if (this._closed) this._value = v;
|
|
45
|
+
else this._frame.regs[this._slot] = v;
|
|
46
|
+
};
|
|
47
|
+
Upvalue.prototype._close = function () {
|
|
48
|
+
this._value = this._frame.regs[this._slot];
|
|
49
|
+
this._closed = true;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Closure & Frame
|
|
53
|
+
function Closure(fn) {
|
|
54
|
+
this.fn = fn;
|
|
55
|
+
this.upvalues = [];
|
|
56
|
+
this.prototype = {}; // <- default prototype object for `new`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Frame(closure, returnPc, parent, thisVal, retDstReg) {
|
|
60
|
+
this.closure = closure;
|
|
61
|
+
this.regs = new Array(closure.fn.regCount).fill(undefined);
|
|
62
|
+
this._pc = closure.fn.startPc; // <- initialize from fn descriptor
|
|
63
|
+
this._returnPc = returnPc; // pc to resume in parent frame after RETURN
|
|
64
|
+
this._parent = parent;
|
|
65
|
+
this.thisVal = thisVal !== undefined ? thisVal : undefined;
|
|
66
|
+
this._retDstReg = retDstReg !== undefined ? retDstReg : 0; // register in parent to write return value
|
|
67
|
+
this._newObj = null; // <- set by NEW so RETURN can see it
|
|
68
|
+
this._handlerStack = []; // <- exception handlers pushed by TRY_SETUP
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// VM
|
|
72
|
+
function VM(bytecode, mainStartPc, mainRegCount, constants, globals) {
|
|
73
|
+
this.bytecode = bytecode;
|
|
74
|
+
this.constants = constants;
|
|
75
|
+
this.globals = globals;
|
|
76
|
+
this._frameStack = [];
|
|
77
|
+
this._openUpvalues = []; // all currently open Upvalue objects across all frames
|
|
78
|
+
|
|
79
|
+
var mainFn = {
|
|
80
|
+
paramCount: 0,
|
|
81
|
+
regCount: mainRegCount,
|
|
82
|
+
startPc: mainStartPc, // <- where main begins
|
|
83
|
+
};
|
|
84
|
+
this._currentFrame = new Frame(new Closure(mainFn), null, null, undefined, 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Consume the next slot from the flat bytecode stream and advance the PC.
|
|
88
|
+
// Called by opcode handlers to read each of their operands in order.
|
|
89
|
+
VM.prototype._operand = function () {
|
|
90
|
+
return this.bytecode[this._currentFrame._pc++];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
VM.prototype.captureUpvalue = function (frame, slot) {
|
|
94
|
+
// Reuse existing open upvalue for this frame+slot if one exists.
|
|
95
|
+
// This is what makes two closures share the same mutable cell.
|
|
96
|
+
for (var i = 0; i < this._openUpvalues.length; i++) {
|
|
97
|
+
var uv = this._openUpvalues[i];
|
|
98
|
+
if (uv._frame === frame && uv._slot === slot) return uv;
|
|
99
|
+
}
|
|
100
|
+
var uv = new Upvalue(frame, slot);
|
|
101
|
+
this._openUpvalues.push(uv);
|
|
102
|
+
return uv;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Reads and decodes a constant from the pool.
|
|
106
|
+
// idx — pool index (first operand of the constant pair emitted by resolveConstants).
|
|
107
|
+
// key — conceal key (second operand). 0 means no concealment.
|
|
108
|
+
//
|
|
109
|
+
// For integers: stored value is (original ^ key); XOR again to recover.
|
|
110
|
+
// For strings: stored value is a base64 string containing u16 LE byte pairs.
|
|
111
|
+
// Mirrors decodeBytecode: base64 → bytes → u16 LE → XOR with
|
|
112
|
+
// (key + i) & 0xFFFF to recover the original char codes.
|
|
113
|
+
// idxIn, keyIn are passed in from specializedOpcodes when the operands are determined at compile time.
|
|
114
|
+
VM.prototype._constant = function (idxIn, keyIn) {
|
|
115
|
+
var idx = idxIn ?? this._operand();
|
|
116
|
+
var key = keyIn ?? this._operand();
|
|
117
|
+
|
|
118
|
+
var v = this.constants[idx];
|
|
119
|
+
if (!key) return v;
|
|
120
|
+
if (typeof v === "number") return v ^ key;
|
|
121
|
+
// String: base64-decode to u16 LE byte pairs, then XOR each code with (key+i).
|
|
122
|
+
var b =
|
|
123
|
+
typeof Buffer !== "undefined"
|
|
124
|
+
? Buffer.from(v, "base64")
|
|
125
|
+
: Uint8Array.from(atob(v), function (c) {
|
|
126
|
+
return c.charCodeAt(0);
|
|
127
|
+
});
|
|
128
|
+
var out = "";
|
|
129
|
+
for (var i = 0; i < b.length / 2; i++) {
|
|
130
|
+
var code = b[i * 2] | (b[i * 2 + 1] << 8); // u16 LE
|
|
131
|
+
out += String.fromCharCode(code ^ ((key + i) & 0xffff));
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
VM.prototype._closeUpvaluesFor = function (frame) {
|
|
137
|
+
// Called on RETURN - close every upvalue that was pointing into this frame.
|
|
138
|
+
// After this, closures that captured from the frame read from upvalue.value.
|
|
139
|
+
this._openUpvalues = this._openUpvalues.filter(function (uv) {
|
|
140
|
+
if (uv._frame === frame) {
|
|
141
|
+
uv._close();
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
VM.prototype.run = function () {
|
|
149
|
+
var now = () => {
|
|
150
|
+
return performance.now();
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
var lastTime = now();
|
|
154
|
+
|
|
155
|
+
while (true) {
|
|
156
|
+
var frame = this._currentFrame;
|
|
157
|
+
var bc = this.bytecode;
|
|
158
|
+
if (frame._pc >= bc.length) break;
|
|
159
|
+
|
|
160
|
+
var pc = frame._pc++;
|
|
161
|
+
var op = this.bytecode[pc];
|
|
162
|
+
var opcode = this.bytecode[pc];
|
|
163
|
+
// console.log(
|
|
164
|
+
// "pc=" + pc,
|
|
165
|
+
// "opcode=" + opcode,
|
|
166
|
+
// Object.keys(OP).find((key) => OP[key] === opcode),
|
|
167
|
+
// );
|
|
168
|
+
|
|
169
|
+
// Debugging protection: Detects debugger by checking for >1s pauses which can only happen from debugger; or extremely slow sync tasks
|
|
170
|
+
if (TIMING_CHECKS) {
|
|
171
|
+
var currentTime = now();
|
|
172
|
+
var isTamper = currentTime - lastTime > 1000;
|
|
173
|
+
lastTime = currentTime;
|
|
174
|
+
if (isTamper) {
|
|
175
|
+
// Poison the bytecode
|
|
176
|
+
for (var i = 0; i < this.bytecode.length; i++) this.bytecode[i] = 0;
|
|
177
|
+
// Break the current state
|
|
178
|
+
frame.regs.fill(undefined);
|
|
179
|
+
op = OP.JUMP;
|
|
180
|
+
frame._pc = this.bytecode.length; // jump past end to halt
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
/* @SWITCH */
|
|
186
|
+
switch (op) {
|
|
187
|
+
case OP.LOAD_CONST: {
|
|
188
|
+
var dst = this._operand();
|
|
189
|
+
frame.regs[dst] = this._constant();
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case OP.LOAD_INT: {
|
|
194
|
+
var dst = this._operand();
|
|
195
|
+
frame.regs[dst] = this._operand();
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case OP.LOAD_GLOBAL: {
|
|
200
|
+
var dst = this._operand();
|
|
201
|
+
var globalName = this._constant();
|
|
202
|
+
|
|
203
|
+
if (!(globalName in this.globals)) {
|
|
204
|
+
throw new ReferenceError(`${globalName} is not defined`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
frame.regs[dst] = this.globals[globalName];
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
case OP.LOAD_UPVALUE: {
|
|
212
|
+
var dst = this._operand();
|
|
213
|
+
frame.regs[dst] = frame.closure.upvalues[this._operand()]._read();
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
case OP.LOAD_THIS: {
|
|
218
|
+
var dst = this._operand();
|
|
219
|
+
frame.regs[dst] = frame.thisVal;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
case OP.MOVE: {
|
|
224
|
+
var dst = this._operand();
|
|
225
|
+
frame.regs[dst] = frame.regs[this._operand()];
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
case OP.STORE_GLOBAL: {
|
|
230
|
+
// nameIdx and key are consumed inline so the concealConstants runtime
|
|
231
|
+
// transform can rewrite this._constant() consistently.
|
|
232
|
+
this.globals[this._constant()] = frame.regs[this._operand()];
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case OP.STORE_UPVALUE: {
|
|
237
|
+
var uvIdx = this._operand();
|
|
238
|
+
frame.closure.upvalues[uvIdx]._write(frame.regs[this._operand()]);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
case OP.GET_PROP: {
|
|
243
|
+
// dst = regs[obj][regs[key]]
|
|
244
|
+
var dst = this._operand();
|
|
245
|
+
var obj = frame.regs[this._operand()];
|
|
246
|
+
var key = frame.regs[this._operand()];
|
|
247
|
+
frame.regs[dst] = obj[key];
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
case OP.SET_PROP: {
|
|
252
|
+
// regs[obj][regs[key]] = regs[val]
|
|
253
|
+
var obj = frame.regs[this._operand()];
|
|
254
|
+
var key = frame.regs[this._operand()];
|
|
255
|
+
var val = frame.regs[this._operand()];
|
|
256
|
+
// Reflect.set performs [[Set]] without throwing on failure,
|
|
257
|
+
// correctly simulating sloppy-mode assignment from a strict-mode host.
|
|
258
|
+
Reflect.set(obj, key, val);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
case OP.DELETE_PROP: {
|
|
263
|
+
var dst = this._operand();
|
|
264
|
+
var obj = frame.regs[this._operand()];
|
|
265
|
+
var key = frame.regs[this._operand()];
|
|
266
|
+
frame.regs[dst] = delete obj[key];
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ── Arithmetic (dst, src1, src2) ────────────────────────────────────
|
|
271
|
+
case OP.ADD: {
|
|
272
|
+
var dst = this._operand();
|
|
273
|
+
var a = frame.regs[this._operand()];
|
|
274
|
+
frame.regs[dst] = a + frame.regs[this._operand()];
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
case OP.SUB: {
|
|
278
|
+
var dst = this._operand();
|
|
279
|
+
var a = frame.regs[this._operand()];
|
|
280
|
+
frame.regs[dst] = a - frame.regs[this._operand()];
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
case OP.MUL: {
|
|
284
|
+
var dst = this._operand();
|
|
285
|
+
var a = frame.regs[this._operand()];
|
|
286
|
+
frame.regs[dst] = a * frame.regs[this._operand()];
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case OP.DIV: {
|
|
290
|
+
var dst = this._operand();
|
|
291
|
+
var a = frame.regs[this._operand()];
|
|
292
|
+
frame.regs[dst] = a / frame.regs[this._operand()];
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
case OP.MOD: {
|
|
296
|
+
var dst = this._operand();
|
|
297
|
+
var a = frame.regs[this._operand()];
|
|
298
|
+
frame.regs[dst] = a % frame.regs[this._operand()];
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
case OP.BAND: {
|
|
302
|
+
var dst = this._operand();
|
|
303
|
+
var a = frame.regs[this._operand()];
|
|
304
|
+
frame.regs[dst] = a & frame.regs[this._operand()];
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case OP.BOR: {
|
|
308
|
+
var dst = this._operand();
|
|
309
|
+
var a = frame.regs[this._operand()];
|
|
310
|
+
frame.regs[dst] = a | frame.regs[this._operand()];
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
case OP.BXOR: {
|
|
314
|
+
var dst = this._operand();
|
|
315
|
+
var a = frame.regs[this._operand()];
|
|
316
|
+
frame.regs[dst] = a ^ frame.regs[this._operand()];
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case OP.SHL: {
|
|
320
|
+
var dst = this._operand();
|
|
321
|
+
var a = frame.regs[this._operand()];
|
|
322
|
+
frame.regs[dst] = a << frame.regs[this._operand()];
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case OP.SHR: {
|
|
326
|
+
var dst = this._operand();
|
|
327
|
+
var a = frame.regs[this._operand()];
|
|
328
|
+
frame.regs[dst] = a >> frame.regs[this._operand()];
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case OP.USHR: {
|
|
332
|
+
var dst = this._operand();
|
|
333
|
+
var a = frame.regs[this._operand()];
|
|
334
|
+
frame.regs[dst] = a >>> frame.regs[this._operand()];
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ── Comparison (dst, src1, src2) ─────────────────────────────────────
|
|
339
|
+
case OP.LT: {
|
|
340
|
+
var dst = this._operand();
|
|
341
|
+
var a = frame.regs[this._operand()];
|
|
342
|
+
frame.regs[dst] = a < frame.regs[this._operand()];
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
case OP.GT: {
|
|
346
|
+
var dst = this._operand();
|
|
347
|
+
var a = frame.regs[this._operand()];
|
|
348
|
+
frame.regs[dst] = a > frame.regs[this._operand()];
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case OP.LTE: {
|
|
352
|
+
var dst = this._operand();
|
|
353
|
+
var a = frame.regs[this._operand()];
|
|
354
|
+
frame.regs[dst] = a <= frame.regs[this._operand()];
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case OP.GTE: {
|
|
358
|
+
var dst = this._operand();
|
|
359
|
+
var a = frame.regs[this._operand()];
|
|
360
|
+
frame.regs[dst] = a >= frame.regs[this._operand()];
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case OP.EQ: {
|
|
364
|
+
var dst = this._operand();
|
|
365
|
+
var a = frame.regs[this._operand()];
|
|
366
|
+
frame.regs[dst] = a === frame.regs[this._operand()];
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case OP.NEQ: {
|
|
370
|
+
var dst = this._operand();
|
|
371
|
+
var a = frame.regs[this._operand()];
|
|
372
|
+
frame.regs[dst] = a !== frame.regs[this._operand()];
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case OP.LOOSE_EQ: {
|
|
376
|
+
var dst = this._operand();
|
|
377
|
+
var a = frame.regs[this._operand()];
|
|
378
|
+
frame.regs[dst] = a == frame.regs[this._operand()];
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case OP.LOOSE_NEQ: {
|
|
382
|
+
var dst = this._operand();
|
|
383
|
+
var a = frame.regs[this._operand()];
|
|
384
|
+
frame.regs[dst] = a != frame.regs[this._operand()];
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case OP.IN: {
|
|
388
|
+
var dst = this._operand();
|
|
389
|
+
var a = frame.regs[this._operand()];
|
|
390
|
+
frame.regs[dst] = a in frame.regs[this._operand()];
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
case OP.INSTANCEOF: {
|
|
394
|
+
var dst = this._operand();
|
|
395
|
+
var obj = frame.regs[this._operand()];
|
|
396
|
+
var ctor = frame.regs[this._operand()];
|
|
397
|
+
if (typeof ctor === "function") {
|
|
398
|
+
frame.regs[dst] = obj instanceof ctor;
|
|
399
|
+
} else {
|
|
400
|
+
// VM Closure - walk prototype chain for identity with ctor.prototype.
|
|
401
|
+
var proto = ctor.prototype;
|
|
402
|
+
var target = Object.getPrototypeOf(obj);
|
|
403
|
+
var result = false;
|
|
404
|
+
while (target !== null) {
|
|
405
|
+
if (target === proto) {
|
|
406
|
+
result = true;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
target = Object.getPrototypeOf(target);
|
|
410
|
+
}
|
|
411
|
+
frame.regs[dst] = result;
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ── Unary (dst, src) ─────────────────────────────────────────────────
|
|
417
|
+
case OP.UNARY_NEG: {
|
|
418
|
+
var dst = this._operand();
|
|
419
|
+
frame.regs[dst] = -frame.regs[this._operand()];
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
case OP.UNARY_POS: {
|
|
423
|
+
var dst = this._operand();
|
|
424
|
+
frame.regs[dst] = +frame.regs[this._operand()];
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
case OP.UNARY_NOT: {
|
|
428
|
+
var dst = this._operand();
|
|
429
|
+
frame.regs[dst] = !frame.regs[this._operand()];
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case OP.UNARY_BITNOT: {
|
|
433
|
+
var dst = this._operand();
|
|
434
|
+
frame.regs[dst] = ~frame.regs[this._operand()];
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
case OP.TYPEOF: {
|
|
438
|
+
var dst = this._operand();
|
|
439
|
+
frame.regs[dst] = typeof frame.regs[this._operand()];
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
case OP.VOID: {
|
|
443
|
+
var dst = this._operand();
|
|
444
|
+
this._operand(); // consume src — evaluated for side-effects by compiler
|
|
445
|
+
frame.regs[dst] = undefined;
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case OP.TYPEOF_SAFE: {
|
|
449
|
+
// dst, nameConstIdx — safe typeof for potentially-undeclared globals.
|
|
450
|
+
var dst = this._operand();
|
|
451
|
+
var name = this._constant();
|
|
452
|
+
var val = Object.prototype.hasOwnProperty.call(this.globals, name)
|
|
453
|
+
? this.globals[name]
|
|
454
|
+
: undefined;
|
|
455
|
+
frame.regs[dst] = typeof val;
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ── Control flow ──────────────────────────────────────────────────────
|
|
460
|
+
case OP.JUMP:
|
|
461
|
+
frame._pc = this._operand();
|
|
462
|
+
break;
|
|
463
|
+
|
|
464
|
+
case OP.JUMP_IF_FALSE: {
|
|
465
|
+
var src = this._operand();
|
|
466
|
+
var target = this._operand();
|
|
467
|
+
if (!frame.regs[src]) frame._pc = target;
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
case OP.JUMP_IF_TRUE: {
|
|
472
|
+
// || short-circuit: if truthy, jump over RHS.
|
|
473
|
+
var src = this._operand();
|
|
474
|
+
var target = this._operand();
|
|
475
|
+
if (frame.regs[src]) frame._pc = target;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ── Calls ─────────────────────────────────────────────────────────────
|
|
480
|
+
case OP.CALL: {
|
|
481
|
+
// dst, calleeReg, argc, [argReg...]
|
|
482
|
+
var dst = this._operand();
|
|
483
|
+
var callee = frame.regs[this._operand()];
|
|
484
|
+
var argc = this._operand();
|
|
485
|
+
var args = new Array(argc);
|
|
486
|
+
for (var i = 0; i < argc; i++) args[i] = frame.regs[this._operand()];
|
|
487
|
+
|
|
488
|
+
if (callee && callee[CLOSURE_SYM]) {
|
|
489
|
+
var c = callee[CLOSURE_SYM];
|
|
490
|
+
var f = new Frame(c, frame._pc, frame, this.globals, dst);
|
|
491
|
+
for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
|
|
492
|
+
f.regs[c.fn.paramCount] = args;
|
|
493
|
+
this._frameStack.push(this._currentFrame);
|
|
494
|
+
this._currentFrame = f;
|
|
495
|
+
} else {
|
|
496
|
+
frame.regs[dst] = callee.apply(null, args);
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
case OP.CALL_METHOD: {
|
|
502
|
+
// dst, receiverReg, calleeReg, argc, [argReg...]
|
|
503
|
+
var dst = this._operand();
|
|
504
|
+
var receiver = frame.regs[this._operand()];
|
|
505
|
+
var callee = frame.regs[this._operand()];
|
|
506
|
+
var argc = this._operand();
|
|
507
|
+
var args = new Array(argc);
|
|
508
|
+
for (var i = 0; i < argc; i++) args[i] = frame.regs[this._operand()];
|
|
509
|
+
|
|
510
|
+
if (callee && callee[CLOSURE_SYM]) {
|
|
511
|
+
var c = callee[CLOSURE_SYM];
|
|
512
|
+
var f = new Frame(c, frame._pc, frame, receiver, dst);
|
|
513
|
+
for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
|
|
514
|
+
f.regs[c.fn.paramCount] = args;
|
|
515
|
+
this._frameStack.push(this._currentFrame);
|
|
516
|
+
this._currentFrame = f;
|
|
517
|
+
} else {
|
|
518
|
+
frame.regs[dst] = callee.apply(receiver, args);
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
case OP.NEW: {
|
|
524
|
+
// dst, calleeReg, argc, [argReg...]
|
|
525
|
+
var dst = this._operand();
|
|
526
|
+
var callee = frame.regs[this._operand()];
|
|
527
|
+
var argc = this._operand();
|
|
528
|
+
var args = new Array(argc);
|
|
529
|
+
for (var i = 0; i < argc; i++) args[i] = frame.regs[this._operand()];
|
|
530
|
+
|
|
531
|
+
if (callee && callee[CLOSURE_SYM]) {
|
|
532
|
+
var c = callee[CLOSURE_SYM];
|
|
533
|
+
var newObj = Object.create(c.prototype || null);
|
|
534
|
+
var f = new Frame(c, frame._pc, frame, newObj, dst);
|
|
535
|
+
f._newObj = newObj;
|
|
536
|
+
for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
|
|
537
|
+
f.regs[c.fn.paramCount] = args;
|
|
538
|
+
this._frameStack.push(this._currentFrame);
|
|
539
|
+
this._currentFrame = f;
|
|
540
|
+
} else {
|
|
541
|
+
// Reflect.construct is required - Object.create+apply does NOT set
|
|
542
|
+
// internal slots ([[NumberData]], [[StringData]], etc.) for built-ins.
|
|
543
|
+
frame.regs[dst] = Reflect.construct(callee, args);
|
|
544
|
+
}
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
case OP.RETURN: {
|
|
549
|
+
var retVal = frame.regs[this._operand()];
|
|
550
|
+
this._closeUpvaluesFor(frame); // must happen before frame is abandoned
|
|
551
|
+
|
|
552
|
+
if (this._frameStack.length === 0) return retVal; // main script returning
|
|
553
|
+
|
|
554
|
+
// new-call rule: primitive return -> discard, use the constructed object instead
|
|
555
|
+
if (frame._newObj !== null) {
|
|
556
|
+
if (typeof retVal !== "object" || retVal === null)
|
|
557
|
+
retVal = frame._newObj;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
var parentFrame = this._frameStack.pop();
|
|
561
|
+
parentFrame.regs[frame._retDstReg] = retVal;
|
|
562
|
+
this._currentFrame = parentFrame;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
case OP.THROW:
|
|
567
|
+
throw frame.regs[this._operand()];
|
|
568
|
+
|
|
569
|
+
// ── Closures ──────────────────────────────────────────────────────────
|
|
570
|
+
case OP.MAKE_CLOSURE: {
|
|
571
|
+
// dst, startPc, paramCount, regCount, uvCount, [isLocal, idx, ...]
|
|
572
|
+
var dst = this._operand();
|
|
573
|
+
var startPc = this._operand();
|
|
574
|
+
var paramCount = this._operand();
|
|
575
|
+
var regCount = this._operand();
|
|
576
|
+
var uvCount = this._operand();
|
|
577
|
+
|
|
578
|
+
var uvDescs = new Array(uvCount);
|
|
579
|
+
for (var i = 0; i < uvCount; i++) {
|
|
580
|
+
var isLocalRaw = this._operand();
|
|
581
|
+
var uvIndex = this._operand();
|
|
582
|
+
uvDescs[i] = { isLocal: isLocalRaw, _index: uvIndex };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
var fn = {
|
|
586
|
+
paramCount: paramCount,
|
|
587
|
+
regCount: regCount,
|
|
588
|
+
startPc: startPc,
|
|
589
|
+
upvalueDescriptors: uvDescs,
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
var closure = new Closure(fn);
|
|
593
|
+
for (var i = 0; i < uvDescs.length; i++) {
|
|
594
|
+
var uvd = uvDescs[i];
|
|
595
|
+
if (uvd.isLocal) {
|
|
596
|
+
closure.upvalues.push(this.captureUpvalue(frame, uvd._index));
|
|
597
|
+
} else {
|
|
598
|
+
closure.upvalues.push(frame.closure.upvalues[uvd._index]);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Wrap in a native callable shell so host code (array methods,
|
|
603
|
+
// test assertions, setTimeout, etc.) can invoke VM closures.
|
|
604
|
+
// CLOSURE_SYM lets VM-internal CALL/NEW bypass the sub-VM entirely.
|
|
605
|
+
var self = this;
|
|
606
|
+
var shell = (function (c) {
|
|
607
|
+
return function () {
|
|
608
|
+
var args = Array.prototype.slice.call(arguments);
|
|
609
|
+
var sub = new VM(
|
|
610
|
+
self.bytecode,
|
|
611
|
+
0,
|
|
612
|
+
c.fn.regCount,
|
|
613
|
+
self.constants,
|
|
614
|
+
self.globals,
|
|
615
|
+
);
|
|
616
|
+
var f = new Frame(
|
|
617
|
+
c,
|
|
618
|
+
null,
|
|
619
|
+
null,
|
|
620
|
+
this == null ? self.globals : this,
|
|
621
|
+
0,
|
|
622
|
+
);
|
|
623
|
+
for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
|
|
624
|
+
f.regs[c.fn.paramCount] = args;
|
|
625
|
+
sub._currentFrame = f;
|
|
626
|
+
return sub.run();
|
|
627
|
+
};
|
|
628
|
+
})(closure);
|
|
629
|
+
shell[CLOSURE_SYM] = closure;
|
|
630
|
+
shell.prototype = closure.prototype; // unified prototype for new/instanceof
|
|
631
|
+
frame.regs[dst] = shell;
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ── Collections ───────────────────────────────────────────────────────
|
|
636
|
+
case OP.BUILD_ARRAY: {
|
|
637
|
+
// dst, count, [elemReg...]
|
|
638
|
+
var dst = this._operand();
|
|
639
|
+
var count = this._operand();
|
|
640
|
+
var elems = new Array(count);
|
|
641
|
+
for (var i = 0; i < count; i++)
|
|
642
|
+
elems[i] = frame.regs[this._operand()];
|
|
643
|
+
frame.regs[dst] = elems;
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
case OP.BUILD_OBJECT: {
|
|
648
|
+
// dst, pairCount, [keyReg, valReg, ...]
|
|
649
|
+
var dst = this._operand();
|
|
650
|
+
var pairCount = this._operand();
|
|
651
|
+
var o = {};
|
|
652
|
+
for (var i = 0; i < pairCount; i++) {
|
|
653
|
+
var key = frame.regs[this._operand()];
|
|
654
|
+
var val = frame.regs[this._operand()];
|
|
655
|
+
o[key] = val;
|
|
656
|
+
}
|
|
657
|
+
frame.regs[dst] = o;
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ── Property definitions (getters / setters) ──────────────────────────
|
|
662
|
+
case OP.DEFINE_GETTER: {
|
|
663
|
+
// obj, key, fn
|
|
664
|
+
var obj = frame.regs[this._operand()];
|
|
665
|
+
var key = frame.regs[this._operand()];
|
|
666
|
+
var getterFn = frame.regs[this._operand()];
|
|
667
|
+
var existingDesc = Object.getOwnPropertyDescriptor(obj, key);
|
|
668
|
+
var getDesc: PropertyDescriptor = {
|
|
669
|
+
get: getterFn,
|
|
670
|
+
configurable: true,
|
|
671
|
+
enumerable: true,
|
|
672
|
+
};
|
|
673
|
+
if (existingDesc && typeof existingDesc.set === "function") {
|
|
674
|
+
getDesc.set = existingDesc.set;
|
|
675
|
+
}
|
|
676
|
+
Object.defineProperty(obj, key, getDesc);
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
case OP.DEFINE_SETTER: {
|
|
681
|
+
// obj, key, fn
|
|
682
|
+
var obj = frame.regs[this._operand()];
|
|
683
|
+
var key = frame.regs[this._operand()];
|
|
684
|
+
var setterFn = frame.regs[this._operand()];
|
|
685
|
+
var existingDesc = Object.getOwnPropertyDescriptor(obj, key);
|
|
686
|
+
var setDesc: PropertyDescriptor = {
|
|
687
|
+
set: setterFn,
|
|
688
|
+
configurable: true,
|
|
689
|
+
enumerable: true,
|
|
690
|
+
};
|
|
691
|
+
if (existingDesc && typeof existingDesc.get === "function") {
|
|
692
|
+
setDesc.get = existingDesc.get;
|
|
693
|
+
}
|
|
694
|
+
Object.defineProperty(obj, key, setDesc);
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// ── For-in iteration ──────────────────────────────────────────────────
|
|
699
|
+
case OP.FOR_IN_SETUP: {
|
|
700
|
+
// dst, src — build iterator object from enumerable keys of regs[src]
|
|
701
|
+
var dst = this._operand();
|
|
702
|
+
var obj = frame.regs[this._operand()];
|
|
703
|
+
var keys = [];
|
|
704
|
+
if (obj !== null && obj !== undefined) {
|
|
705
|
+
var seen = Object.create(null);
|
|
706
|
+
var cur = Object(obj); // box primitives
|
|
707
|
+
while (cur !== null) {
|
|
708
|
+
var ownNames = Object.getOwnPropertyNames(cur);
|
|
709
|
+
for (var i = 0; i < ownNames.length; i++) {
|
|
710
|
+
var k = ownNames[i];
|
|
711
|
+
if (!(k in seen)) {
|
|
712
|
+
seen[k] = true;
|
|
713
|
+
var propDesc = Object.getOwnPropertyDescriptor(cur, k);
|
|
714
|
+
if (propDesc && propDesc.enumerable) {
|
|
715
|
+
keys.push(k);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
cur = Object.getPrototypeOf(cur);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
frame.regs[dst] = { _keys: keys, i: 0 };
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
case OP.FOR_IN_NEXT: {
|
|
727
|
+
// dst, iterReg, exitTarget
|
|
728
|
+
// Advances iterator; writes next key to dst, or jumps to exitTarget when done.
|
|
729
|
+
var dst = this._operand();
|
|
730
|
+
var iter = frame.regs[this._operand()];
|
|
731
|
+
var exitTarget = this._operand();
|
|
732
|
+
if (iter.i >= iter._keys.length) {
|
|
733
|
+
frame._pc = exitTarget;
|
|
734
|
+
} else {
|
|
735
|
+
frame.regs[dst] = iter._keys[iter.i++];
|
|
736
|
+
}
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// ── Exception handling ────────────────────────────────────────────────
|
|
741
|
+
case OP.TRY_SETUP: {
|
|
742
|
+
// handlerPc, exceptionReg — push exception handler record onto current frame.
|
|
743
|
+
frame._handlerStack.push({
|
|
744
|
+
handlerPc: this._operand(),
|
|
745
|
+
exceptionReg: this._operand(),
|
|
746
|
+
frameStackDepth: this._frameStack.length,
|
|
747
|
+
});
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
case OP.TRY_END: {
|
|
752
|
+
// Normal exit from a try block — disarm the exception handler.
|
|
753
|
+
frame._handlerStack.pop();
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// ── Self-modifying bytecode ───────────────────────────────────────────
|
|
758
|
+
case OP.PATCH: {
|
|
759
|
+
// destPc, sliceStart, sliceEnd
|
|
760
|
+
var destPc = this._operand();
|
|
761
|
+
var sliceStart = this._operand();
|
|
762
|
+
var sliceEnd = this._operand();
|
|
763
|
+
for (var pi = sliceStart; pi < sliceEnd; pi++) {
|
|
764
|
+
this.bytecode[destPc + (pi - sliceStart)] = this.bytecode[pi];
|
|
765
|
+
}
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
case OP.DEBUGGER: {
|
|
770
|
+
debugger;
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
default:
|
|
775
|
+
throw new Error(
|
|
776
|
+
"Unknown opcode: " + op + " at pc " + (frame._pc - 1),
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
} catch (err) {
|
|
780
|
+
// Exception handler unwinding (CPython-style frame walk, Lua-style upvalue close).
|
|
781
|
+
// Walk from the current frame upward until we find a frame that has an open
|
|
782
|
+
// exception handler (TRY_SETUP without a matching TRY_END).
|
|
783
|
+
// For every frame we abandon along the way, close its captured upvalues.
|
|
784
|
+
var handledFrame = null;
|
|
785
|
+
var searchFrame = this._currentFrame;
|
|
786
|
+
while (true) {
|
|
787
|
+
if (searchFrame._handlerStack.length > 0) {
|
|
788
|
+
handledFrame = searchFrame;
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
// No handler in this frame — abandon it and walk up.
|
|
792
|
+
this._closeUpvaluesFor(searchFrame);
|
|
793
|
+
if (this._frameStack.length === 0) break;
|
|
794
|
+
searchFrame = this._frameStack.pop();
|
|
795
|
+
this._currentFrame = searchFrame;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (!handledFrame) throw err; // no handler anywhere — propagate to host
|
|
799
|
+
|
|
800
|
+
var h = handledFrame._handlerStack.pop();
|
|
801
|
+
// Discard any call-frames that were pushed inside the try body.
|
|
802
|
+
this._frameStack.length = h.frameStackDepth;
|
|
803
|
+
// Write the caught exception directly into the designated register.
|
|
804
|
+
handledFrame.regs[h.exceptionReg] = err;
|
|
805
|
+
// Jump to the catch block.
|
|
806
|
+
handledFrame._pc = h.handlerPc;
|
|
807
|
+
this._currentFrame = handledFrame;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// Boot
|
|
813
|
+
var globals: any = {}; // global object for globals
|
|
814
|
+
|
|
815
|
+
// Always pull built-ins from globalThis so eval() scoping can't shadow them
|
|
816
|
+
// with a local `window` variable (e.g. the test harness fake window).
|
|
817
|
+
for (var k of Object.getOwnPropertyNames(globalThis)) {
|
|
818
|
+
globals[k] = globalThis[k];
|
|
819
|
+
}
|
|
820
|
+
// If a window object is in scope (browser or test harness), capture it
|
|
821
|
+
// explicitly so VM code can read/write window.TEST_OUTPUT etc.
|
|
822
|
+
if (typeof window !== "undefined") {
|
|
823
|
+
globals["window"] = window;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Transfer common primitives
|
|
827
|
+
globals.undefined = undefined;
|
|
828
|
+
globals.Infinity = Infinity;
|
|
829
|
+
globals.NaN = NaN;
|
|
830
|
+
|
|
831
|
+
var vm = new VM(
|
|
832
|
+
decodeBytecode(BYTECODE),
|
|
833
|
+
MAIN_START_PC,
|
|
834
|
+
MAIN_REG_COUNT,
|
|
835
|
+
CONSTANTS,
|
|
836
|
+
globals,
|
|
837
|
+
);
|
|
838
|
+
vm.run();
|