luau-obfuscator 1.0.1 → 1.0.3
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.cjs +272 -133
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +272 -133
- package/generated/grammers.luau +11812 -0
- package/generated/tests.luau +4330 -0
- package/package.json +1 -1
- package/scripts/test.js +18 -12
- package/src/passes/ConstantArray.ts +6 -5
- package/src/passes/EncryptNumbers.ts +2 -1
- package/src/passes/EncryptStrings.ts +11 -11
- package/src/passes/NumbersToExpressions.ts +4 -1
- package/src/passes/StringsToExpressions.ts +35 -11
- package/src/passes/Vmify.ts +30 -11
- package/src/passes/nodeFactory.ts +19 -0
- package/src/passes/vmify/chunk.ts +9 -5
- package/src/passes/vmify/compiler.ts +18 -0
- package/src/passes/vmify/names.ts +2 -8
- package/src/passes/vmify/runtime.ts +49 -49
- package/src/passes/vmify/serialize.ts +11 -11
- package/src/passes/walk.ts +141 -43
- package/src/pipeline.ts +3 -6
- package/generated/final.luau +0 -63216
- package/generated/smoketest.luau +0 -24339
- /package/{scripts/example.luau → smoketest/grammers.luau} +0 -0
- /package/{scripts/smoketest.luau → smoketest/tests.luau} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -209,67 +209,171 @@ function visitArray(arr, visitor) {
|
|
|
209
209
|
arr[i] = mapExpression(arr[i], visitor);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
-
function
|
|
212
|
+
function getExpressionChildren(expr) {
|
|
213
213
|
switch (expr.type) {
|
|
214
|
-
case "InterpolatedStringExpression":
|
|
214
|
+
case "InterpolatedStringExpression": {
|
|
215
|
+
const children = [];
|
|
215
216
|
for (const part of expr.parts) {
|
|
216
217
|
if (part.kind === "expression") {
|
|
217
|
-
|
|
218
|
+
children.push({
|
|
219
|
+
get: () => part.expression,
|
|
220
|
+
set: (v) => {
|
|
221
|
+
part.expression = v;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
218
224
|
}
|
|
219
225
|
}
|
|
220
|
-
|
|
226
|
+
return children;
|
|
227
|
+
}
|
|
221
228
|
case "FunctionExpression":
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
229
|
+
return [];
|
|
230
|
+
case "TableExpression": {
|
|
231
|
+
const children = [];
|
|
225
232
|
for (const field of expr.fields) {
|
|
226
233
|
if (field.type === "TableFieldPositional") {
|
|
227
|
-
|
|
234
|
+
children.push({ get: () => field.value, set: (v) => {
|
|
235
|
+
field.value = v;
|
|
236
|
+
} });
|
|
228
237
|
} else if (field.type === "TableFieldNamed") {
|
|
229
|
-
|
|
238
|
+
children.push({ get: () => field.value, set: (v) => {
|
|
239
|
+
field.value = v;
|
|
240
|
+
} });
|
|
230
241
|
} else if (field.type === "TableFieldComputed") {
|
|
231
|
-
|
|
232
|
-
|
|
242
|
+
children.push({ get: () => field.key, set: (v) => {
|
|
243
|
+
field.key = v;
|
|
244
|
+
} });
|
|
245
|
+
children.push({ get: () => field.value, set: (v) => {
|
|
246
|
+
field.value = v;
|
|
247
|
+
} });
|
|
233
248
|
}
|
|
234
249
|
}
|
|
235
|
-
|
|
250
|
+
return children;
|
|
251
|
+
}
|
|
236
252
|
case "BinaryExpression":
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
return [
|
|
254
|
+
{ get: () => expr.left, set: (v) => {
|
|
255
|
+
expr.left = v;
|
|
256
|
+
} },
|
|
257
|
+
{ get: () => expr.right, set: (v) => {
|
|
258
|
+
expr.right = v;
|
|
259
|
+
} }
|
|
260
|
+
];
|
|
240
261
|
case "UnaryExpression":
|
|
241
|
-
|
|
242
|
-
|
|
262
|
+
return [{ get: () => expr.argument, set: (v) => {
|
|
263
|
+
expr.argument = v;
|
|
264
|
+
} }];
|
|
243
265
|
case "MemberExpression":
|
|
244
|
-
|
|
245
|
-
|
|
266
|
+
return [{ get: () => expr.object, set: (v) => {
|
|
267
|
+
expr.object = v;
|
|
268
|
+
} }];
|
|
246
269
|
case "IndexExpression":
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
270
|
+
return [
|
|
271
|
+
{ get: () => expr.object, set: (v) => {
|
|
272
|
+
expr.object = v;
|
|
273
|
+
} },
|
|
274
|
+
{ get: () => expr.index, set: (v) => {
|
|
275
|
+
expr.index = v;
|
|
276
|
+
} }
|
|
277
|
+
];
|
|
278
|
+
case "CallExpression": {
|
|
279
|
+
const children = [
|
|
280
|
+
{ get: () => expr.callee, set: (v) => {
|
|
281
|
+
expr.callee = v;
|
|
282
|
+
} }
|
|
283
|
+
];
|
|
284
|
+
for (let i = 0; i < expr.arguments.length; i++) {
|
|
285
|
+
const idx = i;
|
|
286
|
+
children.push({
|
|
287
|
+
get: () => expr.arguments[idx],
|
|
288
|
+
set: (v) => {
|
|
289
|
+
expr.arguments[idx] = v;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return children;
|
|
294
|
+
}
|
|
295
|
+
case "MethodCallExpression": {
|
|
296
|
+
const children = [
|
|
297
|
+
{ get: () => expr.object, set: (v) => {
|
|
298
|
+
expr.object = v;
|
|
299
|
+
} }
|
|
300
|
+
];
|
|
301
|
+
for (let i = 0; i < expr.arguments.length; i++) {
|
|
302
|
+
const idx = i;
|
|
303
|
+
children.push({
|
|
304
|
+
get: () => expr.arguments[idx],
|
|
305
|
+
set: (v) => {
|
|
306
|
+
expr.arguments[idx] = v;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return children;
|
|
311
|
+
}
|
|
258
312
|
case "ParenthesizedExpression":
|
|
259
|
-
|
|
260
|
-
|
|
313
|
+
return [{ get: () => expr.expression, set: (v) => {
|
|
314
|
+
expr.expression = v;
|
|
315
|
+
} }];
|
|
261
316
|
case "TypeAssertionExpression":
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
317
|
+
return [{ get: () => expr.expression, set: (v) => {
|
|
318
|
+
expr.expression = v;
|
|
319
|
+
} }];
|
|
320
|
+
case "IfElseExpression": {
|
|
321
|
+
const children = [];
|
|
265
322
|
for (const clause of expr.clauses) {
|
|
266
|
-
|
|
267
|
-
|
|
323
|
+
children.push({ get: () => clause.condition, set: (v) => {
|
|
324
|
+
clause.condition = v;
|
|
325
|
+
} });
|
|
326
|
+
children.push({ get: () => clause.body, set: (v) => {
|
|
327
|
+
clause.body = v;
|
|
328
|
+
} });
|
|
268
329
|
}
|
|
269
|
-
|
|
270
|
-
|
|
330
|
+
children.push({ get: () => expr.alternate, set: (v) => {
|
|
331
|
+
expr.alternate = v;
|
|
332
|
+
} });
|
|
333
|
+
return children;
|
|
334
|
+
}
|
|
335
|
+
default:
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function mapExpression(root, visitor) {
|
|
340
|
+
if (root.type === "FunctionExpression") {
|
|
341
|
+
walkFunctionBody(root.func, visitor);
|
|
342
|
+
return visitor(root) ?? root;
|
|
343
|
+
}
|
|
344
|
+
let result = root;
|
|
345
|
+
const stack = [{
|
|
346
|
+
expr: root,
|
|
347
|
+
children: getExpressionChildren(root),
|
|
348
|
+
index: 0,
|
|
349
|
+
setInParent: (v) => {
|
|
350
|
+
result = v;
|
|
351
|
+
}
|
|
352
|
+
}];
|
|
353
|
+
while (stack.length > 0) {
|
|
354
|
+
const frame = stack[stack.length - 1];
|
|
355
|
+
if (frame.index < frame.children.length) {
|
|
356
|
+
const child = frame.children[frame.index];
|
|
357
|
+
frame.index++;
|
|
358
|
+
const childExpr = child.get();
|
|
359
|
+
if (childExpr.type === "FunctionExpression") {
|
|
360
|
+
walkFunctionBody(childExpr.func, visitor);
|
|
361
|
+
child.set(visitor(childExpr) ?? childExpr);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
stack.push({
|
|
365
|
+
expr: childExpr,
|
|
366
|
+
children: getExpressionChildren(childExpr),
|
|
367
|
+
index: 0,
|
|
368
|
+
setInParent: child.set
|
|
369
|
+
});
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const finished = visitor(frame.expr) ?? frame.expr;
|
|
373
|
+
frame.setInParent?.(finished);
|
|
374
|
+
stack.pop();
|
|
271
375
|
}
|
|
272
|
-
return
|
|
376
|
+
return result;
|
|
273
377
|
}
|
|
274
378
|
function walkFunctionBody(func, visitor) {
|
|
275
379
|
walkBlock(func.body, visitor);
|
|
@@ -355,6 +459,12 @@ function stringLiteral(value) {
|
|
|
355
459
|
function numberLiteral(value) {
|
|
356
460
|
return { type: "NumberLiteral", value, raw: String(value), line: POS, column: POS };
|
|
357
461
|
}
|
|
462
|
+
var vmStructuralNumbers = /* @__PURE__ */ new WeakSet();
|
|
463
|
+
function vmNumberLiteral(value) {
|
|
464
|
+
const node = numberLiteral(value);
|
|
465
|
+
vmStructuralNumbers.add(node);
|
|
466
|
+
return node;
|
|
467
|
+
}
|
|
358
468
|
function binary(operator, left, right) {
|
|
359
469
|
return { type: "BinaryExpression", operator, left, right, line: POS, column: POS };
|
|
360
470
|
}
|
|
@@ -484,14 +594,15 @@ function splitByteChunks(bytes, min, max) {
|
|
|
484
594
|
return chunks.length > 0 ? chunks : [bytes];
|
|
485
595
|
}
|
|
486
596
|
function stringCharCall(bytes) {
|
|
487
|
-
return call(member(identifier("string"), "char"), bytes.map(
|
|
597
|
+
return call(member(identifier("string"), "char"), bytes.map(vmNumberLiteral));
|
|
488
598
|
}
|
|
489
599
|
function buildConcatChain(chunks) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
600
|
+
function build(lo, hi) {
|
|
601
|
+
if (hi - lo === 1) return stringCharCall(chunks[lo]);
|
|
602
|
+
const mid = lo + Math.floor((hi - lo) / 2);
|
|
603
|
+
return binary("..", build(lo, mid), build(mid, hi));
|
|
493
604
|
}
|
|
494
|
-
return
|
|
605
|
+
return build(0, chunks.length);
|
|
495
606
|
}
|
|
496
607
|
function shuffledIndices(n) {
|
|
497
608
|
const arr = Array.from({ length: n }, (_, i) => i);
|
|
@@ -514,7 +625,7 @@ function buildShuffledTables(chunks) {
|
|
|
514
625
|
for (let slot = 0; slot < n; slot++) {
|
|
515
626
|
order[storageOrder[slot]] = slot + 1;
|
|
516
627
|
}
|
|
517
|
-
const orderFields = order.map((slot) => positionalField(
|
|
628
|
+
const orderFields = order.map((slot) => positionalField(vmNumberLiteral(slot)));
|
|
518
629
|
return {
|
|
519
630
|
storeVar,
|
|
520
631
|
orderVar,
|
|
@@ -533,7 +644,7 @@ function buildForLoopStringExpr(chunks) {
|
|
|
533
644
|
localStatement(accVar, stringLiteral("")),
|
|
534
645
|
numericForStatement(
|
|
535
646
|
iVar,
|
|
536
|
-
|
|
647
|
+
vmNumberLiteral(1),
|
|
537
648
|
unary("#", identifier(orderVar)),
|
|
538
649
|
block([
|
|
539
650
|
assignmentStatement(
|
|
@@ -565,7 +676,7 @@ function buildRecursiveStringExpr(chunks) {
|
|
|
565
676
|
]),
|
|
566
677
|
returnStatement([
|
|
567
678
|
call(identifier(recName), [
|
|
568
|
-
binary("+", identifier(iParam),
|
|
679
|
+
binary("+", identifier(iParam), vmNumberLiteral(1)),
|
|
569
680
|
binary(
|
|
570
681
|
"..",
|
|
571
682
|
identifier(accParam),
|
|
@@ -577,18 +688,21 @@ function buildRecursiveStringExpr(chunks) {
|
|
|
577
688
|
return iife([
|
|
578
689
|
...stmts,
|
|
579
690
|
localFunctionStatement(recName, functionBody([functionParam(iParam), functionParam(accParam)], body)),
|
|
580
|
-
returnStatement([call(identifier(recName), [
|
|
691
|
+
returnStatement([call(identifier(recName), [vmNumberLiteral(1), stringLiteral("")])])
|
|
581
692
|
]);
|
|
582
693
|
}
|
|
583
694
|
function pickControlFlowStrategy() {
|
|
584
695
|
return Math.random() < 0.5 ? "forLoop" : "recursive";
|
|
585
696
|
}
|
|
697
|
+
var MAX_CHUNKS_PER_STRING = 2e4;
|
|
586
698
|
function runStringsToExpressions(program, options) {
|
|
587
699
|
const controlFlowProbability = options.controlFlowProbability ?? CONTROL_FLOW_PROBABILITY_DEFAULT;
|
|
588
700
|
transformExpressions(program, (expr) => {
|
|
589
701
|
if (expr.type !== "StringLiteral") return;
|
|
590
702
|
if (expr.value.length === 0) return;
|
|
591
703
|
const bytes = toUtf8Bytes(expr.value);
|
|
704
|
+
const estimatedChunks = Math.ceil(bytes.length / Math.max(1, options.min));
|
|
705
|
+
if (estimatedChunks > MAX_CHUNKS_PER_STRING) return;
|
|
592
706
|
const chunks = splitByteChunks(bytes, options.min, options.max);
|
|
593
707
|
if (chunks.length > 1 && Math.random() < controlFlowProbability) {
|
|
594
708
|
return pickControlFlowStrategy() === "forLoop" ? buildForLoopStringExpr(chunks) : buildRecursiveStringExpr(chunks);
|
|
@@ -761,6 +875,7 @@ function runNumbersToExpressions(program, options) {
|
|
|
761
875
|
const controlFlowProbability = options.controlFlowProbability ?? CONTROL_FLOW_PROBABILITY_DEFAULT2;
|
|
762
876
|
transformExpressions(program, (expr) => {
|
|
763
877
|
if (expr.type !== "NumberLiteral") return;
|
|
878
|
+
if (vmStructuralNumbers.has(expr)) return;
|
|
764
879
|
if (Number.isInteger(expr.value) && Math.random() < controlFlowProbability) {
|
|
765
880
|
return buildControlFlowNumberExpr(expr.value, options.min, options.max);
|
|
766
881
|
}
|
|
@@ -953,7 +1068,7 @@ function runConstantArray(program, options) {
|
|
|
953
1068
|
seen.add(k);
|
|
954
1069
|
order.push({ kind: "string", value: expr.value });
|
|
955
1070
|
}
|
|
956
|
-
} else if (expr.type === "NumberLiteral") {
|
|
1071
|
+
} else if (expr.type === "NumberLiteral" && !vmStructuralNumbers.has(expr)) {
|
|
957
1072
|
const k = keyOf("number", expr.value);
|
|
958
1073
|
if (!seen.has(k)) {
|
|
959
1074
|
seen.add(k);
|
|
@@ -973,11 +1088,11 @@ function runConstantArray(program, options) {
|
|
|
973
1088
|
transformExpressions(program, (expr) => {
|
|
974
1089
|
if (expr.type === "StringLiteral") {
|
|
975
1090
|
const idx = indexOf.get(keyOf("string", expr.value));
|
|
976
|
-
return index(identifier(arrayName),
|
|
1091
|
+
return index(identifier(arrayName), vmNumberLiteral(idx));
|
|
977
1092
|
}
|
|
978
|
-
if (expr.type === "NumberLiteral") {
|
|
1093
|
+
if (expr.type === "NumberLiteral" && !vmStructuralNumbers.has(expr)) {
|
|
979
1094
|
const idx = indexOf.get(keyOf("number", expr.value));
|
|
980
|
-
return index(identifier(arrayName),
|
|
1095
|
+
return index(identifier(arrayName), vmNumberLiteral(idx));
|
|
981
1096
|
}
|
|
982
1097
|
return void 0;
|
|
983
1098
|
});
|
|
@@ -1018,15 +1133,15 @@ function obfuscatedNumber(value) {
|
|
|
1018
1133
|
const variant = randomInt4(0, 2);
|
|
1019
1134
|
if (variant === 0) {
|
|
1020
1135
|
const a2 = randomInt4(0, value);
|
|
1021
|
-
return binary("+",
|
|
1136
|
+
return binary("+", vmNumberLiteral(a2), vmNumberLiteral(value - a2));
|
|
1022
1137
|
}
|
|
1023
1138
|
if (variant === 1) {
|
|
1024
1139
|
const a2 = randomInt4(0, 16777215);
|
|
1025
|
-
return binary("-",
|
|
1140
|
+
return binary("-", vmNumberLiteral(value + a2), vmNumberLiteral(a2));
|
|
1026
1141
|
}
|
|
1027
1142
|
const a = randomInt4(0, 16777215);
|
|
1028
1143
|
const b = (value ^ a) >>> 0;
|
|
1029
|
-
return call(member(identifier("bit32"), "bxor"), [
|
|
1144
|
+
return call(member(identifier("bit32"), "bxor"), [vmNumberLiteral(a), vmNumberLiteral(b)]);
|
|
1030
1145
|
}
|
|
1031
1146
|
function buildDecoderStatement(name) {
|
|
1032
1147
|
const dataParam = randomName3();
|
|
@@ -1037,17 +1152,17 @@ function buildDecoderStatement(name) {
|
|
|
1037
1152
|
const nKeysVar = randomName3();
|
|
1038
1153
|
const idxExpr = binary(
|
|
1039
1154
|
"+",
|
|
1040
|
-
binary("%", binary("-", identifier(iVar),
|
|
1041
|
-
|
|
1155
|
+
binary("%", binary("-", identifier(iVar), vmNumberLiteral(1)), identifier(nKeysVar)),
|
|
1156
|
+
vmNumberLiteral(1)
|
|
1042
1157
|
);
|
|
1043
1158
|
const keyByte = index(identifier(keysParam), idxExpr);
|
|
1044
1159
|
const mixed = call(member(identifier("bit32"), "band"), [
|
|
1045
|
-
call(member(identifier("bit32"), "lrotate"), [keyByte, binary("%", identifier(iVar),
|
|
1046
|
-
|
|
1160
|
+
call(member(identifier("bit32"), "lrotate"), [keyByte, binary("%", identifier(iVar), vmNumberLiteral(8))]),
|
|
1161
|
+
vmNumberLiteral(255)
|
|
1047
1162
|
]);
|
|
1048
1163
|
const posByte = call(member(identifier("bit32"), "band"), [
|
|
1049
1164
|
binary("*", identifier(iVar), identifier(multParam)),
|
|
1050
|
-
|
|
1165
|
+
vmNumberLiteral(255)
|
|
1051
1166
|
]);
|
|
1052
1167
|
const keystream = call(member(identifier("bit32"), "bxor"), [mixed, posByte]);
|
|
1053
1168
|
const finalByte = call(member(identifier("bit32"), "bxor"), [
|
|
@@ -1059,7 +1174,7 @@ function buildDecoderStatement(name) {
|
|
|
1059
1174
|
localStatement(outVar, table([])),
|
|
1060
1175
|
numericForStatement(
|
|
1061
1176
|
iVar,
|
|
1062
|
-
|
|
1177
|
+
vmNumberLiteral(1),
|
|
1063
1178
|
unary("#", identifier(dataParam)),
|
|
1064
1179
|
block([
|
|
1065
1180
|
assignmentStatement(
|
|
@@ -1087,7 +1202,7 @@ function runEncryptStrings(program, _options) {
|
|
|
1087
1202
|
const mult = randomInt4(1, 255);
|
|
1088
1203
|
const plainBytes = toUtf8Bytes2(expr.value);
|
|
1089
1204
|
const cipherBytes = plainBytes.map((b, idx) => b ^ keystreamByte(keys, mult, idx + 1));
|
|
1090
|
-
const dataTable = table(cipherBytes.map((b) => positionalField(
|
|
1205
|
+
const dataTable = table(cipherBytes.map((b) => positionalField(vmNumberLiteral(b))));
|
|
1091
1206
|
const keysTable = table(keys.map((k) => positionalField(obfuscatedNumber(k))));
|
|
1092
1207
|
const multExpr = obfuscatedNumber(mult);
|
|
1093
1208
|
return call(identifier(decoderName), [dataTable, keysTable, multExpr]);
|
|
@@ -1154,6 +1269,7 @@ function runEncryptNumbers(program, _options) {
|
|
|
1154
1269
|
let used = false;
|
|
1155
1270
|
transformExpressions(program, (expr) => {
|
|
1156
1271
|
if (expr.type !== "NumberLiteral") return;
|
|
1272
|
+
if (vmStructuralNumbers.has(expr)) return;
|
|
1157
1273
|
if (!isEncryptable(expr.value)) return;
|
|
1158
1274
|
used = true;
|
|
1159
1275
|
const keyA = randomInt5(1, 16777215);
|
|
@@ -1404,16 +1520,18 @@ function createProto(id) {
|
|
|
1404
1520
|
maxRegs: 0,
|
|
1405
1521
|
code: [],
|
|
1406
1522
|
consts: [],
|
|
1523
|
+
constIndex: /* @__PURE__ */ new Map(),
|
|
1407
1524
|
upvalDescs: [],
|
|
1408
1525
|
protos: []
|
|
1409
1526
|
};
|
|
1410
1527
|
}
|
|
1411
1528
|
function internConst(proto, value) {
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
}
|
|
1529
|
+
const existing = proto.constIndex.get(value);
|
|
1530
|
+
if (existing !== void 0) return existing;
|
|
1415
1531
|
proto.consts.push(value);
|
|
1416
|
-
|
|
1532
|
+
const idx = proto.consts.length - 1;
|
|
1533
|
+
proto.constIndex.set(value, idx);
|
|
1534
|
+
return idx;
|
|
1417
1535
|
}
|
|
1418
1536
|
|
|
1419
1537
|
// src/passes/vmify/registers.ts
|
|
@@ -1665,6 +1783,23 @@ var VmCompiler = class {
|
|
|
1665
1783
|
captured = /* @__PURE__ */ new Set();
|
|
1666
1784
|
protoIdCounter = 0;
|
|
1667
1785
|
declToBinding = /* @__PURE__ */ new Map();
|
|
1786
|
+
/**
|
|
1787
|
+
* 실제로 프로그램이 참조하는 모든 전역 이름(사전 등록된 builtinGlobals +
|
|
1788
|
+
* 스코프 분석이 자동으로 찾아낸, 로컬/파라미터/업밸류로 안 풀리는 나머지 식별자
|
|
1789
|
+
* 전부)을 돌려준다. Vmify.ts가 이 목록으로 브릿지 테이블(globals)을 만든다.
|
|
1790
|
+
*
|
|
1791
|
+
* 예전엔 호출자가 넘긴 builtinGlobals 목록만 브릿지에 넣었는데, 그 목록에
|
|
1792
|
+
* 없는 전역(예: Roblox API나 실행기 전용 전역을 다 못 채운 경우)을 참조하면
|
|
1793
|
+
* 컴파일은 성공하지만 GETGLOBAL이 조용히 nil을 반환해서 "attempt to call a
|
|
1794
|
+
* nil value"로 터진다 — 게다가 원인이 VM 디스패치 쪽 스택에서만 보여서
|
|
1795
|
+
* "opcode 매핑이 깨졌나?" 하고 엉뚱한 곳을 의심하게 만든다. analyzeScopes는
|
|
1796
|
+
* 어차피 로컬/업밸류로 못 푸는 식별자를 전부 global 바인딩으로 잡아두므로,
|
|
1797
|
+
* builtinGlobals에 없던 이름도 여기 globalsByName에는 다 들어있다 —
|
|
1798
|
+
* 하드코딩된 허용목록 대신 이걸 그대로 쓰면 커버리지 문제 자체가 사라진다.
|
|
1799
|
+
*/
|
|
1800
|
+
getUsedGlobalNames() {
|
|
1801
|
+
return Array.from(this.analysis.globalsByName.keys());
|
|
1802
|
+
}
|
|
1668
1803
|
computeCaptured() {
|
|
1669
1804
|
for (const binding of this.analysis.bindings.values()) {
|
|
1670
1805
|
if (binding.kind === "global") continue;
|
|
@@ -2585,13 +2720,13 @@ function serializeConstValue(v) {
|
|
|
2585
2720
|
if (typeof v === "number") return numberLiteral(v);
|
|
2586
2721
|
return booleanLiteral(v);
|
|
2587
2722
|
}
|
|
2588
|
-
function serializeInstrs(proto
|
|
2723
|
+
function serializeInstrs(proto) {
|
|
2589
2724
|
const fields = proto.code.map((instr) => positionalField(
|
|
2590
2725
|
table([
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2726
|
+
positionalField(vmNumberLiteral(instr.op)),
|
|
2727
|
+
positionalField(vmNumberLiteral(instr.a)),
|
|
2728
|
+
positionalField(vmNumberLiteral(instr.b)),
|
|
2729
|
+
positionalField(vmNumberLiteral(instr.c))
|
|
2595
2730
|
])
|
|
2596
2731
|
));
|
|
2597
2732
|
return table(fields);
|
|
@@ -2600,17 +2735,17 @@ function serializeUpvalDescs(proto, names) {
|
|
|
2600
2735
|
const fields = proto.upvalDescs.map((d) => positionalField(
|
|
2601
2736
|
table([
|
|
2602
2737
|
namedField(names.kind, stringLiteral(d.kind)),
|
|
2603
|
-
namedField(names.index,
|
|
2738
|
+
namedField(names.index, vmNumberLiteral(d.index))
|
|
2604
2739
|
])
|
|
2605
2740
|
));
|
|
2606
2741
|
return table(fields);
|
|
2607
2742
|
}
|
|
2608
2743
|
function serializeProto(proto, names) {
|
|
2609
2744
|
const fields = [
|
|
2610
|
-
namedField(names.numParams,
|
|
2745
|
+
namedField(names.numParams, vmNumberLiteral(proto.numParams)),
|
|
2611
2746
|
namedField(names.hasVarargs, booleanLiteral(proto.hasVarargs)),
|
|
2612
|
-
namedField(names.maxRegs,
|
|
2613
|
-
namedField(names.code, serializeInstrs(proto
|
|
2747
|
+
namedField(names.maxRegs, vmNumberLiteral(proto.maxRegs)),
|
|
2748
|
+
namedField(names.code, serializeInstrs(proto)),
|
|
2614
2749
|
namedField(names.consts, serializeConsts(proto.consts)),
|
|
2615
2750
|
namedField(names.upvalDescs, serializeUpvalDescs(proto, names)),
|
|
2616
2751
|
namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names)))))
|
|
@@ -2627,7 +2762,7 @@ local ${N.handlers} = {}
|
|
|
2627
2762
|
|
|
2628
2763
|
local function ${N.dispatch}(${N.frame}, pc)
|
|
2629
2764
|
local instr = ${N.frame}.${N.code}[pc]
|
|
2630
|
-
return ${N.handlers}[instr
|
|
2765
|
+
return ${N.handlers}[instr[1]](${N.frame}, pc, instr)
|
|
2631
2766
|
end
|
|
2632
2767
|
|
|
2633
2768
|
local function ${N.rk}(${N.frame}, x)
|
|
@@ -2640,156 +2775,156 @@ end
|
|
|
2640
2775
|
|
|
2641
2776
|
-- MOVE
|
|
2642
2777
|
${N.handlers}[${op(0 /* MOVE */)}] = function(${N.frame}, pc, instr)
|
|
2643
|
-
${N.frame}.${N.R}[instr
|
|
2778
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]]
|
|
2644
2779
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2645
2780
|
end
|
|
2646
2781
|
|
|
2647
2782
|
-- LOADK
|
|
2648
2783
|
${N.handlers}[${op(1 /* LOADK */)}] = function(${N.frame}, pc, instr)
|
|
2649
|
-
${N.frame}.${N.R}[instr
|
|
2784
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.K}[instr[3] + 1]
|
|
2650
2785
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2651
2786
|
end
|
|
2652
2787
|
|
|
2653
2788
|
-- LOADBOOL
|
|
2654
2789
|
${N.handlers}[${op(2 /* LOADBOOL */)}] = function(${N.frame}, pc, instr)
|
|
2655
|
-
${N.frame}.${N.R}[instr
|
|
2790
|
+
${N.frame}.${N.R}[instr[2]] = (instr[3] ~= 0)
|
|
2656
2791
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2657
2792
|
end
|
|
2658
2793
|
|
|
2659
2794
|
-- LOADNIL
|
|
2660
2795
|
${N.handlers}[${op(3 /* LOADNIL */)}] = function(${N.frame}, pc, instr)
|
|
2661
|
-
for i = instr
|
|
2796
|
+
for i = instr[2], instr[3] do ${N.frame}.${N.R}[i] = nil end
|
|
2662
2797
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2663
2798
|
end
|
|
2664
2799
|
|
|
2665
2800
|
-- GETUPVAL
|
|
2666
2801
|
${N.handlers}[${op(4 /* GETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2667
|
-
${N.frame}.${N.R}[instr
|
|
2802
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.upvals}[instr[3] + 1]
|
|
2668
2803
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2669
2804
|
end
|
|
2670
2805
|
|
|
2671
2806
|
-- SETUPVAL
|
|
2672
2807
|
${N.handlers}[${op(5 /* SETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2673
|
-
${N.frame}.${N.upvals}[instr
|
|
2808
|
+
${N.frame}.${N.upvals}[instr[3] + 1] = ${N.frame}.${N.R}[instr[2]]
|
|
2674
2809
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2675
2810
|
end
|
|
2676
2811
|
|
|
2677
2812
|
-- GETGLOBAL (b\uB294 RK\uAC00 \uC544\uB2C8\uB77C \uC0C1\uC218\uD480 \uC21C\uC218 \uC778\uB371\uC2A4)
|
|
2678
2813
|
${N.handlers}[${op(6 /* GETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2679
|
-
${N.frame}.${N.R}[instr
|
|
2814
|
+
${N.frame}.${N.R}[instr[2]] = ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]]
|
|
2680
2815
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2681
2816
|
end
|
|
2682
2817
|
|
|
2683
2818
|
-- SETGLOBAL
|
|
2684
2819
|
${N.handlers}[${op(7 /* SETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2685
|
-
${N.globals}[${N.frame}.${N.K}[instr
|
|
2820
|
+
${N.globals}[${N.frame}.${N.K}[instr[3] + 1]] = ${N.frame}.${N.R}[instr[2]]
|
|
2686
2821
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2687
2822
|
end
|
|
2688
2823
|
|
|
2689
2824
|
-- GETTABLE
|
|
2690
2825
|
${N.handlers}[${op(8 /* GETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2691
|
-
${N.frame}.${N.R}[instr
|
|
2826
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]][${N.rk}(${N.frame}, instr[4])]
|
|
2692
2827
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2693
2828
|
end
|
|
2694
2829
|
|
|
2695
2830
|
-- SETTABLE
|
|
2696
2831
|
${N.handlers}[${op(9 /* SETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2697
|
-
${N.frame}.${N.R}[instr
|
|
2832
|
+
${N.frame}.${N.R}[instr[2]][${N.rk}(${N.frame}, instr[3])] = ${N.rk}(${N.frame}, instr[4])
|
|
2698
2833
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2699
2834
|
end
|
|
2700
2835
|
|
|
2701
2836
|
-- NEWTABLE
|
|
2702
2837
|
${N.handlers}[${op(10 /* NEWTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2703
|
-
${N.frame}.${N.R}[instr
|
|
2838
|
+
${N.frame}.${N.R}[instr[2]] = {}
|
|
2704
2839
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2705
2840
|
end
|
|
2706
2841
|
|
|
2707
2842
|
-- SELF (R[a] = R[b][key]; R[a+1] = R[b])
|
|
2708
2843
|
${N.handlers}[${op(11 /* SELF */)}] = function(${N.frame}, pc, instr)
|
|
2709
|
-
local obj = ${N.frame}.${N.R}[instr
|
|
2710
|
-
${N.frame}.${N.R}[instr
|
|
2711
|
-
${N.frame}.${N.R}[instr
|
|
2844
|
+
local obj = ${N.frame}.${N.R}[instr[3]]
|
|
2845
|
+
${N.frame}.${N.R}[instr[2]] = obj[${N.rk}(${N.frame}, instr[4])]
|
|
2846
|
+
${N.frame}.${N.R}[instr[2] + 1] = obj
|
|
2712
2847
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2713
2848
|
end
|
|
2714
2849
|
|
|
2715
2850
|
-- ADD SUB MUL DIV MOD POW
|
|
2716
2851
|
${N.handlers}[${op(12 /* ADD */)}] = function(${N.frame}, pc, instr)
|
|
2717
|
-
${N.frame}.${N.R}[instr
|
|
2852
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) + ${N.rk}(${N.frame}, instr[4])
|
|
2718
2853
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2719
2854
|
end
|
|
2720
2855
|
${N.handlers}[${op(13 /* SUB */)}] = function(${N.frame}, pc, instr)
|
|
2721
|
-
${N.frame}.${N.R}[instr
|
|
2856
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) - ${N.rk}(${N.frame}, instr[4])
|
|
2722
2857
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2723
2858
|
end
|
|
2724
2859
|
${N.handlers}[${op(14 /* MUL */)}] = function(${N.frame}, pc, instr)
|
|
2725
|
-
${N.frame}.${N.R}[instr
|
|
2860
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) * ${N.rk}(${N.frame}, instr[4])
|
|
2726
2861
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2727
2862
|
end
|
|
2728
2863
|
${N.handlers}[${op(15 /* DIV */)}] = function(${N.frame}, pc, instr)
|
|
2729
|
-
${N.frame}.${N.R}[instr
|
|
2864
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) / ${N.rk}(${N.frame}, instr[4])
|
|
2730
2865
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2731
2866
|
end
|
|
2732
2867
|
${N.handlers}[${op(16 /* MOD */)}] = function(${N.frame}, pc, instr)
|
|
2733
|
-
${N.frame}.${N.R}[instr
|
|
2868
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) % ${N.rk}(${N.frame}, instr[4])
|
|
2734
2869
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2735
2870
|
end
|
|
2736
2871
|
${N.handlers}[${op(17 /* POW */)}] = function(${N.frame}, pc, instr)
|
|
2737
|
-
${N.frame}.${N.R}[instr
|
|
2872
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) ^ ${N.rk}(${N.frame}, instr[4])
|
|
2738
2873
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2739
2874
|
end
|
|
2740
2875
|
|
|
2741
2876
|
-- IDIV (Luau \uBC14\uB2E5 \uB098\uB217\uC148)
|
|
2742
2877
|
${N.handlers}[${op(34 /* IDIV */)}] = function(${N.frame}, pc, instr)
|
|
2743
|
-
${N.frame}.${N.R}[instr
|
|
2878
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) // ${N.rk}(${N.frame}, instr[4])
|
|
2744
2879
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2745
2880
|
end
|
|
2746
2881
|
|
|
2747
2882
|
-- CONCAT
|
|
2748
2883
|
${N.handlers}[${op(18 /* CONCAT */)}] = function(${N.frame}, pc, instr)
|
|
2749
|
-
${N.frame}.${N.R}[instr
|
|
2884
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) .. ${N.rk}(${N.frame}, instr[4])
|
|
2750
2885
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2751
2886
|
end
|
|
2752
2887
|
|
|
2753
2888
|
-- UNM
|
|
2754
2889
|
${N.handlers}[${op(19 /* UNM */)}] = function(${N.frame}, pc, instr)
|
|
2755
|
-
${N.frame}.${N.R}[instr
|
|
2890
|
+
${N.frame}.${N.R}[instr[2]] = -${N.frame}.${N.R}[instr[3]]
|
|
2756
2891
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2757
2892
|
end
|
|
2758
2893
|
|
|
2759
2894
|
-- NOT
|
|
2760
2895
|
${N.handlers}[${op(20 /* NOT */)}] = function(${N.frame}, pc, instr)
|
|
2761
|
-
${N.frame}.${N.R}[instr
|
|
2896
|
+
${N.frame}.${N.R}[instr[2]] = not ${N.frame}.${N.R}[instr[3]]
|
|
2762
2897
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2763
2898
|
end
|
|
2764
2899
|
|
|
2765
2900
|
-- LEN
|
|
2766
2901
|
${N.handlers}[${op(21 /* LEN */)}] = function(${N.frame}, pc, instr)
|
|
2767
|
-
${N.frame}.${N.R}[instr
|
|
2902
|
+
${N.frame}.${N.R}[instr[2]] = #${N.frame}.${N.R}[instr[3]]
|
|
2768
2903
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2769
2904
|
end
|
|
2770
2905
|
|
|
2771
2906
|
-- JMP (pc += a)
|
|
2772
2907
|
${N.handlers}[${op(22 /* JMP */)}] = function(${N.frame}, pc, instr)
|
|
2773
|
-
return ${N.dispatch}(${N.frame}, pc + instr
|
|
2908
|
+
return ${N.dispatch}(${N.frame}, pc + instr[2])
|
|
2774
2909
|
end
|
|
2775
2910
|
|
|
2776
2911
|
-- EQ LT LE (\uAC12 \uC0DD\uC131\uD615 \uBE44\uAD50)
|
|
2777
2912
|
${N.handlers}[${op(23 /* EQ */)}] = function(${N.frame}, pc, instr)
|
|
2778
|
-
${N.frame}.${N.R}[instr
|
|
2913
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) == ${N.rk}(${N.frame}, instr[4])
|
|
2779
2914
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2780
2915
|
end
|
|
2781
2916
|
${N.handlers}[${op(24 /* LT */)}] = function(${N.frame}, pc, instr)
|
|
2782
|
-
${N.frame}.${N.R}[instr
|
|
2917
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) < ${N.rk}(${N.frame}, instr[4])
|
|
2783
2918
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2784
2919
|
end
|
|
2785
2920
|
${N.handlers}[${op(25 /* LE */)}] = function(${N.frame}, pc, instr)
|
|
2786
|
-
${N.frame}.${N.R}[instr
|
|
2921
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) <= ${N.rk}(${N.frame}, instr[4])
|
|
2787
2922
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2788
2923
|
end
|
|
2789
2924
|
|
|
2790
2925
|
-- TEST (if (not R[a]) != (c!=0) then pc += 2 else pc += 1 \u2014 \uBCF4\uD1B5 \uB2E4\uC74C \uBA85\uB839\uC740 JMP)
|
|
2791
2926
|
${N.handlers}[${op(26 /* TEST */)}] = function(${N.frame}, pc, instr)
|
|
2792
|
-
if (not ${N.frame}.${N.R}[instr
|
|
2927
|
+
if (not ${N.frame}.${N.R}[instr[2]]) ~= (instr[4] ~= 0) then
|
|
2793
2928
|
return ${N.dispatch}(${N.frame}, pc + 2)
|
|
2794
2929
|
end
|
|
2795
2930
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
@@ -2797,7 +2932,7 @@ end
|
|
|
2797
2932
|
|
|
2798
2933
|
-- CALL
|
|
2799
2934
|
${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
2800
|
-
local a, b, c = instr
|
|
2935
|
+
local a, b, c = instr[2], instr[3], instr[4]
|
|
2801
2936
|
local fn = ${N.frame}.${N.R}[a]
|
|
2802
2937
|
local nargs
|
|
2803
2938
|
if b == 0 then
|
|
@@ -2822,7 +2957,7 @@ end
|
|
|
2822
2957
|
|
|
2823
2958
|
-- RETURN
|
|
2824
2959
|
${N.handlers}[${op(28 /* RETURN */)}] = function(${N.frame}, pc, instr)
|
|
2825
|
-
local a, b = instr
|
|
2960
|
+
local a, b = instr[2], instr[3]
|
|
2826
2961
|
if b == 0 then
|
|
2827
2962
|
return table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1)
|
|
2828
2963
|
end
|
|
@@ -2831,14 +2966,14 @@ end
|
|
|
2831
2966
|
|
|
2832
2967
|
-- FORPREP
|
|
2833
2968
|
${N.handlers}[${op(29 /* FORPREP */)}] = function(${N.frame}, pc, instr)
|
|
2834
|
-
local a = instr
|
|
2969
|
+
local a = instr[2]
|
|
2835
2970
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] - ${N.frame}.${N.R}[a + 2]
|
|
2836
|
-
return ${N.dispatch}(${N.frame}, pc + instr
|
|
2971
|
+
return ${N.dispatch}(${N.frame}, pc + instr[3])
|
|
2837
2972
|
end
|
|
2838
2973
|
|
|
2839
2974
|
-- FORLOOP
|
|
2840
2975
|
${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
2841
|
-
local a = instr
|
|
2976
|
+
local a = instr[2]
|
|
2842
2977
|
local step = ${N.frame}.${N.R}[a + 2]
|
|
2843
2978
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] + step
|
|
2844
2979
|
local ok
|
|
@@ -2849,26 +2984,26 @@ ${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
|
2849
2984
|
end
|
|
2850
2985
|
if ok then
|
|
2851
2986
|
${N.frame}.${N.R}[a + 3] = ${N.frame}.${N.R}[a]
|
|
2852
|
-
return ${N.dispatch}(${N.frame}, pc + instr
|
|
2987
|
+
return ${N.dispatch}(${N.frame}, pc + instr[3])
|
|
2853
2988
|
end
|
|
2854
2989
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2855
2990
|
end
|
|
2856
2991
|
|
|
2857
2992
|
-- CLOSURE
|
|
2858
2993
|
${N.handlers}[${op(31 /* CLOSURE */)}] = function(${N.frame}, pc, instr)
|
|
2859
|
-
local childProto = ${N.frame}.${N.protos}[instr
|
|
2994
|
+
local childProto = ${N.frame}.${N.protos}[instr[3] + 1]
|
|
2860
2995
|
local n = #childProto.${N.upvalDescs}
|
|
2861
2996
|
local capturedUpvals = table.create(n)
|
|
2862
2997
|
for i = 1, n do
|
|
2863
2998
|
local desc = childProto.${N.upvalDescs}[i]
|
|
2864
2999
|
local captureInstr = ${N.frame}.${N.code}[pc + i]
|
|
2865
3000
|
if desc.${N.kind} == "local" then
|
|
2866
|
-
capturedUpvals[i] = ${N.frame}.${N.R}[captureInstr
|
|
3001
|
+
capturedUpvals[i] = ${N.frame}.${N.R}[captureInstr[3]]
|
|
2867
3002
|
else
|
|
2868
|
-
capturedUpvals[i] = ${N.frame}.${N.upvals}[captureInstr
|
|
3003
|
+
capturedUpvals[i] = ${N.frame}.${N.upvals}[captureInstr[3] + 1]
|
|
2869
3004
|
end
|
|
2870
3005
|
end
|
|
2871
|
-
${N.frame}.${N.R}[instr
|
|
3006
|
+
${N.frame}.${N.R}[instr[2]] = function(...)
|
|
2872
3007
|
return ${N.execute}(childProto, capturedUpvals, ...)
|
|
2873
3008
|
end
|
|
2874
3009
|
return ${N.dispatch}(${N.frame}, pc + 1 + n)
|
|
@@ -2876,22 +3011,22 @@ end
|
|
|
2876
3011
|
|
|
2877
3012
|
-- VARARG
|
|
2878
3013
|
${N.handlers}[${op(32 /* VARARG */)}] = function(${N.frame}, pc, instr)
|
|
2879
|
-
local count = instr
|
|
3014
|
+
local count = instr[3] == 0 and ${N.frame}.${N.nVarargs} or (instr[3] - 1)
|
|
2880
3015
|
for i = 1, count do
|
|
2881
|
-
${N.frame}.${N.R}[instr
|
|
3016
|
+
${N.frame}.${N.R}[instr[2] + i - 1] = ${N.frame}.${N.varargs}[i]
|
|
2882
3017
|
end
|
|
2883
|
-
if instr
|
|
2884
|
-
${N.frame}.${N.multiTop} = instr
|
|
3018
|
+
if instr[3] == 0 then
|
|
3019
|
+
${N.frame}.${N.multiTop} = instr[2] + count
|
|
2885
3020
|
end
|
|
2886
3021
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2887
3022
|
end
|
|
2888
3023
|
|
|
2889
3024
|
-- SETLIST
|
|
2890
3025
|
${N.handlers}[${op(33 /* SETLIST */)}] = function(${N.frame}, pc, instr)
|
|
2891
|
-
local tbl = ${N.frame}.${N.R}[instr
|
|
2892
|
-
local valuesBase = instr
|
|
3026
|
+
local tbl = ${N.frame}.${N.R}[instr[2]]
|
|
3027
|
+
local valuesBase = instr[3]
|
|
2893
3028
|
local top = ${N.frame}.${N.multiTop} or (valuesBase + 1)
|
|
2894
|
-
local startIdx = instr
|
|
3029
|
+
local startIdx = instr[4]
|
|
2895
3030
|
local n = top - valuesBase
|
|
2896
3031
|
for i = 0, n - 1 do
|
|
2897
3032
|
tbl[startIdx + i] = ${N.frame}.${N.R}[valuesBase + i]
|
|
@@ -2965,10 +3100,6 @@ function generateVmNames(random = Math.random) {
|
|
|
2965
3100
|
varargs: id(),
|
|
2966
3101
|
nVarargs: id(),
|
|
2967
3102
|
multiTop: id(),
|
|
2968
|
-
op: id(),
|
|
2969
|
-
a: id(),
|
|
2970
|
-
b: id(),
|
|
2971
|
-
c: id(),
|
|
2972
3103
|
numParams: id(),
|
|
2973
3104
|
hasVarargs: id(),
|
|
2974
3105
|
maxRegs: id(),
|
|
@@ -2980,19 +3111,27 @@ function generateVmNames(random = Math.random) {
|
|
|
2980
3111
|
}
|
|
2981
3112
|
|
|
2982
3113
|
// src/passes/Vmify.ts
|
|
3114
|
+
function markRuntimeNumbersAsStructural(body) {
|
|
3115
|
+
transformExpressions({ body }, (expr) => {
|
|
3116
|
+
if (expr.type === "NumberLiteral") vmStructuralNumbers.add(expr);
|
|
3117
|
+
return void 0;
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
2983
3120
|
function runVmify(program, options) {
|
|
2984
|
-
const
|
|
3121
|
+
const builtinGlobals = options.builtinGlobals ?? DEFAULT_BUILTIN_GLOBALS;
|
|
2985
3122
|
const random = options.random ?? Math.random;
|
|
2986
3123
|
const opcodeMap = createOpcodeMap(random);
|
|
2987
3124
|
const names = generateVmNames(random);
|
|
2988
|
-
const compiler = new VmCompiler(program,
|
|
3125
|
+
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap);
|
|
2989
3126
|
const topProto = compiler.compile();
|
|
3127
|
+
const usedGlobalNames = compiler.getUsedGlobalNames();
|
|
2990
3128
|
const globalsTable = table(
|
|
2991
|
-
|
|
3129
|
+
usedGlobalNames.map((name) => namedField(name, identifier(name)))
|
|
2992
3130
|
);
|
|
2993
3131
|
const runtimeSource = buildVmRuntimeSource(names, opcodeMap);
|
|
2994
3132
|
const runtimeProgram = import_luau_parser4.luauparser.parse(runtimeSource);
|
|
2995
3133
|
const runtimeStatements = runtimeProgram.body.statements;
|
|
3134
|
+
markRuntimeNumbersAsStructural(runtimeProgram.body);
|
|
2996
3135
|
const protoLiteral = serializeProto(topProto, names);
|
|
2997
3136
|
const newBody = [
|
|
2998
3137
|
localStatement(names.globals, globalsTable),
|
|
@@ -3007,10 +3146,10 @@ function runVmify(program, options) {
|
|
|
3007
3146
|
var PASS_ORDER = [
|
|
3008
3147
|
"Vmify",
|
|
3009
3148
|
"GlobalMapping",
|
|
3010
|
-
"StringsToExpressions",
|
|
3011
|
-
"NumbersToExpressions",
|
|
3012
3149
|
"RenameVariables",
|
|
3013
3150
|
"ConstantArray",
|
|
3151
|
+
"StringsToExpressions",
|
|
3152
|
+
"NumbersToExpressions",
|
|
3014
3153
|
"EncryptStrings",
|
|
3015
3154
|
"EncryptNumbers",
|
|
3016
3155
|
"InsertJunk",
|