@tsvm/vm-runtime 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.
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2185 -0
- package/package.json +35 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2185 @@
|
|
|
1
|
+
// src/polymorphic-builder.ts
|
|
2
|
+
import { OpCode, ConstantEncodingScheme, ImmediateEncodingScheme, SeededRandom } from "@tsvm/shared";
|
|
3
|
+
function createOpaqueNameFactory(seed) {
|
|
4
|
+
const rng = new SeededRandom(seed ^ 5369200);
|
|
5
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
6
|
+
const used = /* @__PURE__ */ new Set();
|
|
7
|
+
return () => {
|
|
8
|
+
let value = "_";
|
|
9
|
+
do {
|
|
10
|
+
value = "_";
|
|
11
|
+
const len = rng.nextRange(5, 9);
|
|
12
|
+
for (let i = 0; i < len; i++) {
|
|
13
|
+
value += alphabet[rng.nextRange(0, alphabet.length - 1)];
|
|
14
|
+
}
|
|
15
|
+
} while (used.has(value));
|
|
16
|
+
used.add(value);
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function materializeConstant(value, seed) {
|
|
21
|
+
const rng = new SeededRandom(seed ^ value ^ 2068122);
|
|
22
|
+
const offset = rng.nextRange(1e3, 99999);
|
|
23
|
+
const choice = rng.nextRange(0, 3);
|
|
24
|
+
switch (choice) {
|
|
25
|
+
case 0:
|
|
26
|
+
return `(((${value - offset}) + ${offset}) | 0)`;
|
|
27
|
+
case 1:
|
|
28
|
+
return `(((${value ^ offset}) ^ ${offset}) | 0)`;
|
|
29
|
+
default:
|
|
30
|
+
return `(((${value + offset}) - ${offset}) | 0)`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function mutateArithmeticExpression(op, a, b, seed) {
|
|
34
|
+
const rng = new SeededRandom(seed ^ 9679525);
|
|
35
|
+
const choice = rng.nextRange(0, 9);
|
|
36
|
+
let expr = "";
|
|
37
|
+
if (op === "add") {
|
|
38
|
+
switch (choice) {
|
|
39
|
+
case 0:
|
|
40
|
+
expr = `(((${a}) ^ (${b})) + 2 * ((${a}) & (${b})))`;
|
|
41
|
+
break;
|
|
42
|
+
// a+b = (a^b) + 2*(a&b)
|
|
43
|
+
case 1:
|
|
44
|
+
expr = `(((${a}) | (${b})) + ((${a}) & (${b})))`;
|
|
45
|
+
break;
|
|
46
|
+
// a+b = (a|b) + (a&b)
|
|
47
|
+
case 2:
|
|
48
|
+
expr = `(2 * ((${a}) | (${b})) - ((${a}) ^ (${b})))`;
|
|
49
|
+
break;
|
|
50
|
+
// a+b = 2*(a|b) - (a^b)
|
|
51
|
+
case 3:
|
|
52
|
+
expr = `(((${a}) ^ ~(${b})) + 2 * ((${a}) | (${b})) + 1)`;
|
|
53
|
+
break;
|
|
54
|
+
// a+b = (a^~b) + 2*(a|b) + 1
|
|
55
|
+
case 4:
|
|
56
|
+
expr = `(((${a}) - (-(${b}))))`;
|
|
57
|
+
break;
|
|
58
|
+
// a+b = a - (-b)
|
|
59
|
+
case 5:
|
|
60
|
+
expr = `(((${a}) - ~(${b})) - 1)`;
|
|
61
|
+
break;
|
|
62
|
+
// a+b = a - ~b - 1, since ~b = -(b+1)
|
|
63
|
+
case 6:
|
|
64
|
+
expr = `(~(~(${a}) - (${b})))`;
|
|
65
|
+
break;
|
|
66
|
+
// a+b = ~(~a - b), since ~(~a-b) = a+b
|
|
67
|
+
case 7:
|
|
68
|
+
expr = `((((${a}) + (${b})) | 0))`;
|
|
69
|
+
break;
|
|
70
|
+
// a+b with int coercion
|
|
71
|
+
case 8:
|
|
72
|
+
expr = `((${a}) * 2 - (${a}) + (${b}))`;
|
|
73
|
+
break;
|
|
74
|
+
// 2a - a + b = a + b
|
|
75
|
+
default:
|
|
76
|
+
expr = `((${a}) + (${b}))`;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
} else if (op === "sub") {
|
|
80
|
+
switch (choice) {
|
|
81
|
+
case 0:
|
|
82
|
+
expr = `(((${a}) ^ ~(${b})) + 2 * ((${a}) & ~(${b})) + 1)`;
|
|
83
|
+
break;
|
|
84
|
+
// a-b via complement add
|
|
85
|
+
case 1:
|
|
86
|
+
expr = `(((${a}) & ~(${b})) - (~(${a}) & (${b})))`;
|
|
87
|
+
break;
|
|
88
|
+
// a-b = (a&~b) - (~a&b)
|
|
89
|
+
case 2:
|
|
90
|
+
expr = `(((${a}) | ~(${b})) - (~(${a}) | (${b})))`;
|
|
91
|
+
break;
|
|
92
|
+
// a-b via complement or
|
|
93
|
+
case 3:
|
|
94
|
+
expr = `(((${a}) ^ (${b})) - 2 * (~(${a}) & (${b})))`;
|
|
95
|
+
break;
|
|
96
|
+
// a-b = (a^b) - 2*(~a&b)
|
|
97
|
+
case 4:
|
|
98
|
+
expr = `((${a}) + (-(${b})))`;
|
|
99
|
+
break;
|
|
100
|
+
// a-b = a + (-b)
|
|
101
|
+
case 5:
|
|
102
|
+
expr = `(~((${b}) - (${a}) - 1))`;
|
|
103
|
+
break;
|
|
104
|
+
// a-b = ~(b-a-1), since ~x = -(x+1)
|
|
105
|
+
case 6:
|
|
106
|
+
expr = `((${a}) + (~(${b})) + 1)`;
|
|
107
|
+
break;
|
|
108
|
+
// a-b = a + ~b + 1 (two's complement)
|
|
109
|
+
case 7:
|
|
110
|
+
expr = `(((${a}) | 0) - ((${b}) | 0))`;
|
|
111
|
+
break;
|
|
112
|
+
// a-b with int coercion
|
|
113
|
+
case 8:
|
|
114
|
+
expr = `(~(~(${a}) + (${b})))`;
|
|
115
|
+
break;
|
|
116
|
+
// a-b = ~(~a+b), since ~(~a+b) = -(~a+b+1) = a-b
|
|
117
|
+
default:
|
|
118
|
+
expr = `((${a}) - (${b}))`;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
} else if (op === "and") {
|
|
122
|
+
switch (choice) {
|
|
123
|
+
case 0:
|
|
124
|
+
expr = `(((${a}) | (${b})) - ((${a}) ^ (${b})))`;
|
|
125
|
+
break;
|
|
126
|
+
// a&b = (a|b) - (a^b)
|
|
127
|
+
case 1:
|
|
128
|
+
expr = `((((${a}) + (${b})) - ((${a}) ^ (${b}))) >> 1)`;
|
|
129
|
+
break;
|
|
130
|
+
// a&b = ((a+b) - (a^b)) / 2
|
|
131
|
+
case 2:
|
|
132
|
+
expr = `(~(~(${a}) | ~(${b})))`;
|
|
133
|
+
break;
|
|
134
|
+
// De Morgan: a&b = ~(~a|~b)
|
|
135
|
+
case 3:
|
|
136
|
+
expr = `((${a}) & (${b}))`;
|
|
137
|
+
break;
|
|
138
|
+
// identity
|
|
139
|
+
case 4:
|
|
140
|
+
expr = `(((${a}) | (${b})) & ~((${a}) ^ (${b})))`;
|
|
141
|
+
break;
|
|
142
|
+
// a&b = (a|b) & ~(a^b)
|
|
143
|
+
case 5:
|
|
144
|
+
expr = `(((${a}) + (${b}) - ((${a}) | (${b}))))`;
|
|
145
|
+
break;
|
|
146
|
+
// a&b = a+b - (a|b)
|
|
147
|
+
case 6:
|
|
148
|
+
expr = `((${a}) - ((${a}) & ~(${b})))`;
|
|
149
|
+
break;
|
|
150
|
+
// a&b = a - (a&~b)
|
|
151
|
+
case 7:
|
|
152
|
+
expr = `((${b}) - (~(${a}) & (${b})))`;
|
|
153
|
+
break;
|
|
154
|
+
// a&b = b - (~a&b)
|
|
155
|
+
case 8:
|
|
156
|
+
expr = `((${a}) ^ ((${a}) ^ ((${a}) & (${b}))))`;
|
|
157
|
+
break;
|
|
158
|
+
// a ^ a ^ (a&b) = a&b
|
|
159
|
+
default:
|
|
160
|
+
expr = `((${a}) & (${b}))`;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
} else if (op === "or") {
|
|
164
|
+
switch (choice) {
|
|
165
|
+
case 0:
|
|
166
|
+
expr = `(((${a}) & (${b})) + ((${a}) ^ (${b})))`;
|
|
167
|
+
break;
|
|
168
|
+
// a|b = (a&b) + (a^b)
|
|
169
|
+
case 1:
|
|
170
|
+
expr = `(((${a}) ^ (${b})) | ((${a}) & (${b})))`;
|
|
171
|
+
break;
|
|
172
|
+
// a|b = (a^b) | (a&b) (disjoint)
|
|
173
|
+
case 2:
|
|
174
|
+
expr = `(~(~(${a}) & ~(${b})))`;
|
|
175
|
+
break;
|
|
176
|
+
// De Morgan: a|b = ~(~a&~b)
|
|
177
|
+
case 3:
|
|
178
|
+
expr = `((${a}) | (${b}))`;
|
|
179
|
+
break;
|
|
180
|
+
// identity
|
|
181
|
+
case 4:
|
|
182
|
+
expr = `(((${a}) + (${b})) - ((${a}) & (${b})))`;
|
|
183
|
+
break;
|
|
184
|
+
// a|b = a+b - (a&b)
|
|
185
|
+
case 5:
|
|
186
|
+
expr = `(((${a}) ^ (${b})) + ((${a}) & (${b})))`;
|
|
187
|
+
break;
|
|
188
|
+
// a|b = (a^b) + (a&b) (same as case 0)
|
|
189
|
+
case 6:
|
|
190
|
+
expr = `((${a}) + ((${b}) & ~(${a})))`;
|
|
191
|
+
break;
|
|
192
|
+
// a|b = a + (b&~a)
|
|
193
|
+
case 7:
|
|
194
|
+
expr = `((${b}) + ((${a}) & ~(${b})))`;
|
|
195
|
+
break;
|
|
196
|
+
// a|b = b + (a&~b)
|
|
197
|
+
case 8:
|
|
198
|
+
expr = `((${a}) ^ ((${a}) ^ ((${a}) | (${b}))))`;
|
|
199
|
+
break;
|
|
200
|
+
// a ^ a ^ (a|b) = a|b
|
|
201
|
+
default:
|
|
202
|
+
expr = `((${a}) | (${b}))`;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
switch (choice) {
|
|
207
|
+
case 0:
|
|
208
|
+
expr = `(((${a}) | (${b})) - ((${a}) & (${b})))`;
|
|
209
|
+
break;
|
|
210
|
+
// a^b = (a|b) - (a&b)
|
|
211
|
+
case 1:
|
|
212
|
+
expr = `(((${a}) + (${b})) - 2 * ((${a}) & (${b})))`;
|
|
213
|
+
break;
|
|
214
|
+
// a^b = a+b - 2*(a&b)
|
|
215
|
+
case 2:
|
|
216
|
+
expr = `(~(((${a}) | ~(${b})) & (~(${a}) | (${b}))))`;
|
|
217
|
+
break;
|
|
218
|
+
// FIX: ~XNOR = XOR
|
|
219
|
+
case 3:
|
|
220
|
+
expr = `((${a}) ^ (${b}))`;
|
|
221
|
+
break;
|
|
222
|
+
// identity
|
|
223
|
+
case 4:
|
|
224
|
+
expr = `(~(${a}) ^ ~(${b}))`;
|
|
225
|
+
break;
|
|
226
|
+
// ~a ^ ~b = a ^ b
|
|
227
|
+
case 5:
|
|
228
|
+
expr = `(2 * ((${a}) | (${b})) - (${a}) - (${b}))`;
|
|
229
|
+
break;
|
|
230
|
+
// 2*(a|b) - a - b = a^b
|
|
231
|
+
case 6:
|
|
232
|
+
expr = `(((${a}) | (${b})) ^ ((${a}) & (${b})))`;
|
|
233
|
+
break;
|
|
234
|
+
// (a|b) ^ (a&b) = a^b
|
|
235
|
+
case 7:
|
|
236
|
+
expr = `((~(${a}) & (${b})) | ((${a}) & ~(${b})))`;
|
|
237
|
+
break;
|
|
238
|
+
// textbook XOR definition
|
|
239
|
+
case 8:
|
|
240
|
+
expr = `((${a}) - (${b}) + 2 * (~(${a}) & (${b})))`;
|
|
241
|
+
break;
|
|
242
|
+
// a^b = (a-b) + 2*(~a&b)
|
|
243
|
+
default:
|
|
244
|
+
expr = `((${a}) ^ (${b}))`;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (op === "and" || op === "or" || op === "xor") {
|
|
249
|
+
return `((${expr}) | 0)`;
|
|
250
|
+
}
|
|
251
|
+
return expr;
|
|
252
|
+
}
|
|
253
|
+
function generateOpaquePredicate(seed, varIdx) {
|
|
254
|
+
const rng = new SeededRandom(seed ^ varIdx ^ 57005);
|
|
255
|
+
const choice = rng.nextRange(0, 7);
|
|
256
|
+
const pVar = `_op${rng.nextRange(100, 999)}`;
|
|
257
|
+
const runtimeVal = "ctx.pc";
|
|
258
|
+
switch (choice) {
|
|
259
|
+
case 0:
|
|
260
|
+
return { expr: `(function(){ var ${pVar} = ${runtimeVal}; return (${pVar} * ${pVar} + ${pVar}) % 2 === 0; })()`, alwaysTrue: true };
|
|
261
|
+
case 1:
|
|
262
|
+
return { expr: `(function(){ var ${pVar} = ${runtimeVal}; return (${pVar} * ${pVar}) % 4 !== 2; })()`, alwaysTrue: true };
|
|
263
|
+
case 2:
|
|
264
|
+
return {
|
|
265
|
+
expr: `(function(){ var ${pVar} = ${runtimeVal} | 0; return (${pVar} | (${pVar} - 1)) >= (${pVar} - 1); })()`,
|
|
266
|
+
alwaysTrue: true
|
|
267
|
+
};
|
|
268
|
+
case 3:
|
|
269
|
+
return { expr: `(function(){ var ${pVar} = ${runtimeVal}; return (${pVar} * ${pVar}) >= 0; })()`, alwaysTrue: true };
|
|
270
|
+
case 4:
|
|
271
|
+
return {
|
|
272
|
+
expr: `(function(){ var ${pVar} = ${runtimeVal}; return ((${pVar} & 1) + ((${pVar} >> 1) & 1)) < 3; })()`,
|
|
273
|
+
alwaysTrue: true
|
|
274
|
+
};
|
|
275
|
+
case 5:
|
|
276
|
+
return { expr: `(function(){ var ${pVar} = (${runtimeVal} & 255); return (${pVar} * ${pVar} & 3) !== 3; })()`, alwaysTrue: true };
|
|
277
|
+
case 6:
|
|
278
|
+
return {
|
|
279
|
+
expr: `(function(){ var ${pVar} = (${runtimeVal} & 255) * 31; return (${pVar} * ${pVar}) % 3 !== 2; })()`,
|
|
280
|
+
alwaysTrue: true
|
|
281
|
+
};
|
|
282
|
+
default:
|
|
283
|
+
return { expr: `(function(){ var ${pVar} = ${runtimeVal} | 0; return (${pVar} | 0) === ${pVar}; })()`, alwaysTrue: true };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function generateOpaqueDeadCode(seed, varIdx, regAlias, ctx) {
|
|
287
|
+
const rng = new SeededRandom(seed ^ varIdx ^ 51966);
|
|
288
|
+
const numBlocks = rng.nextRange(1, 2);
|
|
289
|
+
let code = "";
|
|
290
|
+
for (let i = 0; i < numBlocks; i++) {
|
|
291
|
+
const pred = generateOpaquePredicate(seed ^ i, varIdx);
|
|
292
|
+
const fakeReg1 = rng.nextRange(0, 15);
|
|
293
|
+
const fakeReg2 = rng.nextRange(0, 15);
|
|
294
|
+
const fakeConst = rng.nextRange(1, 255);
|
|
295
|
+
const trapVar = `_dt${rng.nextRange(100, 999)}`;
|
|
296
|
+
code += ` if (!${pred.expr}) {
|
|
297
|
+
`;
|
|
298
|
+
code += ` var ${trapVar} = ${regAlias}[${fakeReg1}];
|
|
299
|
+
`;
|
|
300
|
+
code += ` ${regAlias}[${fakeReg2}] = ${trapVar} ^ ${fakeConst};
|
|
301
|
+
`;
|
|
302
|
+
code += ` ctx.${ctx.rollingState} = (ctx.${ctx.rollingState} ^ ${trapVar}) | 0;
|
|
303
|
+
`;
|
|
304
|
+
code += " }\n";
|
|
305
|
+
}
|
|
306
|
+
return code;
|
|
307
|
+
}
|
|
308
|
+
function generateSignaturePollution(seed, varIdx) {
|
|
309
|
+
const rng = new SeededRandom(seed ^ varIdx ^ 45232);
|
|
310
|
+
const numDecls = rng.nextRange(2, 4);
|
|
311
|
+
let code = "";
|
|
312
|
+
for (let i = 0; i < numDecls; i++) {
|
|
313
|
+
const varName = `_sp${rng.nextRange(100, 999)}_${i}`;
|
|
314
|
+
const choice = rng.nextRange(0, 3);
|
|
315
|
+
switch (choice) {
|
|
316
|
+
case 0:
|
|
317
|
+
code += ` var ${varName} = (${rng.nextRange(1, 65535)} ^ ctx.pc) | 0;
|
|
318
|
+
`;
|
|
319
|
+
break;
|
|
320
|
+
case 1:
|
|
321
|
+
code += ` var ${varName} = (ctx.pc * ${rng.nextRange(2, 7)} + ${rng.nextRange(1, 100)}) & 0xFF;
|
|
322
|
+
`;
|
|
323
|
+
break;
|
|
324
|
+
case 2:
|
|
325
|
+
code += ` var ${varName} = ~(ctx.pc ^ ${rng.nextRange(1, 65535)}) >>> 0;
|
|
326
|
+
`;
|
|
327
|
+
break;
|
|
328
|
+
default:
|
|
329
|
+
code += ` var ${varName} = ((ctx.pc >> ${rng.nextRange(1, 4)}) + ${rng.nextRange(1, 50)}) | 0;
|
|
330
|
+
`;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return code;
|
|
335
|
+
}
|
|
336
|
+
function generateJunkStatements(seed, id, names, mulConst = "1664525", addConst = "1013904223") {
|
|
337
|
+
const rng = new SeededRandom(seed ^ id ^ 8137233);
|
|
338
|
+
const numJunk = rng.nextRange(1, 3);
|
|
339
|
+
let junk = "";
|
|
340
|
+
const ctx = names.ctx;
|
|
341
|
+
for (let i = 0; i < numJunk; i++) {
|
|
342
|
+
const choice = rng.nextRange(0, 3);
|
|
343
|
+
const varName = `_j${id}_${i}`;
|
|
344
|
+
switch (choice) {
|
|
345
|
+
case 0:
|
|
346
|
+
junk += ` var ${varName} = (${seed} ^ ${rng.nextRange(10, 100)}) | 0;
|
|
347
|
+
`;
|
|
348
|
+
junk += ` ctx.${ctx.rollingState} = (Math.imul(ctx.${ctx.rollingState} ^ ${varName}, ${mulConst}) + ${addConst}) | 0;
|
|
349
|
+
`;
|
|
350
|
+
break;
|
|
351
|
+
case 1:
|
|
352
|
+
junk += ` var ${varName} = Math.sin(${rng.nextRange(1, 10)}) * ${rng.nextRange(2, 5)};
|
|
353
|
+
`;
|
|
354
|
+
junk += ` ctx.${ctx.rollingState} = (ctx.${ctx.rollingState} + (${varName} | 0)) & 0xFFFFFFFF;
|
|
355
|
+
`;
|
|
356
|
+
break;
|
|
357
|
+
default:
|
|
358
|
+
junk += ` var ${varName} = (${seed} % ${rng.nextRange(3, 9)}) | 0;
|
|
359
|
+
`;
|
|
360
|
+
junk += ` ctx.${ctx.rollingState} = (ctx.${ctx.rollingState} ^ (${varName} * ${varName})) | 0;
|
|
361
|
+
`;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return junk;
|
|
366
|
+
}
|
|
367
|
+
function createRuntimeNames(config) {
|
|
368
|
+
const stealth = !!config.stealthDispatch;
|
|
369
|
+
if (!stealth) {
|
|
370
|
+
return {
|
|
371
|
+
stealth,
|
|
372
|
+
nextHandlerName: (canonical) => `h_${canonical}`,
|
|
373
|
+
top: {
|
|
374
|
+
seed: "seed",
|
|
375
|
+
rawCP: "rawCP",
|
|
376
|
+
cpCache: "cpCache",
|
|
377
|
+
getCP: "getCP",
|
|
378
|
+
runtimeStrings: "runtimeStrings",
|
|
379
|
+
runtimeStringCache: "runtimeStringCache",
|
|
380
|
+
getRuntimeString: "getRuntimeString",
|
|
381
|
+
weakMapCtor: "VMWeakMap",
|
|
382
|
+
weakMapGet: "VMWeakMapGet",
|
|
383
|
+
weakMapSet: "VMWeakMapSet",
|
|
384
|
+
reflectObj: "VMReflect",
|
|
385
|
+
objectObj: "VMObject",
|
|
386
|
+
nativeToString: "VMNativeToString",
|
|
387
|
+
nativeMathSin: "VMNativeMathSin",
|
|
388
|
+
nativeDefineProperty: "VMDefineProperty",
|
|
389
|
+
nativeGetOwnPropertyDescriptor: "VMGetOwnPropertyDescriptor",
|
|
390
|
+
nativeApply: "VMNativeApply",
|
|
391
|
+
nativeCall: "VMNativeCall",
|
|
392
|
+
performanceNow: "VMPerformanceNow",
|
|
393
|
+
mathRandom: "VMMathRandom",
|
|
394
|
+
arraySlice: "sliceArgs",
|
|
395
|
+
promiseResolve: "resolvePromise",
|
|
396
|
+
iteratorSymbol: "iteratorSymbol",
|
|
397
|
+
asyncIteratorSymbol: "asyncIteratorSymbol",
|
|
398
|
+
functionArena: "functionArena",
|
|
399
|
+
functionBytecodes: "functionBytecodes",
|
|
400
|
+
executorCache: "executorCache",
|
|
401
|
+
getExecutorById: "getExecutorById",
|
|
402
|
+
createExecutor: "createExecutor",
|
|
403
|
+
handlers: "handlers",
|
|
404
|
+
result: "result",
|
|
405
|
+
vmFunctions: "vmFunctions",
|
|
406
|
+
privateData: "privateData",
|
|
407
|
+
opaquePredicate: "opaquePredicate",
|
|
408
|
+
junkSink: "junkSink",
|
|
409
|
+
readByte: "readByte"
|
|
410
|
+
},
|
|
411
|
+
ctx: {
|
|
412
|
+
pc: "pc",
|
|
413
|
+
bytecode: "bytecode",
|
|
414
|
+
regs: "regs",
|
|
415
|
+
fnArgs: "fnArgs",
|
|
416
|
+
env: "env",
|
|
417
|
+
globalScope: "globalScope",
|
|
418
|
+
thisArg: "thisArg",
|
|
419
|
+
newTarget: "newTarget",
|
|
420
|
+
resumeMode: "resumeMode",
|
|
421
|
+
resumeValue: "resumeValue",
|
|
422
|
+
resumeReg: "resumeReg",
|
|
423
|
+
awaitPromise: "awaitPromise",
|
|
424
|
+
running: "running",
|
|
425
|
+
returnValue: "returnValue",
|
|
426
|
+
tryFrames: "tryStack",
|
|
427
|
+
rollingState: "rollingKey",
|
|
428
|
+
xorLog: "xorLog",
|
|
429
|
+
executionNonce: "executionNonce",
|
|
430
|
+
currentOpcode: "currentOpcode",
|
|
431
|
+
currentHandlerIdx: "currentHandlerIdx",
|
|
432
|
+
pathHash: "pathHash"
|
|
433
|
+
},
|
|
434
|
+
frame: {
|
|
435
|
+
catchPc: "catchPc",
|
|
436
|
+
endPc: "endPc",
|
|
437
|
+
exceptionReg: "exceptionReg"
|
|
438
|
+
},
|
|
439
|
+
locals: {
|
|
440
|
+
opByte: "op",
|
|
441
|
+
dispatchBank: "handlers",
|
|
442
|
+
dispatchRoute: "dispatchRoute"
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const next = createOpaqueNameFactory(config.seed);
|
|
447
|
+
return {
|
|
448
|
+
stealth,
|
|
449
|
+
nextHandlerName: () => next(),
|
|
450
|
+
top: {
|
|
451
|
+
seed: next(),
|
|
452
|
+
rawCP: next(),
|
|
453
|
+
cpCache: next(),
|
|
454
|
+
getCP: next(),
|
|
455
|
+
runtimeStrings: next(),
|
|
456
|
+
runtimeStringCache: next(),
|
|
457
|
+
getRuntimeString: next(),
|
|
458
|
+
weakMapCtor: next(),
|
|
459
|
+
weakMapGet: next(),
|
|
460
|
+
weakMapSet: next(),
|
|
461
|
+
reflectObj: next(),
|
|
462
|
+
objectObj: next(),
|
|
463
|
+
nativeToString: next(),
|
|
464
|
+
nativeMathSin: next(),
|
|
465
|
+
nativeDefineProperty: next(),
|
|
466
|
+
nativeGetOwnPropertyDescriptor: next(),
|
|
467
|
+
nativeApply: next(),
|
|
468
|
+
nativeCall: next(),
|
|
469
|
+
performanceNow: next(),
|
|
470
|
+
mathRandom: next(),
|
|
471
|
+
arraySlice: next(),
|
|
472
|
+
promiseResolve: next(),
|
|
473
|
+
iteratorSymbol: next(),
|
|
474
|
+
asyncIteratorSymbol: next(),
|
|
475
|
+
functionArena: next(),
|
|
476
|
+
functionBytecodes: next(),
|
|
477
|
+
executorCache: next(),
|
|
478
|
+
getExecutorById: next(),
|
|
479
|
+
createExecutor: next(),
|
|
480
|
+
handlers: next(),
|
|
481
|
+
result: next(),
|
|
482
|
+
vmFunctions: next(),
|
|
483
|
+
privateData: next(),
|
|
484
|
+
opaquePredicate: next(),
|
|
485
|
+
junkSink: next(),
|
|
486
|
+
readByte: next()
|
|
487
|
+
},
|
|
488
|
+
ctx: {
|
|
489
|
+
pc: "pc",
|
|
490
|
+
bytecode: "bytecode",
|
|
491
|
+
regs: "regs",
|
|
492
|
+
fnArgs: "fnArgs",
|
|
493
|
+
env: "env",
|
|
494
|
+
globalScope: "globalScope",
|
|
495
|
+
thisArg: next(),
|
|
496
|
+
newTarget: next(),
|
|
497
|
+
resumeMode: next(),
|
|
498
|
+
resumeValue: next(),
|
|
499
|
+
resumeReg: next(),
|
|
500
|
+
awaitPromise: next(),
|
|
501
|
+
running: "running",
|
|
502
|
+
returnValue: "returnValue",
|
|
503
|
+
tryFrames: next(),
|
|
504
|
+
rollingState: next(),
|
|
505
|
+
xorLog: next(),
|
|
506
|
+
executionNonce: "executionNonce",
|
|
507
|
+
currentOpcode: "currentOpcode",
|
|
508
|
+
currentHandlerIdx: next(),
|
|
509
|
+
pathHash: next()
|
|
510
|
+
},
|
|
511
|
+
frame: {
|
|
512
|
+
catchPc: next(),
|
|
513
|
+
endPc: next(),
|
|
514
|
+
exceptionReg: next()
|
|
515
|
+
},
|
|
516
|
+
locals: {
|
|
517
|
+
opByte: next(),
|
|
518
|
+
dispatchBank: next(),
|
|
519
|
+
dispatchRoute: next()
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function buildVMRuntime(module, config) {
|
|
524
|
+
const names = createRuntimeNames(config);
|
|
525
|
+
const buildRng = new SeededRandom(config.seed ^ 765210);
|
|
526
|
+
const ctx = names.ctx;
|
|
527
|
+
const top = names.top;
|
|
528
|
+
const frame = names.frame;
|
|
529
|
+
const locals = names.locals;
|
|
530
|
+
const mulConst = materializeConstant(1664525, config.seed);
|
|
531
|
+
const addConst = materializeConstant(1013904223, config.seed ^ 1616702);
|
|
532
|
+
const seedConst = materializeConstant(config.seed, config.seed ^ 4069292);
|
|
533
|
+
const regRef = (idx) => `ctx.${ctx.regs}[${idx}]`;
|
|
534
|
+
const ctxRef = (key) => `ctx.${ctx[key]}`;
|
|
535
|
+
const frameRef = (target, key) => `${target}.${frame[key]}`;
|
|
536
|
+
const opToMapped = /* @__PURE__ */ new Map();
|
|
537
|
+
for (const [canonical, mapped] of module.opcodeMapping.forward.entries()) {
|
|
538
|
+
opToMapped.set(canonical, Array.isArray(mapped) ? [...mapped] : [mapped]);
|
|
539
|
+
}
|
|
540
|
+
const cp = JSON.stringify(module.constantPool);
|
|
541
|
+
const targetFnIndex = module.entryPointIndex >= 0 ? module.entryPointIndex : 0;
|
|
542
|
+
const targetFn = module.functions[targetFnIndex];
|
|
543
|
+
if (!targetFn) {
|
|
544
|
+
throw new Error(`No target function found at index ${targetFnIndex}`);
|
|
545
|
+
}
|
|
546
|
+
const exportedFunctions = module.functions.filter((f) => f.isEntryPoint);
|
|
547
|
+
const concealRuntimeStrings = !!config.stealthDispatch || !!config.tamperDetection || !!config.junkInsertion;
|
|
548
|
+
const stringRng = new SeededRandom(config.seed ^ 4030233);
|
|
549
|
+
const keyBytes = [];
|
|
550
|
+
for (let i = 0; i < 16; i++) {
|
|
551
|
+
keyBytes.push(stringRng.nextRange(1, 255));
|
|
552
|
+
}
|
|
553
|
+
const runtimeStringEntries = [
|
|
554
|
+
"VM Integrity Violation at PC ",
|
|
555
|
+
", raw op: ",
|
|
556
|
+
"Unknown VM function id: ",
|
|
557
|
+
"Cannot read private member",
|
|
558
|
+
"@@iterator"
|
|
559
|
+
];
|
|
560
|
+
const runtimeStringIndex = new Map(runtimeStringEntries.map((value, index) => [value, index]));
|
|
561
|
+
const encodedRuntimeStrings = runtimeStringEntries.map((value) => {
|
|
562
|
+
let encoded = "";
|
|
563
|
+
for (let i = 0; i < value.length; i++) {
|
|
564
|
+
const keyByte = keyBytes[i % 16];
|
|
565
|
+
const nextKeyByte = keyBytes[(i + 1) % 16];
|
|
566
|
+
encoded += String.fromCharCode(value.charCodeAt(i) ^ keyByte ^ nextKeyByte);
|
|
567
|
+
}
|
|
568
|
+
return encoded;
|
|
569
|
+
});
|
|
570
|
+
const runtimeStringRef = (value) => {
|
|
571
|
+
if (!concealRuntimeStrings) {
|
|
572
|
+
return JSON.stringify(value);
|
|
573
|
+
}
|
|
574
|
+
const index = runtimeStringIndex.get(value);
|
|
575
|
+
if (index === void 0) {
|
|
576
|
+
throw new Error(`Missing runtime string entry: ${value}`);
|
|
577
|
+
}
|
|
578
|
+
return `${top.getRuntimeString}(${index})`;
|
|
579
|
+
};
|
|
580
|
+
const runtimeStringBootstrap = concealRuntimeStrings ? `
|
|
581
|
+
const ${top.runtimeStrings} = ${JSON.stringify(encodedRuntimeStrings)};
|
|
582
|
+
const ${top.runtimeStringCache} = Object.create(null);
|
|
583
|
+
const _strKey = ${JSON.stringify(keyBytes)};
|
|
584
|
+
function ${top.getRuntimeString}(index) {
|
|
585
|
+
if (${top.runtimeStringCache}[index] !== undefined) return ${top.runtimeStringCache}[index];
|
|
586
|
+
var encoded = ${top.runtimeStrings}[index];
|
|
587
|
+
var decoded = '';
|
|
588
|
+
for (var i = 0; i < encoded.length; i++) {
|
|
589
|
+
var keyByte = _strKey[i % 16];
|
|
590
|
+
var nextKeyByte = _strKey[(i + 1) % 16];
|
|
591
|
+
decoded += String.fromCharCode(encoded.charCodeAt(i) ^ keyByte ^ nextKeyByte);
|
|
592
|
+
}
|
|
593
|
+
return ${top.runtimeStringCache}[index] = decoded;
|
|
594
|
+
}
|
|
595
|
+
function ${top.opaquePredicate}(value) {
|
|
596
|
+
value = (value ^ 0x51ed) | 0;
|
|
597
|
+
var steps = 0;
|
|
598
|
+
var n = value;
|
|
599
|
+
if (n < 0) n = -n;
|
|
600
|
+
while (n > 1 && steps < 12) {
|
|
601
|
+
if ((n & 1) === 0) {
|
|
602
|
+
n = (n >>> 1) | 0;
|
|
603
|
+
} else {
|
|
604
|
+
n = (Math.imul(n, 3) + 1) | 0;
|
|
605
|
+
}
|
|
606
|
+
steps++;
|
|
607
|
+
}
|
|
608
|
+
return (n | 0) !== -9999;
|
|
609
|
+
}
|
|
610
|
+
function ${top.junkSink}(value) {
|
|
611
|
+
var acc = value ^ ${config.seed};
|
|
612
|
+
for (var i = 0; i < 3; i++) acc = ((acc << 5) - acc + i) | 0;
|
|
613
|
+
return acc;
|
|
614
|
+
}
|
|
615
|
+
if (!${top.opaquePredicate}(${top.seed})) {
|
|
616
|
+
${top.junkSink}(${top.seed});
|
|
617
|
+
}
|
|
618
|
+
` : "";
|
|
619
|
+
function isVariableLengthOpcode(opcode) {
|
|
620
|
+
return opcode === 64 || // OpCode.Call
|
|
621
|
+
opcode === 65 || // OpCode.CallMethod
|
|
622
|
+
opcode === 66 || // OpCode.New
|
|
623
|
+
opcode === 84 || // OpCode.ArrayNew
|
|
624
|
+
opcode === 85 || // OpCode.ObjectNew
|
|
625
|
+
opcode === 86 || // OpCode.Spread
|
|
626
|
+
opcode === 87 || // OpCode.SpreadIntoArray
|
|
627
|
+
opcode === 94 || // OpCode.SuperCall
|
|
628
|
+
opcode === 254;
|
|
629
|
+
}
|
|
630
|
+
function isTerminator(opcode) {
|
|
631
|
+
return opcode === OpCode.Jmp || opcode === OpCode.JmpIf || opcode === OpCode.JmpIfNot || opcode === OpCode.Switch || opcode === OpCode.Return || opcode === OpCode.ReturnVoid || opcode === OpCode.TailCall || opcode === OpCode.Throw || opcode === OpCode.Yield || opcode === OpCode.YieldStar || opcode === OpCode.Await || opcode === OpCode.Halt || opcode === OpCode.Trap;
|
|
632
|
+
}
|
|
633
|
+
const handlerDeclarations = [];
|
|
634
|
+
const handlerNames = /* @__PURE__ */ new Map();
|
|
635
|
+
const declaredOpcodes = [];
|
|
636
|
+
const allHandlerVariants = /* @__PURE__ */ new Map();
|
|
637
|
+
const declareHandler = (canonical, body) => {
|
|
638
|
+
const isParanoid = config.runtimeHardening === "paranoid" && canonical !== OpCode.Trap;
|
|
639
|
+
const numVariants = isParanoid ? 4 : 1;
|
|
640
|
+
const fnNames = [];
|
|
641
|
+
for (let vIdx = 0; vIdx < numVariants; vIdx++) {
|
|
642
|
+
const fnName = isParanoid ? `${names.nextHandlerName(canonical)}_${vIdx}` : names.nextHandlerName(canonical);
|
|
643
|
+
fnNames.push(fnName);
|
|
644
|
+
const mySeed = config.seed ^ canonical ^ vIdx;
|
|
645
|
+
const myRng = new SeededRandom(mySeed);
|
|
646
|
+
let junkSkip = "";
|
|
647
|
+
if (config.junkInsertion) {
|
|
648
|
+
const numJunk = (canonical * 7 + config.seed) % 4;
|
|
649
|
+
for (let i = 0; i < numJunk; i++) {
|
|
650
|
+
junkSkip += config.rollingKeys ? ` let __junk_${vIdx}_${i} = ${top.readByte}(ctx);
|
|
651
|
+
` : " ctx.pc++;\n";
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const isVarLength = isVariableLengthOpcode(canonical);
|
|
655
|
+
let myReadArgs = "";
|
|
656
|
+
const argsVar = isParanoid ? `_args_${vIdx}` : "args";
|
|
657
|
+
const valVar = isParanoid ? `_val_${vIdx}` : "val";
|
|
658
|
+
const kindNumVar = isParanoid ? `_kind_${vIdx}` : "kindNum";
|
|
659
|
+
const myAdvanceArg = config.rollingKeys ? `
|
|
660
|
+
let ${kindNumVar} = ${top.readByte}(ctx);
|
|
661
|
+
let ${valVar} = 0;
|
|
662
|
+
${config.immediateEncoding === ImmediateEncodingScheme.VariableLength ? `
|
|
663
|
+
let shift = 0;
|
|
664
|
+
let b;
|
|
665
|
+
do {
|
|
666
|
+
b = ${top.readByte}(ctx);
|
|
667
|
+
${valVar} |= (b & 0x7F) << shift;
|
|
668
|
+
shift += 7;
|
|
669
|
+
} while (b & 0x80);
|
|
670
|
+
` : `
|
|
671
|
+
let b0 = ${top.readByte}(ctx);
|
|
672
|
+
let b1 = ${top.readByte}(ctx);
|
|
673
|
+
let b2 = ${top.readByte}(ctx);
|
|
674
|
+
let b3 = ${top.readByte}(ctx);
|
|
675
|
+
${valVar} = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
|
|
676
|
+
`}
|
|
677
|
+
` : `
|
|
678
|
+
let ${kindNumVar} = ctx.bytecode[ctx.pc++];
|
|
679
|
+
let ${valVar} = 0;
|
|
680
|
+
${config.immediateEncoding === ImmediateEncodingScheme.VariableLength ? `
|
|
681
|
+
let shift = 0;
|
|
682
|
+
let b;
|
|
683
|
+
do {
|
|
684
|
+
b = ctx.bytecode[ctx.pc++];
|
|
685
|
+
${valVar} |= (b & 0x7F) << shift;
|
|
686
|
+
shift += 7;
|
|
687
|
+
} while (b & 0x80);
|
|
688
|
+
` : `
|
|
689
|
+
let b0 = ctx.bytecode[ctx.pc];
|
|
690
|
+
let b1 = ctx.bytecode[ctx.pc + 1];
|
|
691
|
+
let b2 = ctx.bytecode[ctx.pc + 2];
|
|
692
|
+
let b3 = ctx.bytecode[ctx.pc + 3];
|
|
693
|
+
ctx.pc += 4;
|
|
694
|
+
${valVar} = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
|
|
695
|
+
`}
|
|
696
|
+
`;
|
|
697
|
+
if (isVarLength) {
|
|
698
|
+
myReadArgs = `
|
|
699
|
+
let argCount = ${config.rollingKeys ? `${top.readByte}(ctx)` : "ctx.bytecode[ctx.pc++]"};
|
|
700
|
+
const ${argsVar} = [];
|
|
701
|
+
const kinds = [];
|
|
702
|
+
for (let i = 0; i < argCount; i++) {
|
|
703
|
+
${myAdvanceArg}
|
|
704
|
+
${argsVar}.push(${valVar});
|
|
705
|
+
kinds.push(${kindNumVar});
|
|
706
|
+
}
|
|
707
|
+
`;
|
|
708
|
+
} else {
|
|
709
|
+
const matches = body.match(/args\[(\d+)\]/g);
|
|
710
|
+
let operandCount = 0;
|
|
711
|
+
if (matches) {
|
|
712
|
+
for (const m of matches) {
|
|
713
|
+
const idx = Number.parseInt(m.match(/\d+/)[0], 10);
|
|
714
|
+
if (idx + 1 > operandCount) {
|
|
715
|
+
operandCount = idx + 1;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
myReadArgs = `
|
|
720
|
+
const ${argsVar} = [];
|
|
721
|
+
const kinds = [];
|
|
722
|
+
for (let i = 0; i < ${operandCount}; i++) {
|
|
723
|
+
${myAdvanceArg}
|
|
724
|
+
${argsVar}.push(${valVar});
|
|
725
|
+
kinds.push(${kindNumVar});
|
|
726
|
+
}
|
|
727
|
+
`;
|
|
728
|
+
}
|
|
729
|
+
const junkLogic = config.stealthDispatch ? generateJunkStatements(mySeed, canonical, names, mulConst, addConst) : "";
|
|
730
|
+
let nextOpLogic = "";
|
|
731
|
+
if (canonical !== OpCode.Halt && canonical !== OpCode.Return && canonical !== OpCode.ReturnVoid && canonical !== OpCode.Throw && canonical !== OpCode.Trap && canonical !== OpCode.Await && canonical !== OpCode.Yield && canonical !== OpCode.YieldStar) {
|
|
732
|
+
if (config.runtimeHardening === "paranoid") {
|
|
733
|
+
nextOpLogic = `
|
|
734
|
+
if (${ctxRef("pc")} >= ${ctxRef("bytecode")}.length) return null;
|
|
735
|
+
let nextOp = ${top.readByte}(ctx);
|
|
736
|
+
${config.stealthDispatch ? `
|
|
737
|
+
nextOp = (ctx.${ctx.currentHandlerIdx} + nextOp) % 256;
|
|
738
|
+
ctx.${ctx.currentHandlerIdx} = nextOp;
|
|
739
|
+
` : ""}
|
|
740
|
+
${config.rollingKeys ? `
|
|
741
|
+
ctx.${ctx.pathHash} = (Math.imul(ctx.${ctx.pathHash}, 31) + nextOp) & 0xFFFFFFFF;
|
|
742
|
+
` : ""}
|
|
743
|
+
return makeRouteToken(ctx, nextOp);
|
|
744
|
+
`;
|
|
745
|
+
} else {
|
|
746
|
+
nextOpLogic = `
|
|
747
|
+
if (${ctxRef("pc")} >= ${ctxRef("bytecode")}.length) return null;
|
|
748
|
+
let nextOp = ${top.readByte}(ctx);
|
|
749
|
+
${config.stealthDispatch ? `
|
|
750
|
+
nextOp = (ctx.${ctx.currentHandlerIdx} + nextOp) % 256;
|
|
751
|
+
ctx.${ctx.currentHandlerIdx} = nextOp;
|
|
752
|
+
` : ""}
|
|
753
|
+
${config.rollingKeys ? `
|
|
754
|
+
ctx.${ctx.pathHash} = (Math.imul(ctx.${ctx.pathHash}, 31) + nextOp) & 0xFFFFFFFF;
|
|
755
|
+
` : ""}
|
|
756
|
+
return ${locals.dispatchBank}[nextOp];
|
|
757
|
+
`;
|
|
758
|
+
}
|
|
759
|
+
} else {
|
|
760
|
+
nextOpLogic = "return null;";
|
|
761
|
+
}
|
|
762
|
+
let variantBody = body.replace(/\${readArgs}/g, () => myReadArgs);
|
|
763
|
+
variantBody = variantBody.replace(/__ADD_EXPR__/g, () => {
|
|
764
|
+
return mutateArithmeticExpression("add", "ctx.regs[args[0]]", "ctx.regs[args[1]]", mySeed);
|
|
765
|
+
});
|
|
766
|
+
variantBody = variantBody.replace(/__SUB_EXPR__/g, () => {
|
|
767
|
+
return mutateArithmeticExpression("sub", "ctx.regs[args[0]]", "ctx.regs[args[1]]", mySeed);
|
|
768
|
+
});
|
|
769
|
+
variantBody = variantBody.replace(/__AND_EXPR__/g, () => {
|
|
770
|
+
return mutateArithmeticExpression("and", "ctx.regs[args[0]]", "ctx.regs[args[1]]", mySeed);
|
|
771
|
+
});
|
|
772
|
+
variantBody = variantBody.replace(/__OR_EXPR__/g, () => {
|
|
773
|
+
return mutateArithmeticExpression("or", "ctx.regs[args[0]]", "ctx.regs[args[1]]", mySeed);
|
|
774
|
+
});
|
|
775
|
+
variantBody = variantBody.replace(/__XOR_EXPR__/g, () => {
|
|
776
|
+
return mutateArithmeticExpression("xor", "ctx.regs[args[0]]", "ctx.regs[args[1]]", mySeed);
|
|
777
|
+
});
|
|
778
|
+
if (isParanoid) {
|
|
779
|
+
variantBody = variantBody.replace(/\bargs\b/g, argsVar);
|
|
780
|
+
variantBody = variantBody.replace(/args\[/g, `${argsVar}[`);
|
|
781
|
+
}
|
|
782
|
+
const regAlias = isParanoid ? `_r${myRng.nextRange(1e3, 9999)}` : `ctx.${ctx.regs}`;
|
|
783
|
+
const regSetup = isParanoid ? ` var ${regAlias} = ctx.${ctx.regs};
|
|
784
|
+
` : "";
|
|
785
|
+
if (isParanoid) {
|
|
786
|
+
variantBody = variantBody.replace(/\bctx\.regs\[/g, `${regAlias}[`);
|
|
787
|
+
variantBody = variantBody.replace(/\bctx\.regs\b/g, regAlias);
|
|
788
|
+
}
|
|
789
|
+
if (config.stealthDispatch && isTerminator(canonical)) {
|
|
790
|
+
variantBody += `
|
|
791
|
+
ctx.${ctx.currentHandlerIdx} = 0;
|
|
792
|
+
`;
|
|
793
|
+
}
|
|
794
|
+
let currentOpcodeTracker = "";
|
|
795
|
+
if (config.runtimeHardening === "paranoid") {
|
|
796
|
+
currentOpcodeTracker = `ctx.currentOpcode = ${canonical};
|
|
797
|
+
`;
|
|
798
|
+
}
|
|
799
|
+
let opaqueDeadCode = "";
|
|
800
|
+
let sigPollution = "";
|
|
801
|
+
if (isParanoid) {
|
|
802
|
+
opaqueDeadCode = generateOpaqueDeadCode(mySeed, vIdx, regAlias, ctx);
|
|
803
|
+
sigPollution = generateSignaturePollution(mySeed, vIdx);
|
|
804
|
+
}
|
|
805
|
+
handlerDeclarations.push(
|
|
806
|
+
`function ${fnName}(ctx) {
|
|
807
|
+
${currentOpcodeTracker}${junkSkip}
|
|
808
|
+
${regSetup}${sigPollution}${junkLogic}
|
|
809
|
+
${opaqueDeadCode}${variantBody}
|
|
810
|
+
${nextOpLogic}
|
|
811
|
+
}`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
declaredOpcodes.push(canonical);
|
|
815
|
+
handlerNames.set(canonical, fnNames[0]);
|
|
816
|
+
allHandlerVariants.set(canonical, fnNames);
|
|
817
|
+
};
|
|
818
|
+
const readArgs = "${readArgs}";
|
|
819
|
+
declareHandler(
|
|
820
|
+
OpCode.Trap,
|
|
821
|
+
`
|
|
822
|
+
ctx.regs = [];
|
|
823
|
+
ctx.pc = 999999;
|
|
824
|
+
ctx.running = false;
|
|
825
|
+
debugger;
|
|
826
|
+
throw new Error(${runtimeStringRef("VM Integrity Violation at PC ")} + (${ctxRef("pc")} - 1));
|
|
827
|
+
`
|
|
828
|
+
);
|
|
829
|
+
declareHandler(
|
|
830
|
+
OpCode.LoadConst,
|
|
831
|
+
`
|
|
832
|
+
${readArgs}
|
|
833
|
+
${regRef("args[1]")} = (kinds[0] === 2) ? ${top.getCP}(ctx, args[0]) : args[0];
|
|
834
|
+
`
|
|
835
|
+
);
|
|
836
|
+
declareHandler(OpCode.LoadLocal, `${readArgs} ctx.regs[args[1]] = ctx.regs[args[0]];`);
|
|
837
|
+
declareHandler(OpCode.StoreLocal, `${readArgs} ctx.regs[args[0]] = ctx.regs[args[1]];`);
|
|
838
|
+
declareHandler(OpCode.Move, `${readArgs} ctx.regs[args[1]] = ctx.regs[args[0]];`);
|
|
839
|
+
declareHandler(
|
|
840
|
+
OpCode.LoadGlobal,
|
|
841
|
+
`
|
|
842
|
+
${readArgs}
|
|
843
|
+
var propName = ctx.regs[args[0]];
|
|
844
|
+
var globalVal = ${ctxRef("globalScope")}[propName];
|
|
845
|
+
${regRef("args[1]")} = (typeof ${top.result} !== 'undefined' && ${top.result}[propName] !== undefined)
|
|
846
|
+
? ${top.result}[propName]
|
|
847
|
+
: globalVal;
|
|
848
|
+
`
|
|
849
|
+
);
|
|
850
|
+
declareHandler(
|
|
851
|
+
OpCode.StoreGlobal,
|
|
852
|
+
`
|
|
853
|
+
${readArgs}
|
|
854
|
+
var propName = ctx.regs[args[0]];
|
|
855
|
+
${ctxRef("globalScope")}[propName] = ${regRef("args[1]")};
|
|
856
|
+
`
|
|
857
|
+
);
|
|
858
|
+
declareHandler(OpCode.LoadThis, `${readArgs} ${regRef("args[0]")} = ${ctxRef("thisArg")};`);
|
|
859
|
+
declareHandler(OpCode.LoadNewTarget, `${readArgs} ${regRef("args[0]")} = ${ctxRef("newTarget")};`);
|
|
860
|
+
declareHandler(
|
|
861
|
+
OpCode.Add,
|
|
862
|
+
`${readArgs} ctx.regs[args[2]] = (typeof ctx.regs[args[0]] === 'string' || typeof ctx.regs[args[1]] === 'string') ? (ctx.regs[args[0]] + ctx.regs[args[1]]) : (ctx.regs[args[0]] + ctx.regs[args[1]]);`
|
|
863
|
+
);
|
|
864
|
+
declareHandler(
|
|
865
|
+
OpCode.Sub,
|
|
866
|
+
`${readArgs} ctx.regs[args[2]] = (typeof ctx.regs[args[0]] === 'number' && typeof ctx.regs[args[1]] === 'number') ? __SUB_EXPR__ : (ctx.regs[args[0]] - ctx.regs[args[1]]);`
|
|
867
|
+
);
|
|
868
|
+
declareHandler(OpCode.Mul, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] * ctx.regs[args[1]];`);
|
|
869
|
+
declareHandler(OpCode.Div, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] / ctx.regs[args[1]];`);
|
|
870
|
+
declareHandler(OpCode.Mod, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] % ctx.regs[args[1]];`);
|
|
871
|
+
declareHandler(OpCode.Neg, `${readArgs} ctx.regs[args[1]] = -ctx.regs[args[0]];`);
|
|
872
|
+
declareHandler(OpCode.BitAnd, `${readArgs} ctx.regs[args[2]] = (ctx.regs[args[0]] & ctx.regs[args[1]]);`);
|
|
873
|
+
declareHandler(OpCode.BitOr, `${readArgs} ctx.regs[args[2]] = (ctx.regs[args[0]] | ctx.regs[args[1]]);`);
|
|
874
|
+
declareHandler(OpCode.BitXor, `${readArgs} ctx.regs[args[2]] = (ctx.regs[args[0]] ^ ctx.regs[args[1]]);`);
|
|
875
|
+
declareHandler(OpCode.Shl, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] << ctx.regs[args[1]];`);
|
|
876
|
+
declareHandler(OpCode.Shr, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] >> ctx.regs[args[1]];`);
|
|
877
|
+
declareHandler(OpCode.UShr, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] >>> ctx.regs[args[1]];`);
|
|
878
|
+
declareHandler(OpCode.Not, `${readArgs} ctx.regs[args[1]] = !ctx.regs[args[0]];`);
|
|
879
|
+
declareHandler(OpCode.Eq, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] == ctx.regs[args[1]];`);
|
|
880
|
+
declareHandler(OpCode.StrictEq, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] === ctx.regs[args[1]];`);
|
|
881
|
+
declareHandler(OpCode.Lt, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] < ctx.regs[args[1]];`);
|
|
882
|
+
declareHandler(OpCode.LtEq, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] <= ctx.regs[args[1]];`);
|
|
883
|
+
declareHandler(OpCode.Gt, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] > ctx.regs[args[1]];`);
|
|
884
|
+
declareHandler(OpCode.GtEq, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] >= ctx.regs[args[1]];`);
|
|
885
|
+
declareHandler(OpCode.TypeOf, `${readArgs} ctx.regs[args[1]] = typeof ctx.regs[args[0]];`);
|
|
886
|
+
declareHandler(OpCode.InstanceOf, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] instanceof ctx.regs[args[1]];`);
|
|
887
|
+
declareHandler(OpCode.In, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]] in ctx.regs[args[1]];`);
|
|
888
|
+
declareHandler(OpCode.PropGet, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]][ctx.regs[args[1]]];`);
|
|
889
|
+
declareHandler(OpCode.PropSet, `${readArgs} ctx.regs[args[0]][ctx.regs[args[1]]] = ctx.regs[args[2]];`);
|
|
890
|
+
declareHandler(OpCode.ComputedGet, `${readArgs} ctx.regs[args[2]] = ctx.regs[args[0]][ctx.regs[args[1]]];`);
|
|
891
|
+
declareHandler(OpCode.ComputedSet, `${readArgs} ctx.regs[args[0]][ctx.regs[args[1]]] = ctx.regs[args[2]];`);
|
|
892
|
+
declareHandler(OpCode.ArrayNew, `${readArgs} ctx.regs[args[0]] = [];`);
|
|
893
|
+
declareHandler(OpCode.ObjectNew, `${readArgs} ctx.regs[args[0]] = {};`);
|
|
894
|
+
declareHandler(OpCode.Delete, `${readArgs} delete ctx.regs[args[0]][ctx.regs[args[1]]];`);
|
|
895
|
+
declareHandler(OpCode.CellNew, `${readArgs} ctx.regs[args[1]] = { v: ctx.regs[args[0]] };`);
|
|
896
|
+
declareHandler(OpCode.CellGet, `${readArgs} ctx.regs[args[1]] = ctx.regs[args[0]].v;`);
|
|
897
|
+
declareHandler(OpCode.CellSet, `${readArgs} ctx.regs[args[0]].v = ctx.regs[args[1]];`);
|
|
898
|
+
declareHandler(OpCode.EnvGet, `${readArgs} ${regRef("args[1]")} = ${ctxRef("env")}[args[0]];`);
|
|
899
|
+
declareHandler(OpCode.RestArgs, `${readArgs} ${regRef("args[1]")} = ${ctxRef("fnArgs")}.slice(${regRef("args[0]")});`);
|
|
900
|
+
declareHandler(
|
|
901
|
+
OpCode.ClosureNew,
|
|
902
|
+
`
|
|
903
|
+
${readArgs}
|
|
904
|
+
${regRef("args[2]")} = ${top.getExecutorById}(${top.getCP}(ctx, args[0]), ${regRef("args[1]")});
|
|
905
|
+
`
|
|
906
|
+
);
|
|
907
|
+
declareHandler(
|
|
908
|
+
OpCode.Spread,
|
|
909
|
+
`
|
|
910
|
+
${readArgs}
|
|
911
|
+
var spreadTarget = ${regRef("args[0]")};
|
|
912
|
+
var spreadSource = ${regRef("args[1]")};
|
|
913
|
+
if (Array.isArray(spreadTarget)) {
|
|
914
|
+
if (spreadSource == null || typeof spreadSource[Symbol.iterator] !== 'function') {
|
|
915
|
+
throw new TypeError('VM spread source is not iterable');
|
|
916
|
+
}
|
|
917
|
+
var spreadIterator = spreadSource[Symbol.iterator]();
|
|
918
|
+
var spreadStep;
|
|
919
|
+
while (!(spreadStep = spreadIterator.next()).done) {
|
|
920
|
+
spreadTarget.push(spreadStep.value);
|
|
921
|
+
}
|
|
922
|
+
} else if (spreadSource != null) {
|
|
923
|
+
var spreadKeys = Object.keys(spreadSource);
|
|
924
|
+
for (var ski = 0; ski < spreadKeys.length; ski++) {
|
|
925
|
+
var spreadKey = spreadKeys[ski];
|
|
926
|
+
spreadTarget[spreadKey] = spreadSource[spreadKey];
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
`
|
|
930
|
+
);
|
|
931
|
+
declareHandler(
|
|
932
|
+
OpCode.SpreadIntoArray,
|
|
933
|
+
`
|
|
934
|
+
${readArgs}
|
|
935
|
+
var indexedSpreadTarget = ${regRef("args[0]")};
|
|
936
|
+
var indexedSpreadSource = ${regRef("args[1]")};
|
|
937
|
+
var indexedSpreadStart = ${regRef("args[2]")};
|
|
938
|
+
if (indexedSpreadSource == null || typeof indexedSpreadSource[Symbol.iterator] !== 'function') {
|
|
939
|
+
throw new TypeError('VM spread source is not iterable');
|
|
940
|
+
}
|
|
941
|
+
var indexedSpreadIterator = indexedSpreadSource[Symbol.iterator]();
|
|
942
|
+
var indexedSpreadStep;
|
|
943
|
+
var indexedSpreadCount = 0;
|
|
944
|
+
while (!(indexedSpreadStep = indexedSpreadIterator.next()).done) {
|
|
945
|
+
indexedSpreadTarget[indexedSpreadStart + indexedSpreadCount] = indexedSpreadStep.value;
|
|
946
|
+
indexedSpreadCount++;
|
|
947
|
+
}
|
|
948
|
+
${regRef("args[3]")} = indexedSpreadCount;
|
|
949
|
+
`
|
|
950
|
+
);
|
|
951
|
+
declareHandler(
|
|
952
|
+
OpCode.CallMethod,
|
|
953
|
+
`
|
|
954
|
+
${readArgs}
|
|
955
|
+
var obj = ${regRef("args[0]")};
|
|
956
|
+
var method = obj[ctx.regs[args[1]]];
|
|
957
|
+
var aa = [];
|
|
958
|
+
for (var ci = 2; ci < args.length - 1; ci++) {
|
|
959
|
+
aa.push(${regRef("args[ci]")});
|
|
960
|
+
}
|
|
961
|
+
${regRef("args[args.length - 1]")} = ${top.nativeApply}.call(method, obj, aa);
|
|
962
|
+
`
|
|
963
|
+
);
|
|
964
|
+
declareHandler(
|
|
965
|
+
OpCode.Call,
|
|
966
|
+
`
|
|
967
|
+
${readArgs}
|
|
968
|
+
var fn = ${regRef("args[0]")};
|
|
969
|
+
if (typeof fn === 'undefined' || fn === null) {
|
|
970
|
+
throw new TypeError('VM Call target is undefined or null');
|
|
971
|
+
}
|
|
972
|
+
var ab = [];
|
|
973
|
+
for (var ci2 = 1; ci2 < args.length - 1; ci2++) {
|
|
974
|
+
ab.push(${regRef("args[ci2]")});
|
|
975
|
+
}
|
|
976
|
+
${regRef("args[args.length - 1]")} = ${top.nativeApply}.call(fn, null, ab);
|
|
977
|
+
`
|
|
978
|
+
);
|
|
979
|
+
declareHandler(
|
|
980
|
+
OpCode.CallWithArray,
|
|
981
|
+
`
|
|
982
|
+
${readArgs}
|
|
983
|
+
var fnArray = ctx.regs[args[0]];
|
|
984
|
+
ctx.regs[args[2]] = ${top.nativeApply}.call(fnArray, null, ctx.regs[args[1]]);
|
|
985
|
+
`
|
|
986
|
+
);
|
|
987
|
+
declareHandler(
|
|
988
|
+
OpCode.CallMethodWithArray,
|
|
989
|
+
`
|
|
990
|
+
${readArgs}
|
|
991
|
+
var methodObj = ctx.regs[args[0]];
|
|
992
|
+
var methodFn = methodObj[ctx.regs[args[1]]];
|
|
993
|
+
ctx.regs[args[3]] = ${top.nativeApply}.call(methodFn, methodObj, ctx.regs[args[2]]);
|
|
994
|
+
`
|
|
995
|
+
);
|
|
996
|
+
declareHandler(
|
|
997
|
+
OpCode.New,
|
|
998
|
+
`
|
|
999
|
+
${readArgs}
|
|
1000
|
+
var ctor = ctx.regs[args[0]];
|
|
1001
|
+
var ctorArgs = [];
|
|
1002
|
+
for (var ni = 1; ni < args.length - 1; ni++) {
|
|
1003
|
+
ctorArgs.push(ctx.regs[args[ni]]);
|
|
1004
|
+
}
|
|
1005
|
+
ctx.regs[args[args.length - 1]] = typeof Reflect !== 'undefined' && Reflect.construct
|
|
1006
|
+
? Reflect.construct(ctor, ctorArgs)
|
|
1007
|
+
: new (${top.nativeApply}.call((cleanIntrinsics.Function || Function).prototype.bind, ctor, [null].concat(ctorArgs)))();
|
|
1008
|
+
`
|
|
1009
|
+
);
|
|
1010
|
+
declareHandler(
|
|
1011
|
+
OpCode.NewWithArray,
|
|
1012
|
+
`
|
|
1013
|
+
${readArgs}
|
|
1014
|
+
var ctorArray = ctx.regs[args[0]];
|
|
1015
|
+
var ctorArrayArgs = ctx.regs[args[1]];
|
|
1016
|
+
ctx.regs[args[2]] = typeof Reflect !== 'undefined' && Reflect.construct
|
|
1017
|
+
? Reflect.construct(ctorArray, ctorArrayArgs)
|
|
1018
|
+
: new (${top.nativeApply}.call((cleanIntrinsics.Function || Function).prototype.bind, ctorArray, [null].concat(ctorArrayArgs)))();
|
|
1019
|
+
`
|
|
1020
|
+
);
|
|
1021
|
+
declareHandler(
|
|
1022
|
+
OpCode.Jmp,
|
|
1023
|
+
`${readArgs} ${ctxRef("pc")} = (kinds[0] === 0) ? ctx.regs[args[0]] : args[0];${config.rollingKeys ? ` ctx.${ctx.pathHash} = 0;` : ""}`
|
|
1024
|
+
);
|
|
1025
|
+
declareHandler(
|
|
1026
|
+
OpCode.JmpIf,
|
|
1027
|
+
`${readArgs} ${ctxRef("pc")} = ${regRef("args[0]")} ? args[1] : args[2];${config.rollingKeys ? ` ctx.${ctx.pathHash} = 0;` : ""}`
|
|
1028
|
+
);
|
|
1029
|
+
declareHandler(
|
|
1030
|
+
OpCode.JmpIfNot,
|
|
1031
|
+
`${readArgs} ${ctxRef("pc")} = !${regRef("args[0]")} ? args[1] : args[2];${config.rollingKeys ? ` ctx.${ctx.pathHash} = 0;` : ""}`
|
|
1032
|
+
);
|
|
1033
|
+
declareHandler(
|
|
1034
|
+
OpCode.Return,
|
|
1035
|
+
`${readArgs} ${ctxRef("returnValue")} = args.length > 0 ? ${regRef("args[0]")} : undefined; ${ctxRef("running")} = false;`
|
|
1036
|
+
);
|
|
1037
|
+
declareHandler(OpCode.ReturnVoid, `${readArgs} ${ctxRef("returnValue")} = undefined; ${ctxRef("running")} = false;`);
|
|
1038
|
+
declareHandler(OpCode.Throw, `${readArgs} throw (args.length > 0 ? ${regRef("args[0]")} : undefined);`);
|
|
1039
|
+
declareHandler(
|
|
1040
|
+
OpCode.TryCatchBegin,
|
|
1041
|
+
`
|
|
1042
|
+
${readArgs}
|
|
1043
|
+
${ctxRef("tryFrames")}.push({
|
|
1044
|
+
${frame.catchPc}: args[0],
|
|
1045
|
+
${frame.endPc}: args[1],
|
|
1046
|
+
${frame.exceptionReg}: args[2],
|
|
1047
|
+
});
|
|
1048
|
+
`
|
|
1049
|
+
);
|
|
1050
|
+
declareHandler(OpCode.TryCatchEnd, `${readArgs} if (${ctxRef("tryFrames")}.length > 0) { ${ctxRef("tryFrames")}.pop(); }`);
|
|
1051
|
+
declareHandler(
|
|
1052
|
+
OpCode.Await,
|
|
1053
|
+
`
|
|
1054
|
+
${readArgs}
|
|
1055
|
+
${ctxRef("awaitPromise")} = Promise.resolve(${regRef("args[0]")});
|
|
1056
|
+
${ctxRef("resumeReg")} = args[1];
|
|
1057
|
+
${ctxRef("resumeMode")} = 'await';
|
|
1058
|
+
${ctxRef("running")} = false;
|
|
1059
|
+
${config.rollingKeys ? `ctx.${ctx.pathHash} = 0;` : ""}
|
|
1060
|
+
`
|
|
1061
|
+
);
|
|
1062
|
+
declareHandler(OpCode.Nop, `${readArgs} /* Junk */`);
|
|
1063
|
+
declareHandler(OpCode.Halt, `${ctxRef("running")} = false;`);
|
|
1064
|
+
declareHandler(
|
|
1065
|
+
OpCode.PrivateGet,
|
|
1066
|
+
`
|
|
1067
|
+
${readArgs}
|
|
1068
|
+
var obj = ctx.regs[args[0]];
|
|
1069
|
+
var key = ctx.regs[args[1]];
|
|
1070
|
+
var p = ${top.weakMapGet}.call(${top.privateData}, obj);
|
|
1071
|
+
if (!p || !(key in p)) throw new TypeError(${runtimeStringRef("Cannot read private member")});
|
|
1072
|
+
ctx.regs[args[2]] = p[key];
|
|
1073
|
+
`
|
|
1074
|
+
);
|
|
1075
|
+
declareHandler(
|
|
1076
|
+
OpCode.PrivateSet,
|
|
1077
|
+
`
|
|
1078
|
+
${readArgs}
|
|
1079
|
+
var obj = ctx.regs[args[0]];
|
|
1080
|
+
var key = ctx.regs[args[1]];
|
|
1081
|
+
var p = ${top.weakMapGet}.call(${top.privateData}, obj);
|
|
1082
|
+
if (!p) { p = {}; ${top.weakMapSet}.call(${top.privateData}, obj, p); }
|
|
1083
|
+
p[key] = ctx.regs[args[2]];
|
|
1084
|
+
`
|
|
1085
|
+
);
|
|
1086
|
+
declareHandler(
|
|
1087
|
+
OpCode.PrivateIn,
|
|
1088
|
+
`
|
|
1089
|
+
${readArgs}
|
|
1090
|
+
var obj = ctx.regs[args[0]];
|
|
1091
|
+
var key = ctx.regs[args[1]];
|
|
1092
|
+
var p = ${top.weakMapGet}.call(${top.privateData}, obj);
|
|
1093
|
+
ctx.regs[args[2]] = p ? (key in p) : false;
|
|
1094
|
+
`
|
|
1095
|
+
);
|
|
1096
|
+
declareHandler(
|
|
1097
|
+
OpCode.SuperPropGet,
|
|
1098
|
+
`
|
|
1099
|
+
${readArgs}
|
|
1100
|
+
var isStatic = typeof ${ctxRef("thisArg")} === 'function';
|
|
1101
|
+
var superProto = isStatic
|
|
1102
|
+
? ${top.objectObj}.getPrototypeOf(${ctxRef("thisArg")})
|
|
1103
|
+
: ${top.objectObj}.getPrototypeOf(${top.objectObj}.getPrototypeOf(${ctxRef("thisArg")}));
|
|
1104
|
+
ctx.regs[args[1]] = superProto[ctx.regs[args[0]]];
|
|
1105
|
+
`
|
|
1106
|
+
);
|
|
1107
|
+
declareHandler(
|
|
1108
|
+
OpCode.SuperPropSet,
|
|
1109
|
+
`
|
|
1110
|
+
${readArgs}
|
|
1111
|
+
var isStatic = typeof ${ctxRef("thisArg")} === 'function';
|
|
1112
|
+
var superProto = isStatic
|
|
1113
|
+
? ${top.objectObj}.getPrototypeOf(${ctxRef("thisArg")})
|
|
1114
|
+
: ${top.objectObj}.getPrototypeOf(${top.objectObj}.getPrototypeOf(${ctxRef("thisArg")}));
|
|
1115
|
+
superProto[ctx.regs[args[0]]] = ctx.regs[args[1]];
|
|
1116
|
+
`
|
|
1117
|
+
);
|
|
1118
|
+
declareHandler(
|
|
1119
|
+
OpCode.SuperCall,
|
|
1120
|
+
`
|
|
1121
|
+
${readArgs}
|
|
1122
|
+
if (!${top.reflectObj} || !${top.reflectObj}.construct) {
|
|
1123
|
+
throw new TypeError('Reflect.construct is required for super()');
|
|
1124
|
+
}
|
|
1125
|
+
var superCtor = ${top.objectObj}.getPrototypeOf(${ctxRef("newTarget")});
|
|
1126
|
+
var aa = [];
|
|
1127
|
+
for (var ci = 0; ci < args.length - 1; ci++) {
|
|
1128
|
+
aa.push(ctx.regs[args[ci]]);
|
|
1129
|
+
}
|
|
1130
|
+
${ctxRef("thisArg")} = ${top.reflectObj}.construct(superCtor, aa, ${ctxRef("newTarget")});
|
|
1131
|
+
ctx.regs[args[args.length - 1]] = ${ctxRef("thisArg")};
|
|
1132
|
+
`
|
|
1133
|
+
);
|
|
1134
|
+
declareHandler(
|
|
1135
|
+
OpCode.SuperCallWithArray,
|
|
1136
|
+
`
|
|
1137
|
+
${readArgs}
|
|
1138
|
+
if (!${top.reflectObj} || !${top.reflectObj}.construct) {
|
|
1139
|
+
throw new TypeError('Reflect.construct is required for super()');
|
|
1140
|
+
}
|
|
1141
|
+
var superCtor = ${top.objectObj}.getPrototypeOf(${ctxRef("newTarget")});
|
|
1142
|
+
${ctxRef("thisArg")} = ${top.reflectObj}.construct(superCtor, ctx.regs[args[0]], ${ctxRef("newTarget")});
|
|
1143
|
+
ctx.regs[args[1]] = ${ctxRef("thisArg")};
|
|
1144
|
+
`
|
|
1145
|
+
);
|
|
1146
|
+
declareHandler(
|
|
1147
|
+
OpCode.Yield,
|
|
1148
|
+
`
|
|
1149
|
+
${readArgs}
|
|
1150
|
+
${ctxRef("resumeReg")} = args[1];
|
|
1151
|
+
${ctxRef("resumeMode")} = 'yield';
|
|
1152
|
+
${ctxRef("resumeValue")} = ctx.regs[args[0]];
|
|
1153
|
+
${ctxRef("running")} = false;
|
|
1154
|
+
${config.rollingKeys ? `ctx.${ctx.pathHash} = 0;` : ""}
|
|
1155
|
+
`
|
|
1156
|
+
);
|
|
1157
|
+
declareHandler(
|
|
1158
|
+
OpCode.YieldStar,
|
|
1159
|
+
`
|
|
1160
|
+
${readArgs}
|
|
1161
|
+
${ctxRef("resumeReg")} = args[1];
|
|
1162
|
+
${ctxRef("resumeMode")} = 'yieldStar';
|
|
1163
|
+
${ctxRef("resumeValue")} = ctx.regs[args[0]];
|
|
1164
|
+
${ctxRef("running")} = false;
|
|
1165
|
+
${config.rollingKeys ? `ctx.${ctx.pathHash} = 0;` : ""}
|
|
1166
|
+
`
|
|
1167
|
+
);
|
|
1168
|
+
declareHandler(
|
|
1169
|
+
OpCode.GetEntropy,
|
|
1170
|
+
`
|
|
1171
|
+
${readArgs}
|
|
1172
|
+
var t = 0;
|
|
1173
|
+
try {
|
|
1174
|
+
if (${top.performanceNow}) {
|
|
1175
|
+
t = ${top.performanceNow}();
|
|
1176
|
+
} else if (typeof Date !== 'undefined' && typeof Date.now === 'function') {
|
|
1177
|
+
t = Date.now();
|
|
1178
|
+
}
|
|
1179
|
+
} catch(e) {}
|
|
1180
|
+
var r = 0;
|
|
1181
|
+
try {
|
|
1182
|
+
r = ${top.mathRandom}() * 1000;
|
|
1183
|
+
} catch(e) {}
|
|
1184
|
+
var tBits = (t * 1000) & 0xFFFFFFFF;
|
|
1185
|
+
var rBits = r & 0xFFFFFFFF;
|
|
1186
|
+
var entropy = (ctx.${ctx.rollingState} ^ (${ctxRef("pc")} * 2654435761) ^ tBits ^ rBits) >>> 0;
|
|
1187
|
+
entropy = (entropy ^ (entropy >>> 16)) & 0xFF;
|
|
1188
|
+
ctx.regs[args[0]] = entropy;
|
|
1189
|
+
`
|
|
1190
|
+
);
|
|
1191
|
+
const superRng = new SeededRandom(config.seed);
|
|
1192
|
+
const superIds = superRng.shuffle([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
1193
|
+
const loadConstMulId = superIds[0];
|
|
1194
|
+
const getEntropyMulId = superIds[1];
|
|
1195
|
+
const loadConstAddId = superIds[2];
|
|
1196
|
+
declareHandler(
|
|
1197
|
+
OpCode.SuperInstruction,
|
|
1198
|
+
`
|
|
1199
|
+
${readArgs}
|
|
1200
|
+
var patternId = args[0];
|
|
1201
|
+
if (patternId === ${loadConstMulId}) {
|
|
1202
|
+
var val = (kinds[1] === 2) ? ${top.getCP}(ctx, args[1]) : args[1];
|
|
1203
|
+
ctx.regs[args[2]] = val;
|
|
1204
|
+
ctx.regs[args[4]] = val * ctx.regs[args[3]];
|
|
1205
|
+
} else if (patternId === ${getEntropyMulId}) {
|
|
1206
|
+
var entropy = (ctx.${ctx.rollingState} ^ (${ctxRef("pc")} * 2654435761)) >>> 0;
|
|
1207
|
+
entropy = (entropy ^ (entropy >>> 16)) & 0xFF;
|
|
1208
|
+
ctx.regs[args[1]] = entropy;
|
|
1209
|
+
ctx.regs[args[2]] = entropy * entropy;
|
|
1210
|
+
} else if (patternId === ${loadConstAddId}) {
|
|
1211
|
+
var val = (kinds[1] === 2) ? ${top.getCP}(ctx, args[1]) : args[1];
|
|
1212
|
+
ctx.regs[args[2]] = val;
|
|
1213
|
+
ctx.regs[args[4]] = val + ctx.regs[args[3]];
|
|
1214
|
+
} else {
|
|
1215
|
+
throw new Error('Invalid Super-Instruction Pattern: ' + patternId);
|
|
1216
|
+
}
|
|
1217
|
+
`
|
|
1218
|
+
);
|
|
1219
|
+
const antiDebugLogic = config.antiDebug ? `
|
|
1220
|
+
// Anti-Debug DevTools & Trace Protection (Self-Destruct Trap)
|
|
1221
|
+
// \u26A0\uFE0F WARNING: Contains literal \`debugger;\` statement. This will trigger
|
|
1222
|
+
// breakpoints in DevTools. Remove this entire block if distributing to production.
|
|
1223
|
+
var _dbg_start = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
1224
|
+
debugger;
|
|
1225
|
+
selfDestruct(ctx);
|
|
1226
|
+
}
|
|
1227
|
+
// Opaque getter trap to detect automated inspect / DevTools formatting
|
|
1228
|
+
var _rTrap = /./;
|
|
1229
|
+
${top.nativeDefineProperty}(_rTrap, 'source', {
|
|
1230
|
+
get: function() {
|
|
1231
|
+
selfDestruct(ctx);
|
|
1232
|
+
return 'trap';
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
` : "";
|
|
1236
|
+
const tamperDetectionLogic = config.tamperDetection ? `
|
|
1237
|
+
// Multi-layer native intrinsics verification (hardened against toString spoofing)
|
|
1238
|
+
var _isNative = function(fn) {
|
|
1239
|
+
try {
|
|
1240
|
+
if (typeof fn !== 'function') return false;
|
|
1241
|
+
var s = ${top.nativeCall}.call(${top.nativeToString}, fn);
|
|
1242
|
+
var regexPass = ${top.nativeCall}.call(RegExp.prototype.test, /^\\s*function\\s*[a-zA-Z0-9_$]*\\s*\\(\\s*\\)\\s*\\{\\s*\\[native code\\]\\s*\\}\\s*$/, s) ||
|
|
1243
|
+
(${top.nativeCall}.call(String.prototype.indexOf, s, '[native code]') !== -1 && ${top.nativeCall}.call(String.prototype.indexOf, s, 'function') !== -1);
|
|
1244
|
+
if (!regexPass) return false;
|
|
1245
|
+
// Cross-check: verify our cached toString reference itself reports native
|
|
1246
|
+
var selfCheck = ${top.nativeCall}.call(${top.nativeToString}, ${top.nativeToString});
|
|
1247
|
+
if (${top.nativeCall}.call(String.prototype.indexOf, selfCheck, '[native code]') === -1) return false;
|
|
1248
|
+
// Prototype identity: native functions have non-configurable prototype
|
|
1249
|
+
var protoDesc = ${top.nativeGetOwnPropertyDescriptor}(fn, 'prototype');
|
|
1250
|
+
// Most builtins lack own 'prototype' (WeakMap.prototype.get, etc.)
|
|
1251
|
+
// If present, it should not be configurable on native constructors
|
|
1252
|
+
if (protoDesc && protoDesc.configurable && fn.length === 0) return false;
|
|
1253
|
+
// Name consistency: native function name should not be writable
|
|
1254
|
+
var nameDesc = ${top.nativeGetOwnPropertyDescriptor}(fn, 'name');
|
|
1255
|
+
if (nameDesc && nameDesc.writable) return false;
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
catch (_) { return false; }
|
|
1259
|
+
};
|
|
1260
|
+
var fnStr = ${top.nativeCall}.call(${top.nativeToString}, __runVm);
|
|
1261
|
+
var hasDbg = 0;
|
|
1262
|
+
for (var i = 0; i < fnStr.length - 7; i++) {
|
|
1263
|
+
if (fnStr.charCodeAt(i) === 100 && fnStr.charCodeAt(i+1) === 101 && fnStr.charCodeAt(i+2) === 98 && fnStr.charCodeAt(i+3) === 117) {
|
|
1264
|
+
hasDbg = 1; break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// Intrinsic verification for descriptor descriptors and identity
|
|
1269
|
+
var verifyIntrinsic = function(obj, prop, expectedNative) {
|
|
1270
|
+
if (!obj || !prop) return false;
|
|
1271
|
+
var desc = ${top.nativeGetOwnPropertyDescriptor}(obj, prop);
|
|
1272
|
+
if (!desc) return false;
|
|
1273
|
+
if (expectedNative && !_isNative(desc.value || desc.get)) return false;
|
|
1274
|
+
return true;
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
// Active hook probes: call native methods on controlled inputs and verify expected behavior
|
|
1278
|
+
var probeActive = function() {
|
|
1279
|
+
try {
|
|
1280
|
+
var map = new ${top.weakMapCtor}();
|
|
1281
|
+
var key = {};
|
|
1282
|
+
${top.nativeCall}.call(${top.weakMapSet}, map, key, 42);
|
|
1283
|
+
if (${top.nativeCall}.call(${top.weakMapGet}, map, key) !== 42) return false;
|
|
1284
|
+
var sliceRes = ${top.nativeCall}.call(${top.arraySlice}, [1, 2], 1);
|
|
1285
|
+
if (!sliceRes || sliceRes[0] !== 2 || sliceRes.length !== 1) return false;
|
|
1286
|
+
return true;
|
|
1287
|
+
} catch (_) { return false; }
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
if (
|
|
1291
|
+
(!hasDbg && ${config.antiDebug}) ||
|
|
1292
|
+
fnStr.length < 50 ||
|
|
1293
|
+
!_isNative(${top.nativeMathSin}) ||
|
|
1294
|
+
!_isNative(${top.weakMapGet}) ||
|
|
1295
|
+
!_isNative(${top.weakMapSet}) ||
|
|
1296
|
+
!_isNative(${top.nativeToString}) ||
|
|
1297
|
+
!verifyIntrinsic((cleanIntrinsics.Function || Function).prototype, 'toString', true) ||
|
|
1298
|
+
!verifyIntrinsic((cleanIntrinsics.Array || Array).prototype, 'slice', true) ||
|
|
1299
|
+
!verifyIntrinsic(${top.objectObj}, 'defineProperty', true) ||
|
|
1300
|
+
!verifyIntrinsic(cleanIntrinsics.Promise || Promise, 'resolve', true) ||
|
|
1301
|
+
!verifyIntrinsic(${top.reflectObj}, 'construct', true) ||
|
|
1302
|
+
!probeActive()
|
|
1303
|
+
) {
|
|
1304
|
+
selfDestruct(ctx);
|
|
1305
|
+
}
|
|
1306
|
+
` : "";
|
|
1307
|
+
const runtimeDispatch = (() => {
|
|
1308
|
+
const vOpToHandlerName = new Array(256);
|
|
1309
|
+
const vOpToVariants = new Array(256);
|
|
1310
|
+
const trapFnName = handlerNames.get(OpCode.Trap);
|
|
1311
|
+
vOpToHandlerName.fill(trapFnName);
|
|
1312
|
+
vOpToVariants.fill(`[${trapFnName}]`);
|
|
1313
|
+
for (const [canonical, mapped] of opToMapped.entries()) {
|
|
1314
|
+
const fnNames = allHandlerVariants.get(canonical);
|
|
1315
|
+
if (!fnNames) continue;
|
|
1316
|
+
for (const vOp of mapped) {
|
|
1317
|
+
if (vOp >= 0 && vOp < 256) {
|
|
1318
|
+
const varIdx = (vOp ^ config.seed) % fnNames.length;
|
|
1319
|
+
vOpToHandlerName[vOp] = fnNames[varIdx];
|
|
1320
|
+
vOpToVariants[vOp] = `[${fnNames.join(", ")}]`;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (config.runtimeHardening === "paranoid") {
|
|
1325
|
+
const dispatchArch = config.seed % 5;
|
|
1326
|
+
if (dispatchArch === 0) {
|
|
1327
|
+
let casesStr = "";
|
|
1328
|
+
for (let b = 0; b < 16; b++) {
|
|
1329
|
+
let subCases = "";
|
|
1330
|
+
for (let offset = 0; offset < 16; offset++) {
|
|
1331
|
+
const vOp = b << 4 | offset;
|
|
1332
|
+
const variants = vOpToVariants[vOp];
|
|
1333
|
+
subCases += ` case ${offset}: var va = ${variants}; return va[(((ctx.${ctx.rollingState} >>> 16) ^ (ctx.${ctx.rollingState} & 0xFFFF)) & 0x7FFF) % va.length];
|
|
1334
|
+
`;
|
|
1335
|
+
}
|
|
1336
|
+
casesStr += ` case ${b}:
|
|
1337
|
+
switch (nextOp & 0xF) {
|
|
1338
|
+
${subCases} }
|
|
1339
|
+
break;
|
|
1340
|
+
`;
|
|
1341
|
+
}
|
|
1342
|
+
return {
|
|
1343
|
+
declarations: `
|
|
1344
|
+
function resolveRoute(ctx, token) {
|
|
1345
|
+
if (token === null) return null;
|
|
1346
|
+
var nextOp = (token ^ ctx.${ctx.rollingState}) & 0xFF;
|
|
1347
|
+
var bucket = (nextOp >> 4) & 0xF;
|
|
1348
|
+
switch (bucket) {
|
|
1349
|
+
${casesStr}
|
|
1350
|
+
}
|
|
1351
|
+
return ${trapFnName};
|
|
1352
|
+
}
|
|
1353
|
+
`,
|
|
1354
|
+
invoke: `resolveRoute(ctx, makeRouteToken(ctx, ${locals.opByte}));`
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
if (dispatchArch === 1) {
|
|
1358
|
+
const saltByte = (config.seed ^ 42483) & 255;
|
|
1359
|
+
const saltByte2 = (config.seed >>> 8 ^ 49841) & 255;
|
|
1360
|
+
const permTable = new Array(256).fill(`[${trapFnName}]`);
|
|
1361
|
+
for (let vOp = 0; vOp < 256; vOp++) {
|
|
1362
|
+
const nibSwap = (vOp >> 4 | (vOp & 15) << 4) & 255;
|
|
1363
|
+
const permuted = (nibSwap ^ saltByte ^ saltByte2) & 255;
|
|
1364
|
+
permTable[permuted] = vOpToVariants[vOp];
|
|
1365
|
+
}
|
|
1366
|
+
return {
|
|
1367
|
+
declarations: `
|
|
1368
|
+
var _hcVT = [
|
|
1369
|
+
${permTable.join(",\n ")}
|
|
1370
|
+
];
|
|
1371
|
+
function resolveRoute(ctx, token) {
|
|
1372
|
+
if (token === null) return null;
|
|
1373
|
+
var nextOp = (token ^ ctx.${ctx.rollingState}) & 0xFF;
|
|
1374
|
+
var nibSwap = ((nextOp >> 4) | ((nextOp & 0xF) << 4)) & 0xFF;
|
|
1375
|
+
var h = (nibSwap ^ ${saltByte} ^ ${saltByte2}) & 0xFF;
|
|
1376
|
+
var variants = _hcVT[h];
|
|
1377
|
+
if (!variants || !variants.length) return ${trapFnName};
|
|
1378
|
+
return variants[(((ctx.${ctx.rollingState} >>> 16) ^ (ctx.${ctx.rollingState} & 0xFFFF)) & 0x7FFF) % variants.length];
|
|
1379
|
+
}
|
|
1380
|
+
`,
|
|
1381
|
+
invoke: `resolveRoute(ctx, makeRouteToken(ctx, ${locals.opByte}));`
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
if (dispatchArch === 2) {
|
|
1385
|
+
const xorScramble = (config.seed ^ 2117967660) & 255;
|
|
1386
|
+
const scrambledTable = new Array(256).fill(`[${trapFnName}]`);
|
|
1387
|
+
for (let vOp = 0; vOp < 256; vOp++) {
|
|
1388
|
+
const idx = (vOp ^ xorScramble) & 255;
|
|
1389
|
+
scrambledTable[idx] = vOpToVariants[vOp];
|
|
1390
|
+
}
|
|
1391
|
+
return {
|
|
1392
|
+
declarations: `
|
|
1393
|
+
var _tfVT = [
|
|
1394
|
+
${scrambledTable.join(",\n ")}
|
|
1395
|
+
];
|
|
1396
|
+
function resolveRoute(ctx, token) {
|
|
1397
|
+
if (token === null) return null;
|
|
1398
|
+
var nextOp = (token ^ ctx.${ctx.rollingState}) & 0xFF;
|
|
1399
|
+
var scrambled = (nextOp ^ ${xorScramble}) & 0xFF;
|
|
1400
|
+
var variants = _tfVT[scrambled];
|
|
1401
|
+
if (!variants || !variants.length) return ${trapFnName};
|
|
1402
|
+
return variants[(((ctx.${ctx.rollingState} >>> 16) ^ (ctx.${ctx.rollingState} & 0xFFFF)) & 0x7FFF) % variants.length];
|
|
1403
|
+
}
|
|
1404
|
+
`,
|
|
1405
|
+
invoke: `resolveRoute(ctx, makeRouteToken(ctx, ${locals.opByte}));`
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
if (dispatchArch === 3) {
|
|
1409
|
+
const bitRevSalt = (config.seed ^ 19325) & 255;
|
|
1410
|
+
const bitRevTable = new Array(256).fill(`[${trapFnName}]`);
|
|
1411
|
+
for (let vOp = 0; vOp < 256; vOp++) {
|
|
1412
|
+
let rev = 0;
|
|
1413
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
1414
|
+
if (vOp & 1 << bit) rev |= 1 << 7 - bit;
|
|
1415
|
+
}
|
|
1416
|
+
const idx = (rev ^ bitRevSalt) & 255;
|
|
1417
|
+
bitRevTable[idx] = vOpToVariants[vOp];
|
|
1418
|
+
}
|
|
1419
|
+
return {
|
|
1420
|
+
declarations: `
|
|
1421
|
+
var _brVT = [
|
|
1422
|
+
${bitRevTable.join(",\n ")}
|
|
1423
|
+
];
|
|
1424
|
+
function _rev8(x) {
|
|
1425
|
+
x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
|
|
1426
|
+
x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
|
|
1427
|
+
x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
|
|
1428
|
+
return x & 0xFF;
|
|
1429
|
+
}
|
|
1430
|
+
function resolveRoute(ctx, token) {
|
|
1431
|
+
if (token === null) return null;
|
|
1432
|
+
var nextOp = (token ^ ctx.${ctx.rollingState}) & 0xFF;
|
|
1433
|
+
var h = (_rev8(nextOp) ^ ${bitRevSalt}) & 0xFF;
|
|
1434
|
+
var variants = _brVT[h];
|
|
1435
|
+
if (!variants || !variants.length) return ${trapFnName};
|
|
1436
|
+
return variants[(((ctx.${ctx.rollingState} >>> 16) ^ (ctx.${ctx.rollingState} & 0xFFFF)) & 0x7FFF) % variants.length];
|
|
1437
|
+
}
|
|
1438
|
+
`,
|
|
1439
|
+
invoke: `resolveRoute(ctx, makeRouteToken(ctx, ${locals.opByte}));`
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
const oddMul = config.seed & 127 | 1 | 2;
|
|
1443
|
+
const addConst4 = config.seed >>> 16 & 255;
|
|
1444
|
+
const affineTable = new Array(256).fill(`[${trapFnName}]`);
|
|
1445
|
+
for (let vOp = 0; vOp < 256; vOp++) {
|
|
1446
|
+
const idx = vOp * oddMul + addConst4 & 255;
|
|
1447
|
+
affineTable[idx] = vOpToVariants[vOp];
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
declarations: `
|
|
1451
|
+
var _afVT = [
|
|
1452
|
+
${affineTable.join(",\n ")}
|
|
1453
|
+
];
|
|
1454
|
+
function resolveRoute(ctx, token) {
|
|
1455
|
+
if (token === null) return null;
|
|
1456
|
+
var nextOp = (token ^ ctx.${ctx.rollingState}) & 0xFF;
|
|
1457
|
+
var h = ((nextOp * ${oddMul}) + ${addConst4}) & 0xFF;
|
|
1458
|
+
var variants = _afVT[h];
|
|
1459
|
+
if (!variants || !variants.length) return ${trapFnName};
|
|
1460
|
+
return variants[(((ctx.${ctx.rollingState} >>> 16) ^ (ctx.${ctx.rollingState} & 0xFFFF)) & 0x7FFF) % variants.length];
|
|
1461
|
+
}
|
|
1462
|
+
`,
|
|
1463
|
+
invoke: `resolveRoute(ctx, makeRouteToken(ctx, ${locals.opByte}));`
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
return {
|
|
1467
|
+
declarations: `
|
|
1468
|
+
const ${locals.dispatchBank} = [
|
|
1469
|
+
${vOpToHandlerName.join(",\n ")}
|
|
1470
|
+
];
|
|
1471
|
+
function resolveRoute(ctx, token) {
|
|
1472
|
+
if (token === null) return null;
|
|
1473
|
+
var nextOp = (token ^ ctx.${ctx.rollingState}) & 0xFF;
|
|
1474
|
+
return ${locals.dispatchBank}[nextOp];
|
|
1475
|
+
}
|
|
1476
|
+
`,
|
|
1477
|
+
invoke: `${locals.dispatchBank}[${locals.opByte}](ctx);`
|
|
1478
|
+
};
|
|
1479
|
+
})();
|
|
1480
|
+
const allBytecodes = [];
|
|
1481
|
+
const functionTable = {};
|
|
1482
|
+
let currentOffset = 0;
|
|
1483
|
+
for (const fn of module.functions) {
|
|
1484
|
+
const fnSalt = buildRng.nextRange(1, 4294967295);
|
|
1485
|
+
functionTable[fn.id] = {
|
|
1486
|
+
o: currentOffset,
|
|
1487
|
+
l: fn.bytecode.length,
|
|
1488
|
+
a: fn.attributes ? [...fn.attributes] : [],
|
|
1489
|
+
r: fn.maxRegisters,
|
|
1490
|
+
s: fnSalt
|
|
1491
|
+
};
|
|
1492
|
+
for (let i = 0; i < fn.bytecode.length; i++) {
|
|
1493
|
+
allBytecodes.push(fn.bytecode[i]);
|
|
1494
|
+
}
|
|
1495
|
+
currentOffset += fn.bytecode.length;
|
|
1496
|
+
}
|
|
1497
|
+
const arenaArray = allBytecodes.join(",");
|
|
1498
|
+
const fnTableStr = Object.entries(functionTable).map(([id, info]) => {
|
|
1499
|
+
return `${JSON.stringify(id)}: { o: ${info.o}, l: ${info.l}, a: ${JSON.stringify(info.a)}, r: ${info.r}, s: ${info.s} }`;
|
|
1500
|
+
}).join(",\n ");
|
|
1501
|
+
const handlerVariantsEntries = Array.from(allHandlerVariants.entries()).map(([canonical, names2]) => {
|
|
1502
|
+
return `[${canonical}]: [${names2.join(", ")}]`;
|
|
1503
|
+
}).join(",\n ");
|
|
1504
|
+
const handlerVariantsStr = `const handlerVariants = {
|
|
1505
|
+
${handlerVariantsEntries}
|
|
1506
|
+
};`;
|
|
1507
|
+
if (config.runtimeHardening === "paranoid") {
|
|
1508
|
+
handlerDeclarations.push(`function _fakeHandler_A(ctx) {
|
|
1509
|
+
var _r192 = ctx.regs;
|
|
1510
|
+
var a = _r192[0] ^ 0x3d2;
|
|
1511
|
+
var b = (a * 9172) | 0;
|
|
1512
|
+
_r192[1] = b ^ 0xdead;
|
|
1513
|
+
return (ctx.${ctx.rollingState} ^ 0xab) & 0xffff;
|
|
1514
|
+
}
|
|
1515
|
+
function _fakeHandler_B(ctx) {
|
|
1516
|
+
var _r928 = ctx.regs;
|
|
1517
|
+
var a = _r928[1] & 0xff;
|
|
1518
|
+
var b = Math.sin(a) * 4;
|
|
1519
|
+
_r928[2] = b | 0;
|
|
1520
|
+
return (ctx.${ctx.rollingState} ^ 0x3e) & 0xffff;
|
|
1521
|
+
}`);
|
|
1522
|
+
}
|
|
1523
|
+
const sourceCode = `
|
|
1524
|
+
// Polymorphic Threaded VM Engine - Build: ${module.buildId}
|
|
1525
|
+
const ${top.vmFunctions} = (function() {
|
|
1526
|
+
"use strict";
|
|
1527
|
+
const ${top.seed} = ${config.seed};
|
|
1528
|
+
const ${top.rawCP} = ${cp};
|
|
1529
|
+
|
|
1530
|
+
// Grab clean intrinsics from a secure isolated context.
|
|
1531
|
+
// Uses iframe (browser) ONLY \u2014 require('vm') is deliberately excluded
|
|
1532
|
+
// due to known sandbox escape vulnerabilities (CVE-2023-37903, etc.).
|
|
1533
|
+
// For Node.js environments, pre-sealed intrinsics should be provided via config.
|
|
1534
|
+
var cleanIntrinsics = (function() {
|
|
1535
|
+
var win;
|
|
1536
|
+
try {
|
|
1537
|
+
if (typeof document !== 'undefined' && typeof document.createElement === 'function') {
|
|
1538
|
+
var iframe = document.createElement('iframe');
|
|
1539
|
+
iframe.style.display = 'none';
|
|
1540
|
+
document.documentElement.appendChild(iframe);
|
|
1541
|
+
win = iframe.contentWindow;
|
|
1542
|
+
try {
|
|
1543
|
+
if (iframe && iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
1544
|
+
} catch(e) {}
|
|
1545
|
+
}
|
|
1546
|
+
} catch (e) {}
|
|
1547
|
+
return win || {};
|
|
1548
|
+
})();
|
|
1549
|
+
|
|
1550
|
+
const ${top.weakMapCtor} = cleanIntrinsics.WeakMap || WeakMap;
|
|
1551
|
+
const ${top.weakMapGet} = ${top.weakMapCtor}.prototype.get;
|
|
1552
|
+
const ${top.weakMapSet} = ${top.weakMapCtor}.prototype.set;
|
|
1553
|
+
const ${top.reflectObj} = cleanIntrinsics.Reflect || (typeof Reflect !== 'undefined' ? Reflect : undefined);
|
|
1554
|
+
const ${top.objectObj} = cleanIntrinsics.Object || Object;
|
|
1555
|
+
const ${top.nativeToString} = (cleanIntrinsics.Function || Function).prototype.toString;
|
|
1556
|
+
const ${top.nativeMathSin} = (cleanIntrinsics.Math || Math).sin;
|
|
1557
|
+
const ${top.arraySlice} = (cleanIntrinsics.Array || Array).prototype.slice;
|
|
1558
|
+
const ${top.promiseResolve} = (cleanIntrinsics.Promise || Promise).resolve.bind(cleanIntrinsics.Promise || Promise);
|
|
1559
|
+
const ${top.iteratorSymbol} = typeof Symbol !== 'undefined' ? Symbol.iterator : '@@iterator';
|
|
1560
|
+
const ${top.asyncIteratorSymbol} = typeof Symbol !== 'undefined' && Symbol.asyncIterator ? Symbol.asyncIterator : null;
|
|
1561
|
+
const ${top.privateData} = new ${top.weakMapCtor}();
|
|
1562
|
+
|
|
1563
|
+
// Secure local caches of essential operations
|
|
1564
|
+
const ${top.nativeDefineProperty} = ${top.objectObj}.defineProperty;
|
|
1565
|
+
const ${top.nativeGetOwnPropertyDescriptor} = ${top.objectObj}.getOwnPropertyDescriptor;
|
|
1566
|
+
const ${top.nativeApply} = (cleanIntrinsics.Function || Function).prototype.apply;
|
|
1567
|
+
const ${top.nativeCall} = (cleanIntrinsics.Function || Function).prototype.call;
|
|
1568
|
+
const ${top.performanceNow} = (cleanIntrinsics.performance && cleanIntrinsics.performance.now) ? cleanIntrinsics.performance.now.bind(cleanIntrinsics.performance) : (typeof performance !== 'undefined' && performance.now) ? performance.now.bind(performance) : null;
|
|
1569
|
+
const ${top.mathRandom} = (cleanIntrinsics.Math || Math).random;
|
|
1570
|
+
let executionCounter = 0;
|
|
1571
|
+
|
|
1572
|
+
function selfDestruct(ctx) {
|
|
1573
|
+
if (!ctx) return;
|
|
1574
|
+
ctx.poisoned = true;
|
|
1575
|
+
ctx.${ctx.running} = false;
|
|
1576
|
+
ctx.${ctx.pc} = 999999;
|
|
1577
|
+
if (ctx.${ctx.bytecode}) {
|
|
1578
|
+
for (var i = 0; i < ctx.${ctx.bytecode}.length; i++) {
|
|
1579
|
+
ctx.${ctx.bytecode}[i] = 0;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
if (ctx.${ctx.xorLog}) {
|
|
1583
|
+
for (var i = 0; i < ctx.${ctx.xorLog}.length; i++) {
|
|
1584
|
+
ctx.${ctx.xorLog}[i] = 0;
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
try {
|
|
1588
|
+
for (var i = 0; i < ctx.regCount; i++) {
|
|
1589
|
+
ctx.${ctx.regs}[i] = 0;
|
|
1590
|
+
}
|
|
1591
|
+
} catch(e) {}
|
|
1592
|
+
ctx.${ctx.env} = [];
|
|
1593
|
+
ctx.${ctx.tryFrames} = [];
|
|
1594
|
+
ctx.${ctx.returnValue} = undefined;
|
|
1595
|
+
ctx.${ctx.globalScope} = {};
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
${runtimeStringBootstrap}
|
|
1599
|
+
|
|
1600
|
+
// Lazy Decryption
|
|
1601
|
+
function ${top.getCP}(ctx, index) {
|
|
1602
|
+
var c = ${top.rawCP}[index];
|
|
1603
|
+
if (!c) return undefined;
|
|
1604
|
+
if (c.kind === 'string' && ${config.constantPoolEncoding === ConstantEncodingScheme.XorRotate}) {
|
|
1605
|
+
var val = c.value;
|
|
1606
|
+
var decoded = '';
|
|
1607
|
+
var stringSeed = (${top.seed} ^ (index * 0x9E3779B9)) | 0;
|
|
1608
|
+
if (ctx && ctx.${ctx.pathHash} !== undefined) {
|
|
1609
|
+
stringSeed = (stringSeed ^ ctx.${ctx.pathHash}) | 0;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// Deriving 16-byte key using LCG
|
|
1613
|
+
var keyBytes = [];
|
|
1614
|
+
var s = stringSeed;
|
|
1615
|
+
for (var i = 0; i < 16; i++) {
|
|
1616
|
+
s = (Math.imul(s, ${mulConst}) + ${addConst}) | 0;
|
|
1617
|
+
keyBytes.push((s >>> 16) & 0xff);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// KSA
|
|
1621
|
+
var S = [];
|
|
1622
|
+
for (var i = 0; i < 256; i++) S.push(i);
|
|
1623
|
+
var j = 0;
|
|
1624
|
+
for (var i = 0; i < 256; i++) {
|
|
1625
|
+
j = (j + S[i] + keyBytes[i % 16]) & 0xff;
|
|
1626
|
+
var temp = S[i];
|
|
1627
|
+
S[i] = S[j];
|
|
1628
|
+
S[j] = temp;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// PRGA with drop-256
|
|
1632
|
+
var ri = 0;
|
|
1633
|
+
j = 0;
|
|
1634
|
+
for (var skip = 0; skip < 256; skip++) {
|
|
1635
|
+
ri = (ri + 1) & 0xff;
|
|
1636
|
+
j = (j + S[ri]) & 0xff;
|
|
1637
|
+
var temp = S[ri];
|
|
1638
|
+
S[ri] = S[j];
|
|
1639
|
+
S[j] = temp;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
for (var i = 0; i < val.length; i++) {
|
|
1643
|
+
ri = (ri + 1) & 0xff;
|
|
1644
|
+
j = (j + S[ri]) & 0xff;
|
|
1645
|
+
var temp = S[ri];
|
|
1646
|
+
S[ri] = S[j];
|
|
1647
|
+
S[j] = temp;
|
|
1648
|
+
var keystreamByte = S[(S[ri] + S[j]) & 0xff];
|
|
1649
|
+
decoded += String.fromCharCode(val.charCodeAt(i) ^ keystreamByte);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
return decoded;
|
|
1653
|
+
}
|
|
1654
|
+
return c.kind === 'undefined' ? undefined : c.value;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
function mixRollingState(ctx, pos, decoded) {
|
|
1658
|
+
var salt = ctx.salt || 0;
|
|
1659
|
+
var nonce = ctx.executionNonce || 0;
|
|
1660
|
+
var op = ctx.currentOpcode || 0;
|
|
1661
|
+
var mixed = (pos ^ decoded ^ op ^ salt ^ nonce) & 0xFF;
|
|
1662
|
+
// MurmurHash3-inspired non-linear mixing (replaces reversible LCG)
|
|
1663
|
+
var s = ctx.${ctx.rollingState} ^ mixed;
|
|
1664
|
+
s = Math.imul(s, 0xcc9e2d51);
|
|
1665
|
+
s = ((s << 15) | (s >>> 17));
|
|
1666
|
+
s = Math.imul(s, 0x1b873593);
|
|
1667
|
+
s ^= (s >>> 13);
|
|
1668
|
+
s = Math.imul(s, 0xc2b2ae35);
|
|
1669
|
+
s ^= (s >>> 16);
|
|
1670
|
+
ctx.${ctx.rollingState} = s;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
function corruptByteNear(ctx, at, mask) {
|
|
1674
|
+
if (at >= 0 && at < ctx.bytecode.length) {
|
|
1675
|
+
// Asymmetric masking: xorLog gets bit-reversed mask (linear over XOR, prevents cancellation)
|
|
1676
|
+
var rev = ((mask & 0xF0) >>> 4) | ((mask & 0x0F) << 4);
|
|
1677
|
+
rev = ((rev & 0xCC) >>> 2) | ((rev & 0x33) << 2);
|
|
1678
|
+
rev = ((rev & 0xAA) >>> 1) | ((rev & 0x55) << 1);
|
|
1679
|
+
ctx.bytecode[at] ^= mask;
|
|
1680
|
+
ctx.${ctx.xorLog}[at] ^= rev;
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
function makeRouteToken(ctx, nextOp) {
|
|
1685
|
+
return (nextOp ^ ctx.${ctx.rollingState}) & 0xFFFF;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
|
|
1690
|
+
function ${top.readByte}(ctx) {
|
|
1691
|
+
var pos = ${ctxRef("pc")}++;
|
|
1692
|
+
if (pos >= ${ctxRef("bytecode")}.length) return 0;
|
|
1693
|
+
var byte = ${ctxRef("bytecode")}[pos];
|
|
1694
|
+
${config.rollingKeys ? `
|
|
1695
|
+
var xorVal = ${ctxRef("xorLog")}[pos];
|
|
1696
|
+
var rev = ((xorVal & 0xF0) >>> 4) | ((xorVal & 0x0F) << 4);
|
|
1697
|
+
rev = ((rev & 0xCC) >>> 2) | ((rev & 0x33) << 2);
|
|
1698
|
+
rev = ((rev & 0xAA) >>> 1) | ((rev & 0x55) << 1);
|
|
1699
|
+
byte ^= rev;
|
|
1700
|
+
var rawDecoded = byte ^ (((${top.seed} ^ (pos * 0x9E3779B9)) >>> 8) & 0xFF);
|
|
1701
|
+
var decoded = (rawDecoded - pos) & 0xFF;
|
|
1702
|
+
if (${config.runtimeHardening === "paranoid" ? "true" : "false"}) {
|
|
1703
|
+
mixRollingState(ctx, pos, decoded);
|
|
1704
|
+
} else {
|
|
1705
|
+
// Non-linear state advancement (replaces reversible LCG)
|
|
1706
|
+
var s_nr = ctx.${ctx.rollingState} ^ pos;
|
|
1707
|
+
s_nr = Math.imul(s_nr, 0xcc9e2d51);
|
|
1708
|
+
s_nr = ((s_nr << 15) | (s_nr >>> 17));
|
|
1709
|
+
s_nr = Math.imul(s_nr, 0x1b873593);
|
|
1710
|
+
s_nr ^= (s_nr >>> 13);
|
|
1711
|
+
ctx.${ctx.rollingState} = s_nr;
|
|
1712
|
+
}
|
|
1713
|
+
var mask = ((ctx.${ctx.rollingState} >>> 16) & 0xFF) | 1;
|
|
1714
|
+
if (${config.runtimeHardening === "paranoid" ? "true" : "false"}) {
|
|
1715
|
+
corruptByteNear(ctx, pos, mask);
|
|
1716
|
+
corruptByteNear(ctx, pos - 1, (mask * 3) & 0xFF);
|
|
1717
|
+
corruptByteNear(ctx, pos + 1, (mask * 7) & 0xFF);
|
|
1718
|
+
var stride = ((ctx.${ctx.rollingState} >>> 8) & 3) + 2;
|
|
1719
|
+
corruptByteNear(ctx, pos + stride, (mask * 13) & 0xFF);
|
|
1720
|
+
} else {
|
|
1721
|
+
var revMask = ((mask & 0xF0) >>> 4) | ((mask & 0x0F) << 4);
|
|
1722
|
+
revMask = ((revMask & 0xCC) >>> 2) | ((revMask & 0x33) << 2);
|
|
1723
|
+
revMask = ((revMask & 0xAA) >>> 1) | ((revMask & 0x55) << 1);
|
|
1724
|
+
ctx.${ctx.bytecode}[pos] ^= mask;
|
|
1725
|
+
ctx.${ctx.xorLog}[pos] ^= revMask;
|
|
1726
|
+
}
|
|
1727
|
+
return decoded;
|
|
1728
|
+
` : `
|
|
1729
|
+
return byte;
|
|
1730
|
+
`}
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
${handlerDeclarations.join("\n\n")}
|
|
1734
|
+
|
|
1735
|
+
${handlerVariantsStr}
|
|
1736
|
+
|
|
1737
|
+
${runtimeDispatch.declarations}
|
|
1738
|
+
|
|
1739
|
+
const ${top.functionArena} = new Uint8Array([${arenaArray}]);
|
|
1740
|
+
const ${top.functionBytecodes} = {
|
|
1741
|
+
${fnTableStr}
|
|
1742
|
+
};
|
|
1743
|
+
const ${top.executorCache} = Object.create(null);
|
|
1744
|
+
|
|
1745
|
+
function ${top.getExecutorById}(functionId, env) {
|
|
1746
|
+
if ((!env || env.length === 0) && ${top.executorCache}[functionId]) {
|
|
1747
|
+
return ${top.executorCache}[functionId];
|
|
1748
|
+
}
|
|
1749
|
+
const functionMeta = ${top.functionBytecodes}[functionId];
|
|
1750
|
+
if (!functionMeta) {
|
|
1751
|
+
throw new Error(${runtimeStringRef("Unknown VM function id: ")} + functionId);
|
|
1752
|
+
}
|
|
1753
|
+
const bytecodeSegment = ${top.functionArena}.subarray(functionMeta.o, functionMeta.o + functionMeta.l);
|
|
1754
|
+
const executor = ${top.createExecutor}(bytecodeSegment, env || [], functionMeta.a || [], functionMeta.r || 0, functionMeta.s || 0);
|
|
1755
|
+
if (!env || env.length === 0) {
|
|
1756
|
+
${top.executorCache}[functionId] = executor;
|
|
1757
|
+
}
|
|
1758
|
+
return executor;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
function __createVmContext(bytecodeArr, envArr, thisArg, newTarget, argsArr, registerCount, salt) {
|
|
1762
|
+
var rawRegs = new Array(registerCount > 0 ? registerCount : argsArr.length + 8).fill(undefined);
|
|
1763
|
+
var regCount = rawRegs.length;
|
|
1764
|
+
var ctx;
|
|
1765
|
+
var regsProxy = new Proxy(rawRegs, {
|
|
1766
|
+
get: function(target, prop) {
|
|
1767
|
+
if (typeof prop === 'string') {
|
|
1768
|
+
var idx = +prop;
|
|
1769
|
+
if (idx === idx) {
|
|
1770
|
+
if (idx < 0 || idx >= regCount) {
|
|
1771
|
+
throw new Error('Register out of bounds (get): ' + idx + ', regCount: ' + regCount);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
return target[prop];
|
|
1776
|
+
},
|
|
1777
|
+
set: function(target, prop, val) {
|
|
1778
|
+
if (typeof prop === 'string') {
|
|
1779
|
+
var idx = +prop;
|
|
1780
|
+
if (idx === idx) {
|
|
1781
|
+
if (idx < 0 || idx >= regCount) {
|
|
1782
|
+
throw new Error('Register out of bounds (set): ' + idx + ', regCount: ' + regCount + ', val: ' + val);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
target[prop] = val;
|
|
1787
|
+
return true;
|
|
1788
|
+
}
|
|
1789
|
+
});
|
|
1790
|
+
ctx = {
|
|
1791
|
+
${ctx.pc}: 0,
|
|
1792
|
+
${ctx.bytecode}: ${config.rollingKeys ? "Uint8Array.from(bytecodeArr)" : "bytecodeArr"},
|
|
1793
|
+
${ctx.regs}: regsProxy,
|
|
1794
|
+
${ctx.fnArgs}: argsArr,
|
|
1795
|
+
${ctx.env}: envArr || [],
|
|
1796
|
+
${ctx.globalScope}: typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : global,
|
|
1797
|
+
${ctx.thisArg}: thisArg,
|
|
1798
|
+
${ctx.newTarget}: newTarget,
|
|
1799
|
+
${ctx.resumeMode}: 'normal',
|
|
1800
|
+
${ctx.resumeValue}: undefined,
|
|
1801
|
+
${ctx.resumeReg}: -1,
|
|
1802
|
+
${ctx.awaitPromise}: null,
|
|
1803
|
+
${ctx.running}: true,
|
|
1804
|
+
${ctx.returnValue}: undefined,
|
|
1805
|
+
${ctx.tryFrames}: [],
|
|
1806
|
+
${ctx.rollingState}: ${config.rollingKeys ? `${top.seed} & 0xFF` : "0"},
|
|
1807
|
+
${ctx.xorLog}: ${config.rollingKeys ? "new Uint8Array(bytecodeArr.length)" : "null"},
|
|
1808
|
+
${ctx.executionNonce}: ${config.rollingKeys ? "(++executionCounter)" : "0"},
|
|
1809
|
+
salt: ${config.rollingKeys ? "salt" : "0"},
|
|
1810
|
+
currentOpcode: 0,
|
|
1811
|
+
poisoned: false,
|
|
1812
|
+
regCount: regCount,
|
|
1813
|
+
${ctx.currentHandlerIdx}: 0,
|
|
1814
|
+
${ctx.pathHash}: 0,
|
|
1815
|
+
shred: function() {
|
|
1816
|
+
// Zero per-execution register backing array
|
|
1817
|
+
if (rawRegs) {
|
|
1818
|
+
for (var i = 0; i < rawRegs.length; i++) rawRegs[i] = 0;
|
|
1819
|
+
}
|
|
1820
|
+
${config.rollingKeys ? `
|
|
1821
|
+
if (ctx.${ctx.bytecode} && ctx.${ctx.bytecode}.fill) {
|
|
1822
|
+
try { ctx.${ctx.bytecode}.fill(0); } catch(e) {}
|
|
1823
|
+
}
|
|
1824
|
+
` : ""}
|
|
1825
|
+
ctx.${ctx.bytecode} = new Uint8Array(0);
|
|
1826
|
+
// Detach env reference without destroying shared envArr
|
|
1827
|
+
ctx.${ctx.env} = [];
|
|
1828
|
+
// Zero per-execution args (these are cloned per call via arraySlice)
|
|
1829
|
+
if (argsArr) {
|
|
1830
|
+
for (var i = 0; i < argsArr.length; i++) argsArr[i] = 0;
|
|
1831
|
+
}
|
|
1832
|
+
if (ctx.${ctx.tryFrames}) {
|
|
1833
|
+
ctx.${ctx.tryFrames} = [];
|
|
1834
|
+
}
|
|
1835
|
+
ctx.${ctx.xorLog} = null;
|
|
1836
|
+
ctx.${ctx.returnValue} = undefined;
|
|
1837
|
+
ctx.${ctx.globalScope} = {};
|
|
1838
|
+
ctx.${ctx.running} = false;
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
for (let i = 0; i < argsArr.length; i++) {
|
|
1842
|
+
${regRef("i")} = argsArr[i];
|
|
1843
|
+
}
|
|
1844
|
+
return ctx;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
function __runVm(ctx) {
|
|
1848
|
+
${antiDebugLogic}
|
|
1849
|
+
${tamperDetectionLogic}
|
|
1850
|
+
if (${ctxRef("pc")} >= ${ctxRef("bytecode")}.length) return { kind: 'return', value: ${ctxRef("returnValue")} };
|
|
1851
|
+
let ${locals.opByte} = ${top.readByte}(ctx);
|
|
1852
|
+
${config.stealthDispatch ? `
|
|
1853
|
+
${locals.opByte} = (ctx.${ctx.currentHandlerIdx} + ${locals.opByte}) % 256;
|
|
1854
|
+
ctx.${ctx.currentHandlerIdx} = ${locals.opByte};
|
|
1855
|
+
` : ""}
|
|
1856
|
+
${config.rollingKeys ? `
|
|
1857
|
+
ctx.${ctx.pathHash} = (Math.imul(ctx.${ctx.pathHash}, 31) + ${locals.opByte}) & 0xFFFFFFFF;
|
|
1858
|
+
` : ""}
|
|
1859
|
+
let handler = ${config.runtimeHardening === "paranoid" ? `resolveRoute(ctx, makeRouteToken(ctx, ${locals.opByte}))` : `${locals.dispatchBank}[${locals.opByte}]`};
|
|
1860
|
+
|
|
1861
|
+
while(handler && ${ctxRef("running")} && !ctx.poisoned) {
|
|
1862
|
+
// Timing Jitter Injection to mitigate Timing Side-Channels
|
|
1863
|
+
var jitterLimit = (ctx.${ctx.rollingState} ^ ${ctxRef("pc")}) & 0x3;
|
|
1864
|
+
var jitterVal = 0;
|
|
1865
|
+
for (var j = 0; j < jitterLimit; j++) {
|
|
1866
|
+
jitterVal = (Math.imul(jitterVal + j, 31) + ctx.${ctx.rollingState}) | 0;
|
|
1867
|
+
}
|
|
1868
|
+
if (jitterVal === 0xdeadbeef) {
|
|
1869
|
+
ctx._j = jitterVal;
|
|
1870
|
+
}
|
|
1871
|
+
if (${ctxRef("tryFrames")}.length > 0) {
|
|
1872
|
+
while (${ctxRef("tryFrames")}.length > 0 && ${ctxRef("pc")} >= ${ctxRef("tryFrames")}[${ctxRef("tryFrames")}.length - 1].${frame.endPc}) {
|
|
1873
|
+
${ctxRef("tryFrames")}.pop();
|
|
1874
|
+
}
|
|
1875
|
+
if (${ctxRef("tryFrames")}.length === 0) {
|
|
1876
|
+
continue;
|
|
1877
|
+
}
|
|
1878
|
+
try {
|
|
1879
|
+
if (${ctxRef("resumeMode")} === 'store') {
|
|
1880
|
+
ctx.${ctx.regs}[ctx.${ctx.resumeReg}] = ctx.${ctx.resumeValue};
|
|
1881
|
+
${ctxRef("resumeMode")} = 'normal';
|
|
1882
|
+
${ctxRef("resumeValue")} = undefined;
|
|
1883
|
+
${ctxRef("resumeReg")} = -1;
|
|
1884
|
+
} else if (${ctxRef("resumeMode")} === 'throw') {
|
|
1885
|
+
const pendingError = ctx.${ctx.resumeValue};
|
|
1886
|
+
${ctxRef("resumeMode")} = 'normal';
|
|
1887
|
+
${ctxRef("resumeValue")} = undefined;
|
|
1888
|
+
throw pendingError;
|
|
1889
|
+
}
|
|
1890
|
+
if (${config.runtimeHardening === "paranoid" ? "true" : "false"}) {
|
|
1891
|
+
handler = resolveRoute(ctx, handler(ctx));
|
|
1892
|
+
} else {
|
|
1893
|
+
handler = handler(ctx);
|
|
1894
|
+
}
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
const handlerFrame = ${ctxRef("tryFrames")}.pop();
|
|
1897
|
+
${regRef(frameRef("handlerFrame", "exceptionReg"))} = error;
|
|
1898
|
+
${ctxRef("running")} = true;
|
|
1899
|
+
${ctxRef("pc")} = ${frameRef("handlerFrame", "catchPc")};
|
|
1900
|
+
${config.stealthDispatch ? `ctx.${ctx.currentHandlerIdx} = 0;` : ""}
|
|
1901
|
+
${config.rollingKeys ? `ctx.${ctx.pathHash} = 0;` : ""}
|
|
1902
|
+
if (${ctxRef("pc")} >= ${ctxRef("bytecode")}.length) {
|
|
1903
|
+
handler = null;
|
|
1904
|
+
} else {
|
|
1905
|
+
let nextOp = ${top.readByte}(ctx);
|
|
1906
|
+
${config.stealthDispatch ? `
|
|
1907
|
+
nextOp = (ctx.${ctx.currentHandlerIdx} + nextOp) % 256;
|
|
1908
|
+
ctx.${ctx.currentHandlerIdx} = nextOp;
|
|
1909
|
+
` : ""}
|
|
1910
|
+
${config.rollingKeys ? `
|
|
1911
|
+
ctx.${ctx.pathHash} = (Math.imul(ctx.${ctx.pathHash}, 31) + nextOp) & 0xFFFFFFFF;
|
|
1912
|
+
` : ""}
|
|
1913
|
+
if (${config.runtimeHardening === "paranoid" ? "true" : "false"}) {
|
|
1914
|
+
handler = resolveRoute(ctx, makeRouteToken(ctx, nextOp));
|
|
1915
|
+
} else {
|
|
1916
|
+
handler = ${locals.dispatchBank}[nextOp];
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
} else {
|
|
1921
|
+
if (${ctxRef("resumeMode")} === 'store') {
|
|
1922
|
+
ctx.${ctx.regs}[ctx.${ctx.resumeReg}] = ctx.${ctx.resumeValue};
|
|
1923
|
+
${ctxRef("resumeMode")} = 'normal';
|
|
1924
|
+
${ctxRef("resumeValue")} = undefined;
|
|
1925
|
+
${ctxRef("resumeReg")} = -1;
|
|
1926
|
+
} else if (${ctxRef("resumeMode")} === 'throw') {
|
|
1927
|
+
const pendingError = ctx.${ctx.resumeValue};
|
|
1928
|
+
${ctxRef("resumeMode")} = 'normal';
|
|
1929
|
+
${ctxRef("resumeValue")} = undefined;
|
|
1930
|
+
throw pendingError;
|
|
1931
|
+
}
|
|
1932
|
+
if (${config.runtimeHardening === "paranoid" ? "true" : "false"}) {
|
|
1933
|
+
handler = resolveRoute(ctx, handler(ctx));
|
|
1934
|
+
} else {
|
|
1935
|
+
handler = handler(ctx);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
if (${ctxRef("resumeMode")} === 'await') {
|
|
1940
|
+
${ctxRef("resumeMode")} = 'normal';
|
|
1941
|
+
return { kind: 'await', promise: ${ctxRef("awaitPromise")} };
|
|
1942
|
+
}
|
|
1943
|
+
if (${ctxRef("resumeMode")} === 'yield' || ${ctxRef("resumeMode")} === 'yieldStar') {
|
|
1944
|
+
return { kind: ${ctxRef("resumeMode")}, value: ${ctxRef("resumeValue")} };
|
|
1945
|
+
}
|
|
1946
|
+
return { kind: 'return', value: ${ctxRef("returnValue")} };
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
function ${top.createExecutor}(bytecodeArr, envArr, attributes, registerCount, salt) {
|
|
1950
|
+
const isAsync = attributes && attributes.indexOf('async') >= 0;
|
|
1951
|
+
const isGenerator = attributes && attributes.indexOf('generator') >= 0;
|
|
1952
|
+
if (isAsync && isGenerator) {
|
|
1953
|
+
return function execute() {
|
|
1954
|
+
const ${ctx.fnArgs} = ${top.nativeCall}.call(${top.arraySlice}, arguments);
|
|
1955
|
+
const ctx = __createVmContext(bytecodeArr, envArr, this, new.target, ${ctx.fnArgs}, registerCount, salt);
|
|
1956
|
+
let delegateIterator = null;
|
|
1957
|
+
let delegateIsAsync = false;
|
|
1958
|
+
let finished = false;
|
|
1959
|
+
const resumeAwait = async (promise, mode) => {
|
|
1960
|
+
try {
|
|
1961
|
+
ctx.${ctx.resumeMode} = 'store';
|
|
1962
|
+
ctx.${ctx.resumeValue} = await ${top.promiseResolve}(promise);
|
|
1963
|
+
ctx.${ctx.running} = true;
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
ctx.${ctx.resumeMode} = 'throw';
|
|
1966
|
+
ctx.${ctx.resumeValue} = error;
|
|
1967
|
+
ctx.${ctx.running} = true;
|
|
1968
|
+
}
|
|
1969
|
+
return mode;
|
|
1970
|
+
};
|
|
1971
|
+
return {
|
|
1972
|
+
[${top.asyncIteratorSymbol} || ${top.iteratorSymbol}]: function() { return this; },
|
|
1973
|
+
next: async function(v) {
|
|
1974
|
+
if (finished) return { value: undefined, done: true };
|
|
1975
|
+
try {
|
|
1976
|
+
while (true) {
|
|
1977
|
+
if (delegateIterator) {
|
|
1978
|
+
const step = delegateIsAsync ? await delegateIterator.next(v) : delegateIterator.next(v);
|
|
1979
|
+
if (step.done) {
|
|
1980
|
+
v = step.value;
|
|
1981
|
+
delegateIterator = null;
|
|
1982
|
+
delegateIsAsync = false;
|
|
1983
|
+
} else {
|
|
1984
|
+
return step;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
if (ctx.${ctx.resumeMode} === 'yield' || ctx.${ctx.resumeMode} === 'yieldStar') {
|
|
1988
|
+
ctx.${ctx.resumeMode} = 'normal';
|
|
1989
|
+
ctx.${ctx.regs}[ctx.${ctx.resumeReg}] = v;
|
|
1990
|
+
ctx.${ctx.resumeReg} = -1;
|
|
1991
|
+
ctx.${ctx.running} = true;
|
|
1992
|
+
}
|
|
1993
|
+
const outcome = __runVm(ctx);
|
|
1994
|
+
if (outcome.kind === 'await') {
|
|
1995
|
+
await resumeAwait(outcome.promise, 'await');
|
|
1996
|
+
v = undefined;
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
if (outcome.kind === 'return') {
|
|
2000
|
+
finished = true;
|
|
2001
|
+
ctx.shred();
|
|
2002
|
+
return { value: outcome.value, done: true };
|
|
2003
|
+
}
|
|
2004
|
+
if (outcome.kind === 'yield') {
|
|
2005
|
+
return { value: outcome.value, done: false };
|
|
2006
|
+
}
|
|
2007
|
+
if (outcome.kind === 'yieldStar') {
|
|
2008
|
+
if (${top.asyncIteratorSymbol} && outcome.value[${top.asyncIteratorSymbol}]) {
|
|
2009
|
+
delegateIterator = outcome.value[${top.asyncIteratorSymbol}]();
|
|
2010
|
+
delegateIsAsync = true;
|
|
2011
|
+
} else {
|
|
2012
|
+
delegateIterator = outcome.value[${top.iteratorSymbol}]();
|
|
2013
|
+
delegateIsAsync = false;
|
|
2014
|
+
}
|
|
2015
|
+
v = undefined;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
} catch(e) {
|
|
2019
|
+
finished = true;
|
|
2020
|
+
ctx.shred();
|
|
2021
|
+
throw e;
|
|
2022
|
+
}
|
|
2023
|
+
},
|
|
2024
|
+
return: async function(v) {
|
|
2025
|
+
finished = true;
|
|
2026
|
+
ctx.${ctx.running} = false;
|
|
2027
|
+
ctx.shred();
|
|
2028
|
+
return { value: v, done: true };
|
|
2029
|
+
},
|
|
2030
|
+
throw: async function(e) {
|
|
2031
|
+
ctx.${ctx.resumeMode} = 'throw';
|
|
2032
|
+
ctx.${ctx.resumeValue} = e;
|
|
2033
|
+
ctx.${ctx.running} = true;
|
|
2034
|
+
try {
|
|
2035
|
+
return await this.next();
|
|
2036
|
+
} catch(err) {
|
|
2037
|
+
ctx.shred();
|
|
2038
|
+
throw err;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
if (isAsync) {
|
|
2045
|
+
return async function execute() {
|
|
2046
|
+
const ${ctx.fnArgs} = ${top.nativeCall}.call(${top.arraySlice}, arguments);
|
|
2047
|
+
const ctx = __createVmContext(bytecodeArr, envArr, this, new.target, ${ctx.fnArgs}, registerCount, salt);
|
|
2048
|
+
try {
|
|
2049
|
+
while (true) {
|
|
2050
|
+
const outcome = __runVm(ctx);
|
|
2051
|
+
if (outcome.kind === 'return') {
|
|
2052
|
+
return outcome.value;
|
|
2053
|
+
}
|
|
2054
|
+
try {
|
|
2055
|
+
ctx.${ctx.resumeMode} = 'store';
|
|
2056
|
+
ctx.${ctx.resumeValue} = await ${top.promiseResolve}(outcome.promise);
|
|
2057
|
+
ctx.${ctx.running} = true;
|
|
2058
|
+
} catch (error) {
|
|
2059
|
+
ctx.${ctx.resumeMode} = 'throw';
|
|
2060
|
+
ctx.${ctx.resumeValue} = error;
|
|
2061
|
+
ctx.${ctx.running} = true;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
} finally {
|
|
2065
|
+
ctx.shred();
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
if (isGenerator) {
|
|
2070
|
+
return function execute() {
|
|
2071
|
+
const ${ctx.fnArgs} = ${top.nativeCall}.call(${top.arraySlice}, arguments);
|
|
2072
|
+
const ctx = __createVmContext(bytecodeArr, envArr, this, new.target, ${ctx.fnArgs}, registerCount, salt);
|
|
2073
|
+
let delegateIterator = null;
|
|
2074
|
+
let finished = false;
|
|
2075
|
+
return {
|
|
2076
|
+
[${top.iteratorSymbol}]: function() { return this; },
|
|
2077
|
+
next: function(v) {
|
|
2078
|
+
if (finished) return { value: undefined, done: true };
|
|
2079
|
+
try {
|
|
2080
|
+
while(true) {
|
|
2081
|
+
if (delegateIterator) {
|
|
2082
|
+
let step = delegateIterator.next(v);
|
|
2083
|
+
if (step.done) {
|
|
2084
|
+
v = step.value;
|
|
2085
|
+
delegateIterator = null;
|
|
2086
|
+
} else {
|
|
2087
|
+
return step;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
if (ctx.${ctx.resumeMode} === 'yield' || ctx.${ctx.resumeMode} === 'yieldStar') {
|
|
2091
|
+
ctx.${ctx.resumeMode} = 'normal';
|
|
2092
|
+
ctx.${ctx.regs}[ctx.${ctx.resumeReg}] = v;
|
|
2093
|
+
ctx.${ctx.resumeReg} = -1;
|
|
2094
|
+
ctx.${ctx.running} = true;
|
|
2095
|
+
}
|
|
2096
|
+
const outcome = __runVm(ctx);
|
|
2097
|
+
if (outcome.kind === 'await') {
|
|
2098
|
+
throw new Error('VM await suspension reached sync generator');
|
|
2099
|
+
}
|
|
2100
|
+
if (outcome.kind === 'return') {
|
|
2101
|
+
finished = true;
|
|
2102
|
+
ctx.shred();
|
|
2103
|
+
return { value: outcome.value, done: true };
|
|
2104
|
+
}
|
|
2105
|
+
if (outcome.kind === 'yield') {
|
|
2106
|
+
return { value: outcome.value, done: false };
|
|
2107
|
+
}
|
|
2108
|
+
if (outcome.kind === 'yieldStar') {
|
|
2109
|
+
delegateIterator = outcome.value[${top.iteratorSymbol}]();
|
|
2110
|
+
v = undefined;
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
} catch(e) {
|
|
2115
|
+
finished = true;
|
|
2116
|
+
ctx.shred();
|
|
2117
|
+
throw e;
|
|
2118
|
+
}
|
|
2119
|
+
},
|
|
2120
|
+
return: function(v) {
|
|
2121
|
+
finished = true;
|
|
2122
|
+
ctx.${ctx.running} = false;
|
|
2123
|
+
ctx.shred();
|
|
2124
|
+
return { value: v, done: true };
|
|
2125
|
+
},
|
|
2126
|
+
throw: function(e) {
|
|
2127
|
+
ctx.${ctx.resumeMode} = 'throw';
|
|
2128
|
+
ctx.${ctx.resumeValue} = e;
|
|
2129
|
+
ctx.${ctx.running} = true;
|
|
2130
|
+
try {
|
|
2131
|
+
return this.next();
|
|
2132
|
+
} catch(err) {
|
|
2133
|
+
ctx.shred();
|
|
2134
|
+
throw err;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
return function execute() {
|
|
2141
|
+
const ${ctx.fnArgs} = ${top.nativeCall}.call(${top.arraySlice}, arguments);
|
|
2142
|
+
const ctx = __createVmContext(bytecodeArr, envArr, this, new.target, ${ctx.fnArgs}, registerCount, salt);
|
|
2143
|
+
try {
|
|
2144
|
+
const outcome = __runVm(ctx);
|
|
2145
|
+
if (outcome.kind === 'await') {
|
|
2146
|
+
throw new Error('VM await suspension reached sync executor');
|
|
2147
|
+
}
|
|
2148
|
+
var retVal = outcome.value;
|
|
2149
|
+
if (new.target) {
|
|
2150
|
+
if (retVal === undefined || (typeof retVal !== 'object' && typeof retVal !== 'function')) {
|
|
2151
|
+
return ctx.${ctx.thisArg};
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
return retVal;
|
|
2155
|
+
} finally {
|
|
2156
|
+
ctx.shred();
|
|
2157
|
+
}
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
var ${top.result} = {};
|
|
2162
|
+
${exportedFunctions.map((fn) => ` ${top.result}[${JSON.stringify(fn.name)}] = ${top.getExecutorById}(${JSON.stringify(fn.id)});`).join("\n")}
|
|
2163
|
+
if (typeof Object.freeze === 'function') {
|
|
2164
|
+
Object.freeze(${top.result});
|
|
2165
|
+
}
|
|
2166
|
+
return ${top.result};
|
|
2167
|
+
})();
|
|
2168
|
+
|
|
2169
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
2170
|
+
${exportedFunctions.map((fn) => ` module.exports[${JSON.stringify(fn.name)}] = ${top.vmFunctions}[${JSON.stringify(fn.name)}];`).join("\n")}
|
|
2171
|
+
}
|
|
2172
|
+
`.trim();
|
|
2173
|
+
return {
|
|
2174
|
+
buildId: module.buildId,
|
|
2175
|
+
dispatchLoop: "function dispatch() {}",
|
|
2176
|
+
handlers: [],
|
|
2177
|
+
constantDecoder: "function decode() {}",
|
|
2178
|
+
bytecodePayload: targetFn.bytecode,
|
|
2179
|
+
entryBootstrap: "function boot() {}",
|
|
2180
|
+
fullSource: sourceCode
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
export {
|
|
2184
|
+
buildVMRuntime
|
|
2185
|
+
};
|