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.js
CHANGED
|
@@ -181,67 +181,171 @@ function visitArray(arr, visitor) {
|
|
|
181
181
|
arr[i] = mapExpression(arr[i], visitor);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
-
function
|
|
184
|
+
function getExpressionChildren(expr) {
|
|
185
185
|
switch (expr.type) {
|
|
186
|
-
case "InterpolatedStringExpression":
|
|
186
|
+
case "InterpolatedStringExpression": {
|
|
187
|
+
const children = [];
|
|
187
188
|
for (const part of expr.parts) {
|
|
188
189
|
if (part.kind === "expression") {
|
|
189
|
-
|
|
190
|
+
children.push({
|
|
191
|
+
get: () => part.expression,
|
|
192
|
+
set: (v) => {
|
|
193
|
+
part.expression = v;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
190
196
|
}
|
|
191
197
|
}
|
|
192
|
-
|
|
198
|
+
return children;
|
|
199
|
+
}
|
|
193
200
|
case "FunctionExpression":
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
return [];
|
|
202
|
+
case "TableExpression": {
|
|
203
|
+
const children = [];
|
|
197
204
|
for (const field of expr.fields) {
|
|
198
205
|
if (field.type === "TableFieldPositional") {
|
|
199
|
-
|
|
206
|
+
children.push({ get: () => field.value, set: (v) => {
|
|
207
|
+
field.value = v;
|
|
208
|
+
} });
|
|
200
209
|
} else if (field.type === "TableFieldNamed") {
|
|
201
|
-
|
|
210
|
+
children.push({ get: () => field.value, set: (v) => {
|
|
211
|
+
field.value = v;
|
|
212
|
+
} });
|
|
202
213
|
} else if (field.type === "TableFieldComputed") {
|
|
203
|
-
|
|
204
|
-
|
|
214
|
+
children.push({ get: () => field.key, set: (v) => {
|
|
215
|
+
field.key = v;
|
|
216
|
+
} });
|
|
217
|
+
children.push({ get: () => field.value, set: (v) => {
|
|
218
|
+
field.value = v;
|
|
219
|
+
} });
|
|
205
220
|
}
|
|
206
221
|
}
|
|
207
|
-
|
|
222
|
+
return children;
|
|
223
|
+
}
|
|
208
224
|
case "BinaryExpression":
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
225
|
+
return [
|
|
226
|
+
{ get: () => expr.left, set: (v) => {
|
|
227
|
+
expr.left = v;
|
|
228
|
+
} },
|
|
229
|
+
{ get: () => expr.right, set: (v) => {
|
|
230
|
+
expr.right = v;
|
|
231
|
+
} }
|
|
232
|
+
];
|
|
212
233
|
case "UnaryExpression":
|
|
213
|
-
|
|
214
|
-
|
|
234
|
+
return [{ get: () => expr.argument, set: (v) => {
|
|
235
|
+
expr.argument = v;
|
|
236
|
+
} }];
|
|
215
237
|
case "MemberExpression":
|
|
216
|
-
|
|
217
|
-
|
|
238
|
+
return [{ get: () => expr.object, set: (v) => {
|
|
239
|
+
expr.object = v;
|
|
240
|
+
} }];
|
|
218
241
|
case "IndexExpression":
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
242
|
+
return [
|
|
243
|
+
{ get: () => expr.object, set: (v) => {
|
|
244
|
+
expr.object = v;
|
|
245
|
+
} },
|
|
246
|
+
{ get: () => expr.index, set: (v) => {
|
|
247
|
+
expr.index = v;
|
|
248
|
+
} }
|
|
249
|
+
];
|
|
250
|
+
case "CallExpression": {
|
|
251
|
+
const children = [
|
|
252
|
+
{ get: () => expr.callee, set: (v) => {
|
|
253
|
+
expr.callee = v;
|
|
254
|
+
} }
|
|
255
|
+
];
|
|
256
|
+
for (let i = 0; i < expr.arguments.length; i++) {
|
|
257
|
+
const idx = i;
|
|
258
|
+
children.push({
|
|
259
|
+
get: () => expr.arguments[idx],
|
|
260
|
+
set: (v) => {
|
|
261
|
+
expr.arguments[idx] = v;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return children;
|
|
266
|
+
}
|
|
267
|
+
case "MethodCallExpression": {
|
|
268
|
+
const children = [
|
|
269
|
+
{ get: () => expr.object, set: (v) => {
|
|
270
|
+
expr.object = v;
|
|
271
|
+
} }
|
|
272
|
+
];
|
|
273
|
+
for (let i = 0; i < expr.arguments.length; i++) {
|
|
274
|
+
const idx = i;
|
|
275
|
+
children.push({
|
|
276
|
+
get: () => expr.arguments[idx],
|
|
277
|
+
set: (v) => {
|
|
278
|
+
expr.arguments[idx] = v;
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return children;
|
|
283
|
+
}
|
|
230
284
|
case "ParenthesizedExpression":
|
|
231
|
-
|
|
232
|
-
|
|
285
|
+
return [{ get: () => expr.expression, set: (v) => {
|
|
286
|
+
expr.expression = v;
|
|
287
|
+
} }];
|
|
233
288
|
case "TypeAssertionExpression":
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
289
|
+
return [{ get: () => expr.expression, set: (v) => {
|
|
290
|
+
expr.expression = v;
|
|
291
|
+
} }];
|
|
292
|
+
case "IfElseExpression": {
|
|
293
|
+
const children = [];
|
|
237
294
|
for (const clause of expr.clauses) {
|
|
238
|
-
|
|
239
|
-
|
|
295
|
+
children.push({ get: () => clause.condition, set: (v) => {
|
|
296
|
+
clause.condition = v;
|
|
297
|
+
} });
|
|
298
|
+
children.push({ get: () => clause.body, set: (v) => {
|
|
299
|
+
clause.body = v;
|
|
300
|
+
} });
|
|
240
301
|
}
|
|
241
|
-
|
|
242
|
-
|
|
302
|
+
children.push({ get: () => expr.alternate, set: (v) => {
|
|
303
|
+
expr.alternate = v;
|
|
304
|
+
} });
|
|
305
|
+
return children;
|
|
306
|
+
}
|
|
307
|
+
default:
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function mapExpression(root, visitor) {
|
|
312
|
+
if (root.type === "FunctionExpression") {
|
|
313
|
+
walkFunctionBody(root.func, visitor);
|
|
314
|
+
return visitor(root) ?? root;
|
|
315
|
+
}
|
|
316
|
+
let result = root;
|
|
317
|
+
const stack = [{
|
|
318
|
+
expr: root,
|
|
319
|
+
children: getExpressionChildren(root),
|
|
320
|
+
index: 0,
|
|
321
|
+
setInParent: (v) => {
|
|
322
|
+
result = v;
|
|
323
|
+
}
|
|
324
|
+
}];
|
|
325
|
+
while (stack.length > 0) {
|
|
326
|
+
const frame = stack[stack.length - 1];
|
|
327
|
+
if (frame.index < frame.children.length) {
|
|
328
|
+
const child = frame.children[frame.index];
|
|
329
|
+
frame.index++;
|
|
330
|
+
const childExpr = child.get();
|
|
331
|
+
if (childExpr.type === "FunctionExpression") {
|
|
332
|
+
walkFunctionBody(childExpr.func, visitor);
|
|
333
|
+
child.set(visitor(childExpr) ?? childExpr);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
stack.push({
|
|
337
|
+
expr: childExpr,
|
|
338
|
+
children: getExpressionChildren(childExpr),
|
|
339
|
+
index: 0,
|
|
340
|
+
setInParent: child.set
|
|
341
|
+
});
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const finished = visitor(frame.expr) ?? frame.expr;
|
|
345
|
+
frame.setInParent?.(finished);
|
|
346
|
+
stack.pop();
|
|
243
347
|
}
|
|
244
|
-
return
|
|
348
|
+
return result;
|
|
245
349
|
}
|
|
246
350
|
function walkFunctionBody(func, visitor) {
|
|
247
351
|
walkBlock(func.body, visitor);
|
|
@@ -327,6 +431,12 @@ function stringLiteral(value) {
|
|
|
327
431
|
function numberLiteral(value) {
|
|
328
432
|
return { type: "NumberLiteral", value, raw: String(value), line: POS, column: POS };
|
|
329
433
|
}
|
|
434
|
+
var vmStructuralNumbers = /* @__PURE__ */ new WeakSet();
|
|
435
|
+
function vmNumberLiteral(value) {
|
|
436
|
+
const node = numberLiteral(value);
|
|
437
|
+
vmStructuralNumbers.add(node);
|
|
438
|
+
return node;
|
|
439
|
+
}
|
|
330
440
|
function binary(operator, left, right) {
|
|
331
441
|
return { type: "BinaryExpression", operator, left, right, line: POS, column: POS };
|
|
332
442
|
}
|
|
@@ -456,14 +566,15 @@ function splitByteChunks(bytes, min, max) {
|
|
|
456
566
|
return chunks.length > 0 ? chunks : [bytes];
|
|
457
567
|
}
|
|
458
568
|
function stringCharCall(bytes) {
|
|
459
|
-
return call(member(identifier("string"), "char"), bytes.map(
|
|
569
|
+
return call(member(identifier("string"), "char"), bytes.map(vmNumberLiteral));
|
|
460
570
|
}
|
|
461
571
|
function buildConcatChain(chunks) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
572
|
+
function build(lo, hi) {
|
|
573
|
+
if (hi - lo === 1) return stringCharCall(chunks[lo]);
|
|
574
|
+
const mid = lo + Math.floor((hi - lo) / 2);
|
|
575
|
+
return binary("..", build(lo, mid), build(mid, hi));
|
|
465
576
|
}
|
|
466
|
-
return
|
|
577
|
+
return build(0, chunks.length);
|
|
467
578
|
}
|
|
468
579
|
function shuffledIndices(n) {
|
|
469
580
|
const arr = Array.from({ length: n }, (_, i) => i);
|
|
@@ -486,7 +597,7 @@ function buildShuffledTables(chunks) {
|
|
|
486
597
|
for (let slot = 0; slot < n; slot++) {
|
|
487
598
|
order[storageOrder[slot]] = slot + 1;
|
|
488
599
|
}
|
|
489
|
-
const orderFields = order.map((slot) => positionalField(
|
|
600
|
+
const orderFields = order.map((slot) => positionalField(vmNumberLiteral(slot)));
|
|
490
601
|
return {
|
|
491
602
|
storeVar,
|
|
492
603
|
orderVar,
|
|
@@ -505,7 +616,7 @@ function buildForLoopStringExpr(chunks) {
|
|
|
505
616
|
localStatement(accVar, stringLiteral("")),
|
|
506
617
|
numericForStatement(
|
|
507
618
|
iVar,
|
|
508
|
-
|
|
619
|
+
vmNumberLiteral(1),
|
|
509
620
|
unary("#", identifier(orderVar)),
|
|
510
621
|
block([
|
|
511
622
|
assignmentStatement(
|
|
@@ -537,7 +648,7 @@ function buildRecursiveStringExpr(chunks) {
|
|
|
537
648
|
]),
|
|
538
649
|
returnStatement([
|
|
539
650
|
call(identifier(recName), [
|
|
540
|
-
binary("+", identifier(iParam),
|
|
651
|
+
binary("+", identifier(iParam), vmNumberLiteral(1)),
|
|
541
652
|
binary(
|
|
542
653
|
"..",
|
|
543
654
|
identifier(accParam),
|
|
@@ -549,18 +660,21 @@ function buildRecursiveStringExpr(chunks) {
|
|
|
549
660
|
return iife([
|
|
550
661
|
...stmts,
|
|
551
662
|
localFunctionStatement(recName, functionBody([functionParam(iParam), functionParam(accParam)], body)),
|
|
552
|
-
returnStatement([call(identifier(recName), [
|
|
663
|
+
returnStatement([call(identifier(recName), [vmNumberLiteral(1), stringLiteral("")])])
|
|
553
664
|
]);
|
|
554
665
|
}
|
|
555
666
|
function pickControlFlowStrategy() {
|
|
556
667
|
return Math.random() < 0.5 ? "forLoop" : "recursive";
|
|
557
668
|
}
|
|
669
|
+
var MAX_CHUNKS_PER_STRING = 2e4;
|
|
558
670
|
function runStringsToExpressions(program, options) {
|
|
559
671
|
const controlFlowProbability = options.controlFlowProbability ?? CONTROL_FLOW_PROBABILITY_DEFAULT;
|
|
560
672
|
transformExpressions(program, (expr) => {
|
|
561
673
|
if (expr.type !== "StringLiteral") return;
|
|
562
674
|
if (expr.value.length === 0) return;
|
|
563
675
|
const bytes = toUtf8Bytes(expr.value);
|
|
676
|
+
const estimatedChunks = Math.ceil(bytes.length / Math.max(1, options.min));
|
|
677
|
+
if (estimatedChunks > MAX_CHUNKS_PER_STRING) return;
|
|
564
678
|
const chunks = splitByteChunks(bytes, options.min, options.max);
|
|
565
679
|
if (chunks.length > 1 && Math.random() < controlFlowProbability) {
|
|
566
680
|
return pickControlFlowStrategy() === "forLoop" ? buildForLoopStringExpr(chunks) : buildRecursiveStringExpr(chunks);
|
|
@@ -733,6 +847,7 @@ function runNumbersToExpressions(program, options) {
|
|
|
733
847
|
const controlFlowProbability = options.controlFlowProbability ?? CONTROL_FLOW_PROBABILITY_DEFAULT2;
|
|
734
848
|
transformExpressions(program, (expr) => {
|
|
735
849
|
if (expr.type !== "NumberLiteral") return;
|
|
850
|
+
if (vmStructuralNumbers.has(expr)) return;
|
|
736
851
|
if (Number.isInteger(expr.value) && Math.random() < controlFlowProbability) {
|
|
737
852
|
return buildControlFlowNumberExpr(expr.value, options.min, options.max);
|
|
738
853
|
}
|
|
@@ -925,7 +1040,7 @@ function runConstantArray(program, options) {
|
|
|
925
1040
|
seen.add(k);
|
|
926
1041
|
order.push({ kind: "string", value: expr.value });
|
|
927
1042
|
}
|
|
928
|
-
} else if (expr.type === "NumberLiteral") {
|
|
1043
|
+
} else if (expr.type === "NumberLiteral" && !vmStructuralNumbers.has(expr)) {
|
|
929
1044
|
const k = keyOf("number", expr.value);
|
|
930
1045
|
if (!seen.has(k)) {
|
|
931
1046
|
seen.add(k);
|
|
@@ -945,11 +1060,11 @@ function runConstantArray(program, options) {
|
|
|
945
1060
|
transformExpressions(program, (expr) => {
|
|
946
1061
|
if (expr.type === "StringLiteral") {
|
|
947
1062
|
const idx = indexOf.get(keyOf("string", expr.value));
|
|
948
|
-
return index(identifier(arrayName),
|
|
1063
|
+
return index(identifier(arrayName), vmNumberLiteral(idx));
|
|
949
1064
|
}
|
|
950
|
-
if (expr.type === "NumberLiteral") {
|
|
1065
|
+
if (expr.type === "NumberLiteral" && !vmStructuralNumbers.has(expr)) {
|
|
951
1066
|
const idx = indexOf.get(keyOf("number", expr.value));
|
|
952
|
-
return index(identifier(arrayName),
|
|
1067
|
+
return index(identifier(arrayName), vmNumberLiteral(idx));
|
|
953
1068
|
}
|
|
954
1069
|
return void 0;
|
|
955
1070
|
});
|
|
@@ -990,15 +1105,15 @@ function obfuscatedNumber(value) {
|
|
|
990
1105
|
const variant = randomInt4(0, 2);
|
|
991
1106
|
if (variant === 0) {
|
|
992
1107
|
const a2 = randomInt4(0, value);
|
|
993
|
-
return binary("+",
|
|
1108
|
+
return binary("+", vmNumberLiteral(a2), vmNumberLiteral(value - a2));
|
|
994
1109
|
}
|
|
995
1110
|
if (variant === 1) {
|
|
996
1111
|
const a2 = randomInt4(0, 16777215);
|
|
997
|
-
return binary("-",
|
|
1112
|
+
return binary("-", vmNumberLiteral(value + a2), vmNumberLiteral(a2));
|
|
998
1113
|
}
|
|
999
1114
|
const a = randomInt4(0, 16777215);
|
|
1000
1115
|
const b = (value ^ a) >>> 0;
|
|
1001
|
-
return call(member(identifier("bit32"), "bxor"), [
|
|
1116
|
+
return call(member(identifier("bit32"), "bxor"), [vmNumberLiteral(a), vmNumberLiteral(b)]);
|
|
1002
1117
|
}
|
|
1003
1118
|
function buildDecoderStatement(name) {
|
|
1004
1119
|
const dataParam = randomName3();
|
|
@@ -1009,17 +1124,17 @@ function buildDecoderStatement(name) {
|
|
|
1009
1124
|
const nKeysVar = randomName3();
|
|
1010
1125
|
const idxExpr = binary(
|
|
1011
1126
|
"+",
|
|
1012
|
-
binary("%", binary("-", identifier(iVar),
|
|
1013
|
-
|
|
1127
|
+
binary("%", binary("-", identifier(iVar), vmNumberLiteral(1)), identifier(nKeysVar)),
|
|
1128
|
+
vmNumberLiteral(1)
|
|
1014
1129
|
);
|
|
1015
1130
|
const keyByte = index(identifier(keysParam), idxExpr);
|
|
1016
1131
|
const mixed = call(member(identifier("bit32"), "band"), [
|
|
1017
|
-
call(member(identifier("bit32"), "lrotate"), [keyByte, binary("%", identifier(iVar),
|
|
1018
|
-
|
|
1132
|
+
call(member(identifier("bit32"), "lrotate"), [keyByte, binary("%", identifier(iVar), vmNumberLiteral(8))]),
|
|
1133
|
+
vmNumberLiteral(255)
|
|
1019
1134
|
]);
|
|
1020
1135
|
const posByte = call(member(identifier("bit32"), "band"), [
|
|
1021
1136
|
binary("*", identifier(iVar), identifier(multParam)),
|
|
1022
|
-
|
|
1137
|
+
vmNumberLiteral(255)
|
|
1023
1138
|
]);
|
|
1024
1139
|
const keystream = call(member(identifier("bit32"), "bxor"), [mixed, posByte]);
|
|
1025
1140
|
const finalByte = call(member(identifier("bit32"), "bxor"), [
|
|
@@ -1031,7 +1146,7 @@ function buildDecoderStatement(name) {
|
|
|
1031
1146
|
localStatement(outVar, table([])),
|
|
1032
1147
|
numericForStatement(
|
|
1033
1148
|
iVar,
|
|
1034
|
-
|
|
1149
|
+
vmNumberLiteral(1),
|
|
1035
1150
|
unary("#", identifier(dataParam)),
|
|
1036
1151
|
block([
|
|
1037
1152
|
assignmentStatement(
|
|
@@ -1059,7 +1174,7 @@ function runEncryptStrings(program, _options) {
|
|
|
1059
1174
|
const mult = randomInt4(1, 255);
|
|
1060
1175
|
const plainBytes = toUtf8Bytes2(expr.value);
|
|
1061
1176
|
const cipherBytes = plainBytes.map((b, idx) => b ^ keystreamByte(keys, mult, idx + 1));
|
|
1062
|
-
const dataTable = table(cipherBytes.map((b) => positionalField(
|
|
1177
|
+
const dataTable = table(cipherBytes.map((b) => positionalField(vmNumberLiteral(b))));
|
|
1063
1178
|
const keysTable = table(keys.map((k) => positionalField(obfuscatedNumber(k))));
|
|
1064
1179
|
const multExpr = obfuscatedNumber(mult);
|
|
1065
1180
|
return call(identifier(decoderName), [dataTable, keysTable, multExpr]);
|
|
@@ -1126,6 +1241,7 @@ function runEncryptNumbers(program, _options) {
|
|
|
1126
1241
|
let used = false;
|
|
1127
1242
|
transformExpressions(program, (expr) => {
|
|
1128
1243
|
if (expr.type !== "NumberLiteral") return;
|
|
1244
|
+
if (vmStructuralNumbers.has(expr)) return;
|
|
1129
1245
|
if (!isEncryptable(expr.value)) return;
|
|
1130
1246
|
used = true;
|
|
1131
1247
|
const keyA = randomInt5(1, 16777215);
|
|
@@ -1376,16 +1492,18 @@ function createProto(id) {
|
|
|
1376
1492
|
maxRegs: 0,
|
|
1377
1493
|
code: [],
|
|
1378
1494
|
consts: [],
|
|
1495
|
+
constIndex: /* @__PURE__ */ new Map(),
|
|
1379
1496
|
upvalDescs: [],
|
|
1380
1497
|
protos: []
|
|
1381
1498
|
};
|
|
1382
1499
|
}
|
|
1383
1500
|
function internConst(proto, value) {
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
}
|
|
1501
|
+
const existing = proto.constIndex.get(value);
|
|
1502
|
+
if (existing !== void 0) return existing;
|
|
1387
1503
|
proto.consts.push(value);
|
|
1388
|
-
|
|
1504
|
+
const idx = proto.consts.length - 1;
|
|
1505
|
+
proto.constIndex.set(value, idx);
|
|
1506
|
+
return idx;
|
|
1389
1507
|
}
|
|
1390
1508
|
|
|
1391
1509
|
// src/passes/vmify/registers.ts
|
|
@@ -1637,6 +1755,23 @@ var VmCompiler = class {
|
|
|
1637
1755
|
captured = /* @__PURE__ */ new Set();
|
|
1638
1756
|
protoIdCounter = 0;
|
|
1639
1757
|
declToBinding = /* @__PURE__ */ new Map();
|
|
1758
|
+
/**
|
|
1759
|
+
* 실제로 프로그램이 참조하는 모든 전역 이름(사전 등록된 builtinGlobals +
|
|
1760
|
+
* 스코프 분석이 자동으로 찾아낸, 로컬/파라미터/업밸류로 안 풀리는 나머지 식별자
|
|
1761
|
+
* 전부)을 돌려준다. Vmify.ts가 이 목록으로 브릿지 테이블(globals)을 만든다.
|
|
1762
|
+
*
|
|
1763
|
+
* 예전엔 호출자가 넘긴 builtinGlobals 목록만 브릿지에 넣었는데, 그 목록에
|
|
1764
|
+
* 없는 전역(예: Roblox API나 실행기 전용 전역을 다 못 채운 경우)을 참조하면
|
|
1765
|
+
* 컴파일은 성공하지만 GETGLOBAL이 조용히 nil을 반환해서 "attempt to call a
|
|
1766
|
+
* nil value"로 터진다 — 게다가 원인이 VM 디스패치 쪽 스택에서만 보여서
|
|
1767
|
+
* "opcode 매핑이 깨졌나?" 하고 엉뚱한 곳을 의심하게 만든다. analyzeScopes는
|
|
1768
|
+
* 어차피 로컬/업밸류로 못 푸는 식별자를 전부 global 바인딩으로 잡아두므로,
|
|
1769
|
+
* builtinGlobals에 없던 이름도 여기 globalsByName에는 다 들어있다 —
|
|
1770
|
+
* 하드코딩된 허용목록 대신 이걸 그대로 쓰면 커버리지 문제 자체가 사라진다.
|
|
1771
|
+
*/
|
|
1772
|
+
getUsedGlobalNames() {
|
|
1773
|
+
return Array.from(this.analysis.globalsByName.keys());
|
|
1774
|
+
}
|
|
1640
1775
|
computeCaptured() {
|
|
1641
1776
|
for (const binding of this.analysis.bindings.values()) {
|
|
1642
1777
|
if (binding.kind === "global") continue;
|
|
@@ -2557,13 +2692,13 @@ function serializeConstValue(v) {
|
|
|
2557
2692
|
if (typeof v === "number") return numberLiteral(v);
|
|
2558
2693
|
return booleanLiteral(v);
|
|
2559
2694
|
}
|
|
2560
|
-
function serializeInstrs(proto
|
|
2695
|
+
function serializeInstrs(proto) {
|
|
2561
2696
|
const fields = proto.code.map((instr) => positionalField(
|
|
2562
2697
|
table([
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2698
|
+
positionalField(vmNumberLiteral(instr.op)),
|
|
2699
|
+
positionalField(vmNumberLiteral(instr.a)),
|
|
2700
|
+
positionalField(vmNumberLiteral(instr.b)),
|
|
2701
|
+
positionalField(vmNumberLiteral(instr.c))
|
|
2567
2702
|
])
|
|
2568
2703
|
));
|
|
2569
2704
|
return table(fields);
|
|
@@ -2572,17 +2707,17 @@ function serializeUpvalDescs(proto, names) {
|
|
|
2572
2707
|
const fields = proto.upvalDescs.map((d) => positionalField(
|
|
2573
2708
|
table([
|
|
2574
2709
|
namedField(names.kind, stringLiteral(d.kind)),
|
|
2575
|
-
namedField(names.index,
|
|
2710
|
+
namedField(names.index, vmNumberLiteral(d.index))
|
|
2576
2711
|
])
|
|
2577
2712
|
));
|
|
2578
2713
|
return table(fields);
|
|
2579
2714
|
}
|
|
2580
2715
|
function serializeProto(proto, names) {
|
|
2581
2716
|
const fields = [
|
|
2582
|
-
namedField(names.numParams,
|
|
2717
|
+
namedField(names.numParams, vmNumberLiteral(proto.numParams)),
|
|
2583
2718
|
namedField(names.hasVarargs, booleanLiteral(proto.hasVarargs)),
|
|
2584
|
-
namedField(names.maxRegs,
|
|
2585
|
-
namedField(names.code, serializeInstrs(proto
|
|
2719
|
+
namedField(names.maxRegs, vmNumberLiteral(proto.maxRegs)),
|
|
2720
|
+
namedField(names.code, serializeInstrs(proto)),
|
|
2586
2721
|
namedField(names.consts, serializeConsts(proto.consts)),
|
|
2587
2722
|
namedField(names.upvalDescs, serializeUpvalDescs(proto, names)),
|
|
2588
2723
|
namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names)))))
|
|
@@ -2599,7 +2734,7 @@ local ${N.handlers} = {}
|
|
|
2599
2734
|
|
|
2600
2735
|
local function ${N.dispatch}(${N.frame}, pc)
|
|
2601
2736
|
local instr = ${N.frame}.${N.code}[pc]
|
|
2602
|
-
return ${N.handlers}[instr
|
|
2737
|
+
return ${N.handlers}[instr[1]](${N.frame}, pc, instr)
|
|
2603
2738
|
end
|
|
2604
2739
|
|
|
2605
2740
|
local function ${N.rk}(${N.frame}, x)
|
|
@@ -2612,156 +2747,156 @@ end
|
|
|
2612
2747
|
|
|
2613
2748
|
-- MOVE
|
|
2614
2749
|
${N.handlers}[${op(0 /* MOVE */)}] = function(${N.frame}, pc, instr)
|
|
2615
|
-
${N.frame}.${N.R}[instr
|
|
2750
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]]
|
|
2616
2751
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2617
2752
|
end
|
|
2618
2753
|
|
|
2619
2754
|
-- LOADK
|
|
2620
2755
|
${N.handlers}[${op(1 /* LOADK */)}] = function(${N.frame}, pc, instr)
|
|
2621
|
-
${N.frame}.${N.R}[instr
|
|
2756
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.K}[instr[3] + 1]
|
|
2622
2757
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2623
2758
|
end
|
|
2624
2759
|
|
|
2625
2760
|
-- LOADBOOL
|
|
2626
2761
|
${N.handlers}[${op(2 /* LOADBOOL */)}] = function(${N.frame}, pc, instr)
|
|
2627
|
-
${N.frame}.${N.R}[instr
|
|
2762
|
+
${N.frame}.${N.R}[instr[2]] = (instr[3] ~= 0)
|
|
2628
2763
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2629
2764
|
end
|
|
2630
2765
|
|
|
2631
2766
|
-- LOADNIL
|
|
2632
2767
|
${N.handlers}[${op(3 /* LOADNIL */)}] = function(${N.frame}, pc, instr)
|
|
2633
|
-
for i = instr
|
|
2768
|
+
for i = instr[2], instr[3] do ${N.frame}.${N.R}[i] = nil end
|
|
2634
2769
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2635
2770
|
end
|
|
2636
2771
|
|
|
2637
2772
|
-- GETUPVAL
|
|
2638
2773
|
${N.handlers}[${op(4 /* GETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2639
|
-
${N.frame}.${N.R}[instr
|
|
2774
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.upvals}[instr[3] + 1]
|
|
2640
2775
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2641
2776
|
end
|
|
2642
2777
|
|
|
2643
2778
|
-- SETUPVAL
|
|
2644
2779
|
${N.handlers}[${op(5 /* SETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2645
|
-
${N.frame}.${N.upvals}[instr
|
|
2780
|
+
${N.frame}.${N.upvals}[instr[3] + 1] = ${N.frame}.${N.R}[instr[2]]
|
|
2646
2781
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2647
2782
|
end
|
|
2648
2783
|
|
|
2649
2784
|
-- GETGLOBAL (b\uB294 RK\uAC00 \uC544\uB2C8\uB77C \uC0C1\uC218\uD480 \uC21C\uC218 \uC778\uB371\uC2A4)
|
|
2650
2785
|
${N.handlers}[${op(6 /* GETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2651
|
-
${N.frame}.${N.R}[instr
|
|
2786
|
+
${N.frame}.${N.R}[instr[2]] = ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]]
|
|
2652
2787
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2653
2788
|
end
|
|
2654
2789
|
|
|
2655
2790
|
-- SETGLOBAL
|
|
2656
2791
|
${N.handlers}[${op(7 /* SETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2657
|
-
${N.globals}[${N.frame}.${N.K}[instr
|
|
2792
|
+
${N.globals}[${N.frame}.${N.K}[instr[3] + 1]] = ${N.frame}.${N.R}[instr[2]]
|
|
2658
2793
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2659
2794
|
end
|
|
2660
2795
|
|
|
2661
2796
|
-- GETTABLE
|
|
2662
2797
|
${N.handlers}[${op(8 /* GETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2663
|
-
${N.frame}.${N.R}[instr
|
|
2798
|
+
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]][${N.rk}(${N.frame}, instr[4])]
|
|
2664
2799
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2665
2800
|
end
|
|
2666
2801
|
|
|
2667
2802
|
-- SETTABLE
|
|
2668
2803
|
${N.handlers}[${op(9 /* SETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2669
|
-
${N.frame}.${N.R}[instr
|
|
2804
|
+
${N.frame}.${N.R}[instr[2]][${N.rk}(${N.frame}, instr[3])] = ${N.rk}(${N.frame}, instr[4])
|
|
2670
2805
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2671
2806
|
end
|
|
2672
2807
|
|
|
2673
2808
|
-- NEWTABLE
|
|
2674
2809
|
${N.handlers}[${op(10 /* NEWTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2675
|
-
${N.frame}.${N.R}[instr
|
|
2810
|
+
${N.frame}.${N.R}[instr[2]] = {}
|
|
2676
2811
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2677
2812
|
end
|
|
2678
2813
|
|
|
2679
2814
|
-- SELF (R[a] = R[b][key]; R[a+1] = R[b])
|
|
2680
2815
|
${N.handlers}[${op(11 /* SELF */)}] = function(${N.frame}, pc, instr)
|
|
2681
|
-
local obj = ${N.frame}.${N.R}[instr
|
|
2682
|
-
${N.frame}.${N.R}[instr
|
|
2683
|
-
${N.frame}.${N.R}[instr
|
|
2816
|
+
local obj = ${N.frame}.${N.R}[instr[3]]
|
|
2817
|
+
${N.frame}.${N.R}[instr[2]] = obj[${N.rk}(${N.frame}, instr[4])]
|
|
2818
|
+
${N.frame}.${N.R}[instr[2] + 1] = obj
|
|
2684
2819
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2685
2820
|
end
|
|
2686
2821
|
|
|
2687
2822
|
-- ADD SUB MUL DIV MOD POW
|
|
2688
2823
|
${N.handlers}[${op(12 /* ADD */)}] = function(${N.frame}, pc, instr)
|
|
2689
|
-
${N.frame}.${N.R}[instr
|
|
2824
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) + ${N.rk}(${N.frame}, instr[4])
|
|
2690
2825
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2691
2826
|
end
|
|
2692
2827
|
${N.handlers}[${op(13 /* SUB */)}] = function(${N.frame}, pc, instr)
|
|
2693
|
-
${N.frame}.${N.R}[instr
|
|
2828
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) - ${N.rk}(${N.frame}, instr[4])
|
|
2694
2829
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2695
2830
|
end
|
|
2696
2831
|
${N.handlers}[${op(14 /* MUL */)}] = 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
|
${N.handlers}[${op(15 /* DIV */)}] = function(${N.frame}, pc, instr)
|
|
2701
|
-
${N.frame}.${N.R}[instr
|
|
2836
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) / ${N.rk}(${N.frame}, instr[4])
|
|
2702
2837
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2703
2838
|
end
|
|
2704
2839
|
${N.handlers}[${op(16 /* MOD */)}] = function(${N.frame}, pc, instr)
|
|
2705
|
-
${N.frame}.${N.R}[instr
|
|
2840
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) % ${N.rk}(${N.frame}, instr[4])
|
|
2706
2841
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2707
2842
|
end
|
|
2708
2843
|
${N.handlers}[${op(17 /* POW */)}] = function(${N.frame}, pc, instr)
|
|
2709
|
-
${N.frame}.${N.R}[instr
|
|
2844
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) ^ ${N.rk}(${N.frame}, instr[4])
|
|
2710
2845
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2711
2846
|
end
|
|
2712
2847
|
|
|
2713
2848
|
-- IDIV (Luau \uBC14\uB2E5 \uB098\uB217\uC148)
|
|
2714
2849
|
${N.handlers}[${op(34 /* IDIV */)}] = function(${N.frame}, pc, instr)
|
|
2715
|
-
${N.frame}.${N.R}[instr
|
|
2850
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) // ${N.rk}(${N.frame}, instr[4])
|
|
2716
2851
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2717
2852
|
end
|
|
2718
2853
|
|
|
2719
2854
|
-- CONCAT
|
|
2720
2855
|
${N.handlers}[${op(18 /* CONCAT */)}] = 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
|
|
|
2725
2860
|
-- UNM
|
|
2726
2861
|
${N.handlers}[${op(19 /* UNM */)}] = function(${N.frame}, pc, instr)
|
|
2727
|
-
${N.frame}.${N.R}[instr
|
|
2862
|
+
${N.frame}.${N.R}[instr[2]] = -${N.frame}.${N.R}[instr[3]]
|
|
2728
2863
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2729
2864
|
end
|
|
2730
2865
|
|
|
2731
2866
|
-- NOT
|
|
2732
2867
|
${N.handlers}[${op(20 /* NOT */)}] = function(${N.frame}, pc, instr)
|
|
2733
|
-
${N.frame}.${N.R}[instr
|
|
2868
|
+
${N.frame}.${N.R}[instr[2]] = not ${N.frame}.${N.R}[instr[3]]
|
|
2734
2869
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2735
2870
|
end
|
|
2736
2871
|
|
|
2737
2872
|
-- LEN
|
|
2738
2873
|
${N.handlers}[${op(21 /* LEN */)}] = function(${N.frame}, pc, instr)
|
|
2739
|
-
${N.frame}.${N.R}[instr
|
|
2874
|
+
${N.frame}.${N.R}[instr[2]] = #${N.frame}.${N.R}[instr[3]]
|
|
2740
2875
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2741
2876
|
end
|
|
2742
2877
|
|
|
2743
2878
|
-- JMP (pc += a)
|
|
2744
2879
|
${N.handlers}[${op(22 /* JMP */)}] = function(${N.frame}, pc, instr)
|
|
2745
|
-
return ${N.dispatch}(${N.frame}, pc + instr
|
|
2880
|
+
return ${N.dispatch}(${N.frame}, pc + instr[2])
|
|
2746
2881
|
end
|
|
2747
2882
|
|
|
2748
2883
|
-- EQ LT LE (\uAC12 \uC0DD\uC131\uD615 \uBE44\uAD50)
|
|
2749
2884
|
${N.handlers}[${op(23 /* EQ */)}] = function(${N.frame}, pc, instr)
|
|
2750
|
-
${N.frame}.${N.R}[instr
|
|
2885
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) == ${N.rk}(${N.frame}, instr[4])
|
|
2751
2886
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2752
2887
|
end
|
|
2753
2888
|
${N.handlers}[${op(24 /* LT */)}] = function(${N.frame}, pc, instr)
|
|
2754
|
-
${N.frame}.${N.R}[instr
|
|
2889
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) < ${N.rk}(${N.frame}, instr[4])
|
|
2755
2890
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2756
2891
|
end
|
|
2757
2892
|
${N.handlers}[${op(25 /* LE */)}] = function(${N.frame}, pc, instr)
|
|
2758
|
-
${N.frame}.${N.R}[instr
|
|
2893
|
+
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) <= ${N.rk}(${N.frame}, instr[4])
|
|
2759
2894
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2760
2895
|
end
|
|
2761
2896
|
|
|
2762
2897
|
-- TEST (if (not R[a]) != (c!=0) then pc += 2 else pc += 1 \u2014 \uBCF4\uD1B5 \uB2E4\uC74C \uBA85\uB839\uC740 JMP)
|
|
2763
2898
|
${N.handlers}[${op(26 /* TEST */)}] = function(${N.frame}, pc, instr)
|
|
2764
|
-
if (not ${N.frame}.${N.R}[instr
|
|
2899
|
+
if (not ${N.frame}.${N.R}[instr[2]]) ~= (instr[4] ~= 0) then
|
|
2765
2900
|
return ${N.dispatch}(${N.frame}, pc + 2)
|
|
2766
2901
|
end
|
|
2767
2902
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
@@ -2769,7 +2904,7 @@ end
|
|
|
2769
2904
|
|
|
2770
2905
|
-- CALL
|
|
2771
2906
|
${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
2772
|
-
local a, b, c = instr
|
|
2907
|
+
local a, b, c = instr[2], instr[3], instr[4]
|
|
2773
2908
|
local fn = ${N.frame}.${N.R}[a]
|
|
2774
2909
|
local nargs
|
|
2775
2910
|
if b == 0 then
|
|
@@ -2794,7 +2929,7 @@ end
|
|
|
2794
2929
|
|
|
2795
2930
|
-- RETURN
|
|
2796
2931
|
${N.handlers}[${op(28 /* RETURN */)}] = function(${N.frame}, pc, instr)
|
|
2797
|
-
local a, b = instr
|
|
2932
|
+
local a, b = instr[2], instr[3]
|
|
2798
2933
|
if b == 0 then
|
|
2799
2934
|
return table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1)
|
|
2800
2935
|
end
|
|
@@ -2803,14 +2938,14 @@ end
|
|
|
2803
2938
|
|
|
2804
2939
|
-- FORPREP
|
|
2805
2940
|
${N.handlers}[${op(29 /* FORPREP */)}] = function(${N.frame}, pc, instr)
|
|
2806
|
-
local a = instr
|
|
2941
|
+
local a = instr[2]
|
|
2807
2942
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] - ${N.frame}.${N.R}[a + 2]
|
|
2808
|
-
return ${N.dispatch}(${N.frame}, pc + instr
|
|
2943
|
+
return ${N.dispatch}(${N.frame}, pc + instr[3])
|
|
2809
2944
|
end
|
|
2810
2945
|
|
|
2811
2946
|
-- FORLOOP
|
|
2812
2947
|
${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
2813
|
-
local a = instr
|
|
2948
|
+
local a = instr[2]
|
|
2814
2949
|
local step = ${N.frame}.${N.R}[a + 2]
|
|
2815
2950
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] + step
|
|
2816
2951
|
local ok
|
|
@@ -2821,26 +2956,26 @@ ${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
|
2821
2956
|
end
|
|
2822
2957
|
if ok then
|
|
2823
2958
|
${N.frame}.${N.R}[a + 3] = ${N.frame}.${N.R}[a]
|
|
2824
|
-
return ${N.dispatch}(${N.frame}, pc + instr
|
|
2959
|
+
return ${N.dispatch}(${N.frame}, pc + instr[3])
|
|
2825
2960
|
end
|
|
2826
2961
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2827
2962
|
end
|
|
2828
2963
|
|
|
2829
2964
|
-- CLOSURE
|
|
2830
2965
|
${N.handlers}[${op(31 /* CLOSURE */)}] = function(${N.frame}, pc, instr)
|
|
2831
|
-
local childProto = ${N.frame}.${N.protos}[instr
|
|
2966
|
+
local childProto = ${N.frame}.${N.protos}[instr[3] + 1]
|
|
2832
2967
|
local n = #childProto.${N.upvalDescs}
|
|
2833
2968
|
local capturedUpvals = table.create(n)
|
|
2834
2969
|
for i = 1, n do
|
|
2835
2970
|
local desc = childProto.${N.upvalDescs}[i]
|
|
2836
2971
|
local captureInstr = ${N.frame}.${N.code}[pc + i]
|
|
2837
2972
|
if desc.${N.kind} == "local" then
|
|
2838
|
-
capturedUpvals[i] = ${N.frame}.${N.R}[captureInstr
|
|
2973
|
+
capturedUpvals[i] = ${N.frame}.${N.R}[captureInstr[3]]
|
|
2839
2974
|
else
|
|
2840
|
-
capturedUpvals[i] = ${N.frame}.${N.upvals}[captureInstr
|
|
2975
|
+
capturedUpvals[i] = ${N.frame}.${N.upvals}[captureInstr[3] + 1]
|
|
2841
2976
|
end
|
|
2842
2977
|
end
|
|
2843
|
-
${N.frame}.${N.R}[instr
|
|
2978
|
+
${N.frame}.${N.R}[instr[2]] = function(...)
|
|
2844
2979
|
return ${N.execute}(childProto, capturedUpvals, ...)
|
|
2845
2980
|
end
|
|
2846
2981
|
return ${N.dispatch}(${N.frame}, pc + 1 + n)
|
|
@@ -2848,22 +2983,22 @@ end
|
|
|
2848
2983
|
|
|
2849
2984
|
-- VARARG
|
|
2850
2985
|
${N.handlers}[${op(32 /* VARARG */)}] = function(${N.frame}, pc, instr)
|
|
2851
|
-
local count = instr
|
|
2986
|
+
local count = instr[3] == 0 and ${N.frame}.${N.nVarargs} or (instr[3] - 1)
|
|
2852
2987
|
for i = 1, count do
|
|
2853
|
-
${N.frame}.${N.R}[instr
|
|
2988
|
+
${N.frame}.${N.R}[instr[2] + i - 1] = ${N.frame}.${N.varargs}[i]
|
|
2854
2989
|
end
|
|
2855
|
-
if instr
|
|
2856
|
-
${N.frame}.${N.multiTop} = instr
|
|
2990
|
+
if instr[3] == 0 then
|
|
2991
|
+
${N.frame}.${N.multiTop} = instr[2] + count
|
|
2857
2992
|
end
|
|
2858
2993
|
return ${N.dispatch}(${N.frame}, pc + 1)
|
|
2859
2994
|
end
|
|
2860
2995
|
|
|
2861
2996
|
-- SETLIST
|
|
2862
2997
|
${N.handlers}[${op(33 /* SETLIST */)}] = function(${N.frame}, pc, instr)
|
|
2863
|
-
local tbl = ${N.frame}.${N.R}[instr
|
|
2864
|
-
local valuesBase = instr
|
|
2998
|
+
local tbl = ${N.frame}.${N.R}[instr[2]]
|
|
2999
|
+
local valuesBase = instr[3]
|
|
2865
3000
|
local top = ${N.frame}.${N.multiTop} or (valuesBase + 1)
|
|
2866
|
-
local startIdx = instr
|
|
3001
|
+
local startIdx = instr[4]
|
|
2867
3002
|
local n = top - valuesBase
|
|
2868
3003
|
for i = 0, n - 1 do
|
|
2869
3004
|
tbl[startIdx + i] = ${N.frame}.${N.R}[valuesBase + i]
|
|
@@ -2937,10 +3072,6 @@ function generateVmNames(random = Math.random) {
|
|
|
2937
3072
|
varargs: id(),
|
|
2938
3073
|
nVarargs: id(),
|
|
2939
3074
|
multiTop: id(),
|
|
2940
|
-
op: id(),
|
|
2941
|
-
a: id(),
|
|
2942
|
-
b: id(),
|
|
2943
|
-
c: id(),
|
|
2944
3075
|
numParams: id(),
|
|
2945
3076
|
hasVarargs: id(),
|
|
2946
3077
|
maxRegs: id(),
|
|
@@ -2952,19 +3083,27 @@ function generateVmNames(random = Math.random) {
|
|
|
2952
3083
|
}
|
|
2953
3084
|
|
|
2954
3085
|
// src/passes/Vmify.ts
|
|
3086
|
+
function markRuntimeNumbersAsStructural(body) {
|
|
3087
|
+
transformExpressions({ body }, (expr) => {
|
|
3088
|
+
if (expr.type === "NumberLiteral") vmStructuralNumbers.add(expr);
|
|
3089
|
+
return void 0;
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
2955
3092
|
function runVmify(program, options) {
|
|
2956
|
-
const
|
|
3093
|
+
const builtinGlobals = options.builtinGlobals ?? DEFAULT_BUILTIN_GLOBALS;
|
|
2957
3094
|
const random = options.random ?? Math.random;
|
|
2958
3095
|
const opcodeMap = createOpcodeMap(random);
|
|
2959
3096
|
const names = generateVmNames(random);
|
|
2960
|
-
const compiler = new VmCompiler(program,
|
|
3097
|
+
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap);
|
|
2961
3098
|
const topProto = compiler.compile();
|
|
3099
|
+
const usedGlobalNames = compiler.getUsedGlobalNames();
|
|
2962
3100
|
const globalsTable = table(
|
|
2963
|
-
|
|
3101
|
+
usedGlobalNames.map((name) => namedField(name, identifier(name)))
|
|
2964
3102
|
);
|
|
2965
3103
|
const runtimeSource = buildVmRuntimeSource(names, opcodeMap);
|
|
2966
3104
|
const runtimeProgram = luauparser2.parse(runtimeSource);
|
|
2967
3105
|
const runtimeStatements = runtimeProgram.body.statements;
|
|
3106
|
+
markRuntimeNumbersAsStructural(runtimeProgram.body);
|
|
2968
3107
|
const protoLiteral = serializeProto(topProto, names);
|
|
2969
3108
|
const newBody = [
|
|
2970
3109
|
localStatement(names.globals, globalsTable),
|
|
@@ -2979,10 +3118,10 @@ function runVmify(program, options) {
|
|
|
2979
3118
|
var PASS_ORDER = [
|
|
2980
3119
|
"Vmify",
|
|
2981
3120
|
"GlobalMapping",
|
|
2982
|
-
"StringsToExpressions",
|
|
2983
|
-
"NumbersToExpressions",
|
|
2984
3121
|
"RenameVariables",
|
|
2985
3122
|
"ConstantArray",
|
|
3123
|
+
"StringsToExpressions",
|
|
3124
|
+
"NumbersToExpressions",
|
|
2986
3125
|
"EncryptStrings",
|
|
2987
3126
|
"EncryptNumbers",
|
|
2988
3127
|
"InsertJunk",
|