luau-obfuscator 1.0.3 → 1.0.5
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/.github/workflows/release.yml +56 -56
- package/dist/index.cjs +146 -67
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +146 -67
- package/generated/grammers.luau +3163 -3025
- package/generated/tests.luau +1826 -1835
- package/package.json +1 -1
- package/scripts/test.js +21 -21
- package/smoketest/grammers.luau +1174 -1174
- package/smoketest/tests.luau +114 -114
- package/src/passes/ConstantArray.ts +90 -90
- package/src/passes/EncryptNumbers.ts +122 -122
- package/src/passes/EncryptStrings.ts +162 -162
- package/src/passes/GlobalMapping.ts +193 -193
- package/src/passes/InsertJunk.ts +184 -184
- package/src/passes/Minify.ts +5 -5
- package/src/passes/StripTypes.ts +191 -191
- package/src/passes/Vmify.ts +11 -3
- package/src/passes/WrapInFunction.ts +31 -31
- package/src/passes/vmify/compiler.ts +72 -13
- package/src/passes/vmify/opcodes.ts +4 -0
- package/src/passes/vmify/runtime.ts +80 -51
- package/src/passes/vmify/serialize.ts +19 -13
- package/tsconfig.json +20 -20
- package/tsup.config.ts +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -60,6 +60,13 @@ interface VmifyOptions {
|
|
|
60
60
|
* Math.random. 재현 가능한 빌드가 필요하면(테스트 등) 시드 고정 PRNG를 넘길 것.
|
|
61
61
|
*/
|
|
62
62
|
random?: () => number;
|
|
63
|
+
/**
|
|
64
|
+
* 디버그 빌드. 켜면 각 인스트럭션에 원본 소스 줄 번호를, CALL에는 호출 대상 이름을
|
|
65
|
+
* 함께 직렬화하고, 런타임 CALL 핸들러가 non-callable 값을 부르려 할 때 그냥
|
|
66
|
+
* "attempt to call a nil value"가 아니라 "[vmdebug] ... '대상이름' (source line N)"으로
|
|
67
|
+
* 원본 위치를 찍어준다. 산출물이 조금 커지고 VM 시그니처가 노출되므로 진단용으로만 쓸 것.
|
|
68
|
+
*/
|
|
69
|
+
debug?: boolean;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
type ObfuscateConfigEach<C> = ({
|
package/dist/index.js
CHANGED
|
@@ -1738,9 +1738,10 @@ var DEFAULT_BUILTIN_GLOBALS = [
|
|
|
1738
1738
|
"next"
|
|
1739
1739
|
];
|
|
1740
1740
|
var VmCompiler = class {
|
|
1741
|
-
constructor(program, builtinGlobals = DEFAULT_BUILTIN_GLOBALS, opcodeMap) {
|
|
1741
|
+
constructor(program, builtinGlobals = DEFAULT_BUILTIN_GLOBALS, opcodeMap, debug = false) {
|
|
1742
1742
|
this.program = program;
|
|
1743
1743
|
this.opcodeMap = opcodeMap;
|
|
1744
|
+
this.debug = debug;
|
|
1744
1745
|
this.analysis = luauparser.analyzeScopes(program, { builtinGlobals });
|
|
1745
1746
|
this.enclosing = buildEnclosingFunctionMap(program);
|
|
1746
1747
|
for (const [id, binding] of this.analysis.bindings) {
|
|
@@ -1750,6 +1751,7 @@ var VmCompiler = class {
|
|
|
1750
1751
|
}
|
|
1751
1752
|
program;
|
|
1752
1753
|
opcodeMap;
|
|
1754
|
+
debug;
|
|
1753
1755
|
analysis;
|
|
1754
1756
|
enclosing;
|
|
1755
1757
|
captured = /* @__PURE__ */ new Set();
|
|
@@ -1808,9 +1810,39 @@ var VmCompiler = class {
|
|
|
1808
1810
|
}
|
|
1809
1811
|
emit(state, op, a, b, c, comment) {
|
|
1810
1812
|
const physicalOp = this.opcodeMap ? this.opcodeMap[op] : op;
|
|
1811
|
-
state.proto.code.push({ op: physicalOp, a, b, c, comment });
|
|
1813
|
+
state.proto.code.push({ op: physicalOp, a, b, c, comment, dbgLine: this.debug ? state.curLine : void 0 });
|
|
1812
1814
|
return state.proto.code.length - 1;
|
|
1813
1815
|
}
|
|
1816
|
+
/** debug 빌드: 방금 emit한 CALL 인스트럭션에 "호출 대상 표현식 문자열"과 그 소스 줄을
|
|
1817
|
+
* 박아넣는다(런타임이 non-callable을 잡았을 때 사람이 읽을 위치를 찍게). */
|
|
1818
|
+
stampCall(state, idx, callExpr, calleeExpr) {
|
|
1819
|
+
if (!this.debug) return;
|
|
1820
|
+
const instr = state.proto.code[idx];
|
|
1821
|
+
instr.dbgName = this.describeExpr(calleeExpr);
|
|
1822
|
+
const line = calleeExpr.line ?? callExpr.line;
|
|
1823
|
+
if (line) instr.dbgLine = line.start;
|
|
1824
|
+
}
|
|
1825
|
+
/** 표현식을 사람이 읽을 짧은 문자열로. 위치 추적용이라 완벽할 필요는 없음. */
|
|
1826
|
+
describeExpr(e) {
|
|
1827
|
+
switch (e.type) {
|
|
1828
|
+
case "Identifier":
|
|
1829
|
+
return e.name;
|
|
1830
|
+
case "MemberExpression":
|
|
1831
|
+
return `${this.describeExpr(e.object)}.${e.property.name}`;
|
|
1832
|
+
case "MethodCallExpression":
|
|
1833
|
+
return `${this.describeExpr(e.object)}:${e.method.name}(...)`;
|
|
1834
|
+
case "IndexExpression":
|
|
1835
|
+
return `${this.describeExpr(e.object)}[...]`;
|
|
1836
|
+
case "CallExpression":
|
|
1837
|
+
return `${this.describeExpr(e.callee)}(...)`;
|
|
1838
|
+
case "ParenthesizedExpression":
|
|
1839
|
+
return `(${this.describeExpr(e.expression)})`;
|
|
1840
|
+
case "StringLiteral":
|
|
1841
|
+
return `"${String(e.value).slice(0, 20)}"`;
|
|
1842
|
+
default:
|
|
1843
|
+
return `<${e.type}>`;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1814
1846
|
konst(state, value) {
|
|
1815
1847
|
return internConst(state.proto, value);
|
|
1816
1848
|
}
|
|
@@ -1902,6 +1934,7 @@ var VmCompiler = class {
|
|
|
1902
1934
|
state.regs.releaseLocalsTo(saved);
|
|
1903
1935
|
}
|
|
1904
1936
|
compileStatement(stmt, state) {
|
|
1937
|
+
if (this.debug && stmt.line) state.curLine = stmt.line.start;
|
|
1905
1938
|
switch (stmt.type) {
|
|
1906
1939
|
case "LocalStatement": {
|
|
1907
1940
|
const bindingIds = stmt.names.map((n) => this.bindingIdOfDecl(n));
|
|
@@ -1927,11 +1960,26 @@ var VmCompiler = class {
|
|
|
1927
1960
|
return;
|
|
1928
1961
|
}
|
|
1929
1962
|
case "AssignmentStatement": {
|
|
1930
|
-
const
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1963
|
+
const targets = stmt.targets;
|
|
1964
|
+
const values = stmt.values;
|
|
1965
|
+
const base = state.regs.top();
|
|
1966
|
+
if (values.length > 0) {
|
|
1967
|
+
for (let i = 0; i < values.length - 1; i++) {
|
|
1968
|
+
this.compileExprTo(values[i], state, base + i);
|
|
1969
|
+
}
|
|
1970
|
+
const lastIdx = values.length - 1;
|
|
1971
|
+
const remaining = Math.max(targets.length - lastIdx, 1);
|
|
1972
|
+
this.compileExprMultiInto(values[lastIdx], state, base + lastIdx, remaining);
|
|
1973
|
+
}
|
|
1974
|
+
const produced = values.length === 0 ? 0 : values.length - 1 + Math.max(targets.length - (values.length - 1), 1);
|
|
1975
|
+
for (let i = produced; i < targets.length; i++) {
|
|
1976
|
+
this.emit(state, 3 /* LOADNIL */, base + i, base + i, 0);
|
|
1977
|
+
}
|
|
1978
|
+
for (let i = 0; i < targets.length; i++) {
|
|
1979
|
+
state.regs.freeTemp(base + targets.length);
|
|
1980
|
+
this.compileAssignTarget(targets[i], base + i, state);
|
|
1981
|
+
}
|
|
1982
|
+
state.regs.freeTemp(base);
|
|
1935
1983
|
return;
|
|
1936
1984
|
}
|
|
1937
1985
|
case "CompoundAssignmentStatement":
|
|
@@ -2275,7 +2323,7 @@ var VmCompiler = class {
|
|
|
2275
2323
|
compileCallMultiInto(callExpr, state, wantCount) {
|
|
2276
2324
|
const base = this.compileExpr(callExpr.callee, state);
|
|
2277
2325
|
this.compileArgsContiguous(callExpr.arguments, state, base + 1);
|
|
2278
|
-
this.emit(state, 27 /* CALL */, base, callExpr.arguments.length + 1, wantCount + 1);
|
|
2326
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, callExpr.arguments.length + 1, wantCount + 1), callExpr, callExpr.callee);
|
|
2279
2327
|
return base;
|
|
2280
2328
|
}
|
|
2281
2329
|
loadConst(state, value) {
|
|
@@ -2326,7 +2374,7 @@ var VmCompiler = class {
|
|
|
2326
2374
|
if (expr.type === "CallExpression") {
|
|
2327
2375
|
const calleeReg = this.compileExpr(expr.callee, state);
|
|
2328
2376
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, calleeReg + 1);
|
|
2329
|
-
this.emit(state, 27 /* CALL */, calleeReg, b, wantCount + 1);
|
|
2377
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, calleeReg, b, wantCount + 1), expr, expr.callee);
|
|
2330
2378
|
for (let i = 0; i < wantCount; i++) {
|
|
2331
2379
|
if (calleeReg + i !== base + i) this.emit(state, 0 /* MOVE */, base + i, calleeReg + i, 0);
|
|
2332
2380
|
}
|
|
@@ -2341,7 +2389,7 @@ var VmCompiler = class {
|
|
|
2341
2389
|
state.regs.allocTemp();
|
|
2342
2390
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, fnSlot + 2);
|
|
2343
2391
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2344
|
-
this.emit(state, 27 /* CALL */, fnSlot, b, wantCount + 1);
|
|
2392
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, fnSlot, b, wantCount + 1), expr, expr);
|
|
2345
2393
|
for (let i = 0; i < wantCount; i++) {
|
|
2346
2394
|
if (fnSlot + i !== base + i) this.emit(state, 0 /* MOVE */, base + i, fnSlot + i, 0);
|
|
2347
2395
|
}
|
|
@@ -2367,7 +2415,7 @@ var VmCompiler = class {
|
|
|
2367
2415
|
if (calleeReg !== base) this.emit(state, 0 /* MOVE */, base, calleeReg, 0);
|
|
2368
2416
|
state.regs.freeTemp(base + 1);
|
|
2369
2417
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, base + 1);
|
|
2370
|
-
this.emit(state, 27 /* CALL */, base, b, 0);
|
|
2418
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, b, 0), expr, expr.callee);
|
|
2371
2419
|
return;
|
|
2372
2420
|
}
|
|
2373
2421
|
if (expr.type === "MethodCallExpression") {
|
|
@@ -2383,7 +2431,7 @@ var VmCompiler = class {
|
|
|
2383
2431
|
state.regs.freeTemp(base + 2);
|
|
2384
2432
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, base + 2);
|
|
2385
2433
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2386
|
-
this.emit(state, 27 /* CALL */, base, b, 0);
|
|
2434
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, b, 0), expr, expr);
|
|
2387
2435
|
return;
|
|
2388
2436
|
}
|
|
2389
2437
|
if (expr.type === "VarargExpression") {
|
|
@@ -2410,6 +2458,7 @@ var VmCompiler = class {
|
|
|
2410
2458
|
return args.length + 1;
|
|
2411
2459
|
}
|
|
2412
2460
|
compileExpr(expr, state, discard = false) {
|
|
2461
|
+
if (this.debug && expr.line) state.curLine = expr.line.start;
|
|
2413
2462
|
switch (expr.type) {
|
|
2414
2463
|
case "NilLiteral": {
|
|
2415
2464
|
const r = state.regs.allocTemp();
|
|
@@ -2567,7 +2616,8 @@ var VmCompiler = class {
|
|
|
2567
2616
|
case "CallExpression": {
|
|
2568
2617
|
const base = this.compileExpr(expr.callee, state);
|
|
2569
2618
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, base + 1);
|
|
2570
|
-
this.emit(state, 27 /* CALL */, base, b, discard ? 1 : 2);
|
|
2619
|
+
const callIdx = this.emit(state, 27 /* CALL */, base, b, discard ? 1 : 2);
|
|
2620
|
+
this.stampCall(state, callIdx, expr, expr.callee);
|
|
2571
2621
|
state.regs.freeTemp(base + (discard ? 0 : 1));
|
|
2572
2622
|
return base;
|
|
2573
2623
|
}
|
|
@@ -2579,7 +2629,8 @@ var VmCompiler = class {
|
|
|
2579
2629
|
state.regs.allocTemp();
|
|
2580
2630
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, fnSlot + 2);
|
|
2581
2631
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2582
|
-
this.emit(state, 27 /* CALL */, fnSlot, b, discard ? 1 : 2);
|
|
2632
|
+
const callIdx = this.emit(state, 27 /* CALL */, fnSlot, b, discard ? 1 : 2);
|
|
2633
|
+
this.stampCall(state, callIdx, expr, expr);
|
|
2583
2634
|
state.regs.freeTemp(fnSlot + (discard ? 0 : 1));
|
|
2584
2635
|
return fnSlot;
|
|
2585
2636
|
}
|
|
@@ -2692,15 +2743,20 @@ function serializeConstValue(v) {
|
|
|
2692
2743
|
if (typeof v === "number") return numberLiteral(v);
|
|
2693
2744
|
return booleanLiteral(v);
|
|
2694
2745
|
}
|
|
2695
|
-
function serializeInstrs(proto) {
|
|
2696
|
-
const fields = proto.code.map((instr) =>
|
|
2697
|
-
|
|
2746
|
+
function serializeInstrs(proto, debug) {
|
|
2747
|
+
const fields = proto.code.map((instr) => {
|
|
2748
|
+
const tuple = [
|
|
2698
2749
|
positionalField(vmNumberLiteral(instr.op)),
|
|
2699
2750
|
positionalField(vmNumberLiteral(instr.a)),
|
|
2700
2751
|
positionalField(vmNumberLiteral(instr.b)),
|
|
2701
2752
|
positionalField(vmNumberLiteral(instr.c))
|
|
2702
|
-
]
|
|
2703
|
-
|
|
2753
|
+
];
|
|
2754
|
+
if (debug) {
|
|
2755
|
+
tuple.push(positionalField(numberLiteral(instr.dbgLine ?? 0)));
|
|
2756
|
+
tuple.push(positionalField(stringLiteral(instr.dbgName ?? "")));
|
|
2757
|
+
}
|
|
2758
|
+
return positionalField(table(tuple));
|
|
2759
|
+
});
|
|
2704
2760
|
return table(fields);
|
|
2705
2761
|
}
|
|
2706
2762
|
function serializeUpvalDescs(proto, names) {
|
|
@@ -2712,29 +2768,41 @@ function serializeUpvalDescs(proto, names) {
|
|
|
2712
2768
|
));
|
|
2713
2769
|
return table(fields);
|
|
2714
2770
|
}
|
|
2715
|
-
function serializeProto(proto, names) {
|
|
2771
|
+
function serializeProto(proto, names, debug = false) {
|
|
2716
2772
|
const fields = [
|
|
2717
2773
|
namedField(names.numParams, vmNumberLiteral(proto.numParams)),
|
|
2718
2774
|
namedField(names.hasVarargs, booleanLiteral(proto.hasVarargs)),
|
|
2719
2775
|
namedField(names.maxRegs, vmNumberLiteral(proto.maxRegs)),
|
|
2720
|
-
namedField(names.code, serializeInstrs(proto)),
|
|
2776
|
+
namedField(names.code, serializeInstrs(proto, debug)),
|
|
2721
2777
|
namedField(names.consts, serializeConsts(proto.consts)),
|
|
2722
2778
|
namedField(names.upvalDescs, serializeUpvalDescs(proto, names)),
|
|
2723
|
-
namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names)))))
|
|
2779
|
+
namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names, debug)))))
|
|
2724
2780
|
];
|
|
2725
2781
|
return table(fields);
|
|
2726
2782
|
}
|
|
2727
2783
|
|
|
2728
2784
|
// src/passes/vmify/runtime.ts
|
|
2729
|
-
function buildVmRuntimeSource(names, opcodeMap) {
|
|
2785
|
+
function buildVmRuntimeSource(names, opcodeMap, debug = false) {
|
|
2730
2786
|
const N = names;
|
|
2731
2787
|
const op = (o) => opcodeMap[o];
|
|
2732
2788
|
return `
|
|
2733
2789
|
local ${N.handlers} = {}
|
|
2734
2790
|
|
|
2791
|
+
-- \uAC01 \uD578\uB4E4\uB7EC\uB294 "\uB2E4\uC74C pc(\uC22B\uC790)"\uB97C \uBC18\uD658\uD55C\uB2E4. RETURN \uD578\uB4E4\uB7EC\uB9CC table.pack \uACB0\uACFC(\uD14C\uC774\uBE14)\uB97C
|
|
2792
|
+
-- \uBC18\uD658\uD558\uBA70, \uADF8\uAC8C \uD568\uC218 \uC2E4\uD589 \uC885\uB8CC \uC2E0\uD638\uB2E4. Luau\uB294 proper tail call\uC744 \uD558\uC9C0 \uC54A\uC73C\uBBC0\uB85C
|
|
2793
|
+
-- \uD578\uB4E4\uB7EC\uB4E4\uC774 \uC11C\uB85C \uAF2C\uB9AC \uD638\uCD9C\uB85C \uC774\uC5B4\uC9C0\uBA74 \uBA85\uB839\uC5B4\uB2F9 \uC2A4\uD0DD \uD504\uB808\uC784\uC774 \uC313\uC5EC \uD130\uC9C4\uB2E4 \u2014 \uADF8\uB798\uC11C
|
|
2794
|
+
-- \uC5EC\uAE30\uC11C while \uB8E8\uD504\uB85C \uB3CC\uB824 \uC2A4\uD0DD \uAE4A\uC774\uB97C \uD568\uC218 \uC2E4\uD589 \uB0B4\uB0B4 \uC77C\uC815\uD558\uAC8C \uC720\uC9C0\uD55C\uB2E4.
|
|
2735
2795
|
local function ${N.dispatch}(${N.frame}, pc)
|
|
2736
|
-
local
|
|
2737
|
-
|
|
2796
|
+
local ${N.handlers} = ${N.handlers}
|
|
2797
|
+
while true do
|
|
2798
|
+
local instr = ${N.frame}.${N.code}[pc]
|
|
2799
|
+
local next = ${N.handlers}[instr[1]](${N.frame}, pc, instr)
|
|
2800
|
+
if type(next) == "number" then
|
|
2801
|
+
pc = next
|
|
2802
|
+
else
|
|
2803
|
+
return table.unpack(next, 1, next.n)
|
|
2804
|
+
end
|
|
2805
|
+
end
|
|
2738
2806
|
end
|
|
2739
2807
|
|
|
2740
2808
|
local function ${N.rk}(${N.frame}, x)
|
|
@@ -2748,67 +2816,67 @@ end
|
|
|
2748
2816
|
-- MOVE
|
|
2749
2817
|
${N.handlers}[${op(0 /* MOVE */)}] = function(${N.frame}, pc, instr)
|
|
2750
2818
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]]
|
|
2751
|
-
return
|
|
2819
|
+
return pc + 1
|
|
2752
2820
|
end
|
|
2753
2821
|
|
|
2754
2822
|
-- LOADK
|
|
2755
2823
|
${N.handlers}[${op(1 /* LOADK */)}] = function(${N.frame}, pc, instr)
|
|
2756
2824
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.K}[instr[3] + 1]
|
|
2757
|
-
return
|
|
2825
|
+
return pc + 1
|
|
2758
2826
|
end
|
|
2759
2827
|
|
|
2760
2828
|
-- LOADBOOL
|
|
2761
2829
|
${N.handlers}[${op(2 /* LOADBOOL */)}] = function(${N.frame}, pc, instr)
|
|
2762
2830
|
${N.frame}.${N.R}[instr[2]] = (instr[3] ~= 0)
|
|
2763
|
-
return
|
|
2831
|
+
return pc + 1
|
|
2764
2832
|
end
|
|
2765
2833
|
|
|
2766
2834
|
-- LOADNIL
|
|
2767
2835
|
${N.handlers}[${op(3 /* LOADNIL */)}] = function(${N.frame}, pc, instr)
|
|
2768
2836
|
for i = instr[2], instr[3] do ${N.frame}.${N.R}[i] = nil end
|
|
2769
|
-
return
|
|
2837
|
+
return pc + 1
|
|
2770
2838
|
end
|
|
2771
2839
|
|
|
2772
2840
|
-- GETUPVAL
|
|
2773
2841
|
${N.handlers}[${op(4 /* GETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2774
2842
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.upvals}[instr[3] + 1]
|
|
2775
|
-
return
|
|
2843
|
+
return pc + 1
|
|
2776
2844
|
end
|
|
2777
2845
|
|
|
2778
2846
|
-- SETUPVAL
|
|
2779
2847
|
${N.handlers}[${op(5 /* SETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2780
2848
|
${N.frame}.${N.upvals}[instr[3] + 1] = ${N.frame}.${N.R}[instr[2]]
|
|
2781
|
-
return
|
|
2849
|
+
return pc + 1
|
|
2782
2850
|
end
|
|
2783
2851
|
|
|
2784
2852
|
-- GETGLOBAL (b\uB294 RK\uAC00 \uC544\uB2C8\uB77C \uC0C1\uC218\uD480 \uC21C\uC218 \uC778\uB371\uC2A4)
|
|
2785
2853
|
${N.handlers}[${op(6 /* GETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2786
2854
|
${N.frame}.${N.R}[instr[2]] = ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]]
|
|
2787
|
-
return
|
|
2855
|
+
return pc + 1
|
|
2788
2856
|
end
|
|
2789
2857
|
|
|
2790
2858
|
-- SETGLOBAL
|
|
2791
2859
|
${N.handlers}[${op(7 /* SETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2792
2860
|
${N.globals}[${N.frame}.${N.K}[instr[3] + 1]] = ${N.frame}.${N.R}[instr[2]]
|
|
2793
|
-
return
|
|
2861
|
+
return pc + 1
|
|
2794
2862
|
end
|
|
2795
2863
|
|
|
2796
2864
|
-- GETTABLE
|
|
2797
2865
|
${N.handlers}[${op(8 /* GETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2798
2866
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]][${N.rk}(${N.frame}, instr[4])]
|
|
2799
|
-
return
|
|
2867
|
+
return pc + 1
|
|
2800
2868
|
end
|
|
2801
2869
|
|
|
2802
2870
|
-- SETTABLE
|
|
2803
2871
|
${N.handlers}[${op(9 /* SETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2804
2872
|
${N.frame}.${N.R}[instr[2]][${N.rk}(${N.frame}, instr[3])] = ${N.rk}(${N.frame}, instr[4])
|
|
2805
|
-
return
|
|
2873
|
+
return pc + 1
|
|
2806
2874
|
end
|
|
2807
2875
|
|
|
2808
2876
|
-- NEWTABLE
|
|
2809
2877
|
${N.handlers}[${op(10 /* NEWTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2810
2878
|
${N.frame}.${N.R}[instr[2]] = {}
|
|
2811
|
-
return
|
|
2879
|
+
return pc + 1
|
|
2812
2880
|
end
|
|
2813
2881
|
|
|
2814
2882
|
-- SELF (R[a] = R[b][key]; R[a+1] = R[b])
|
|
@@ -2816,90 +2884,90 @@ ${N.handlers}[${op(11 /* SELF */)}] = function(${N.frame}, pc, instr)
|
|
|
2816
2884
|
local obj = ${N.frame}.${N.R}[instr[3]]
|
|
2817
2885
|
${N.frame}.${N.R}[instr[2]] = obj[${N.rk}(${N.frame}, instr[4])]
|
|
2818
2886
|
${N.frame}.${N.R}[instr[2] + 1] = obj
|
|
2819
|
-
return
|
|
2887
|
+
return pc + 1
|
|
2820
2888
|
end
|
|
2821
2889
|
|
|
2822
2890
|
-- ADD SUB MUL DIV MOD POW
|
|
2823
2891
|
${N.handlers}[${op(12 /* ADD */)}] = function(${N.frame}, pc, instr)
|
|
2824
2892
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) + ${N.rk}(${N.frame}, instr[4])
|
|
2825
|
-
return
|
|
2893
|
+
return pc + 1
|
|
2826
2894
|
end
|
|
2827
2895
|
${N.handlers}[${op(13 /* SUB */)}] = function(${N.frame}, pc, instr)
|
|
2828
2896
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) - ${N.rk}(${N.frame}, instr[4])
|
|
2829
|
-
return
|
|
2897
|
+
return pc + 1
|
|
2830
2898
|
end
|
|
2831
2899
|
${N.handlers}[${op(14 /* MUL */)}] = function(${N.frame}, pc, instr)
|
|
2832
2900
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) * ${N.rk}(${N.frame}, instr[4])
|
|
2833
|
-
return
|
|
2901
|
+
return pc + 1
|
|
2834
2902
|
end
|
|
2835
2903
|
${N.handlers}[${op(15 /* DIV */)}] = function(${N.frame}, pc, instr)
|
|
2836
2904
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) / ${N.rk}(${N.frame}, instr[4])
|
|
2837
|
-
return
|
|
2905
|
+
return pc + 1
|
|
2838
2906
|
end
|
|
2839
2907
|
${N.handlers}[${op(16 /* MOD */)}] = function(${N.frame}, pc, instr)
|
|
2840
2908
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) % ${N.rk}(${N.frame}, instr[4])
|
|
2841
|
-
return
|
|
2909
|
+
return pc + 1
|
|
2842
2910
|
end
|
|
2843
2911
|
${N.handlers}[${op(17 /* POW */)}] = function(${N.frame}, pc, instr)
|
|
2844
2912
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) ^ ${N.rk}(${N.frame}, instr[4])
|
|
2845
|
-
return
|
|
2913
|
+
return pc + 1
|
|
2846
2914
|
end
|
|
2847
2915
|
|
|
2848
2916
|
-- IDIV (Luau \uBC14\uB2E5 \uB098\uB217\uC148)
|
|
2849
2917
|
${N.handlers}[${op(34 /* IDIV */)}] = function(${N.frame}, pc, instr)
|
|
2850
2918
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) // ${N.rk}(${N.frame}, instr[4])
|
|
2851
|
-
return
|
|
2919
|
+
return pc + 1
|
|
2852
2920
|
end
|
|
2853
2921
|
|
|
2854
2922
|
-- CONCAT
|
|
2855
2923
|
${N.handlers}[${op(18 /* CONCAT */)}] = function(${N.frame}, pc, instr)
|
|
2856
2924
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) .. ${N.rk}(${N.frame}, instr[4])
|
|
2857
|
-
return
|
|
2925
|
+
return pc + 1
|
|
2858
2926
|
end
|
|
2859
2927
|
|
|
2860
2928
|
-- UNM
|
|
2861
2929
|
${N.handlers}[${op(19 /* UNM */)}] = function(${N.frame}, pc, instr)
|
|
2862
2930
|
${N.frame}.${N.R}[instr[2]] = -${N.frame}.${N.R}[instr[3]]
|
|
2863
|
-
return
|
|
2931
|
+
return pc + 1
|
|
2864
2932
|
end
|
|
2865
2933
|
|
|
2866
2934
|
-- NOT
|
|
2867
2935
|
${N.handlers}[${op(20 /* NOT */)}] = function(${N.frame}, pc, instr)
|
|
2868
2936
|
${N.frame}.${N.R}[instr[2]] = not ${N.frame}.${N.R}[instr[3]]
|
|
2869
|
-
return
|
|
2937
|
+
return pc + 1
|
|
2870
2938
|
end
|
|
2871
2939
|
|
|
2872
2940
|
-- LEN
|
|
2873
2941
|
${N.handlers}[${op(21 /* LEN */)}] = function(${N.frame}, pc, instr)
|
|
2874
2942
|
${N.frame}.${N.R}[instr[2]] = #${N.frame}.${N.R}[instr[3]]
|
|
2875
|
-
return
|
|
2943
|
+
return pc + 1
|
|
2876
2944
|
end
|
|
2877
2945
|
|
|
2878
2946
|
-- JMP (pc += a)
|
|
2879
2947
|
${N.handlers}[${op(22 /* JMP */)}] = function(${N.frame}, pc, instr)
|
|
2880
|
-
return
|
|
2948
|
+
return pc + instr[2]
|
|
2881
2949
|
end
|
|
2882
2950
|
|
|
2883
2951
|
-- EQ LT LE (\uAC12 \uC0DD\uC131\uD615 \uBE44\uAD50)
|
|
2884
2952
|
${N.handlers}[${op(23 /* EQ */)}] = function(${N.frame}, pc, instr)
|
|
2885
2953
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) == ${N.rk}(${N.frame}, instr[4])
|
|
2886
|
-
return
|
|
2954
|
+
return pc + 1
|
|
2887
2955
|
end
|
|
2888
2956
|
${N.handlers}[${op(24 /* LT */)}] = function(${N.frame}, pc, instr)
|
|
2889
2957
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) < ${N.rk}(${N.frame}, instr[4])
|
|
2890
|
-
return
|
|
2958
|
+
return pc + 1
|
|
2891
2959
|
end
|
|
2892
2960
|
${N.handlers}[${op(25 /* LE */)}] = function(${N.frame}, pc, instr)
|
|
2893
2961
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) <= ${N.rk}(${N.frame}, instr[4])
|
|
2894
|
-
return
|
|
2962
|
+
return pc + 1
|
|
2895
2963
|
end
|
|
2896
2964
|
|
|
2897
2965
|
-- TEST (if (not R[a]) != (c!=0) then pc += 2 else pc += 1 \u2014 \uBCF4\uD1B5 \uB2E4\uC74C \uBA85\uB839\uC740 JMP)
|
|
2898
2966
|
${N.handlers}[${op(26 /* TEST */)}] = function(${N.frame}, pc, instr)
|
|
2899
2967
|
if (not ${N.frame}.${N.R}[instr[2]]) ~= (instr[4] ~= 0) then
|
|
2900
|
-
return
|
|
2968
|
+
return pc + 2
|
|
2901
2969
|
end
|
|
2902
|
-
return
|
|
2970
|
+
return pc + 1
|
|
2903
2971
|
end
|
|
2904
2972
|
|
|
2905
2973
|
-- CALL
|
|
@@ -2914,7 +2982,13 @@ ${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
|
2914
2982
|
end
|
|
2915
2983
|
local args = table.create(nargs)
|
|
2916
2984
|
for i = 1, nargs do args[i] = ${N.frame}.${N.R}[a + i] end
|
|
2917
|
-
|
|
2985
|
+
${debug ? ` if type(fn) ~= "function" then
|
|
2986
|
+
local mt = getmetatable(fn)
|
|
2987
|
+
if not (mt and mt.__call) then
|
|
2988
|
+
error(("[vmdebug] attempt to call a %s value: '%s' (source line %d)"):format(type(fn), tostring(instr[6]), tonumber(instr[5]) or -1), 0)
|
|
2989
|
+
end
|
|
2990
|
+
end
|
|
2991
|
+
` : ""} local rets = table.pack(fn(table.unpack(args, 1, nargs)))
|
|
2918
2992
|
if c == 1 then
|
|
2919
2993
|
-- discard
|
|
2920
2994
|
elseif c == 0 then
|
|
@@ -2924,23 +2998,27 @@ ${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
|
2924
2998
|
local want = c - 1
|
|
2925
2999
|
for i = 1, want do ${N.frame}.${N.R}[a + i - 1] = rets[i] end
|
|
2926
3000
|
end
|
|
2927
|
-
return
|
|
3001
|
+
return pc + 1
|
|
2928
3002
|
end
|
|
2929
3003
|
|
|
2930
3004
|
-- RETURN
|
|
3005
|
+
-- \uB2E4\uB978 \uD578\uB4E4\uB7EC\uB294 "\uB2E4\uC74C pc(\uC22B\uC790)"\uB97C \uBC18\uD658\uD558\uC9C0\uB9CC, RETURN\uB9CC\uC740 \uACB0\uACFC\uAC12\uC744 table.pack\uC73C\uB85C \uC2F8\uC11C
|
|
3006
|
+
-- \uD14C\uC774\uBE14\uC744 \uBC18\uD658\uD55C\uB2E4 \u2014 dispatch \uB8E8\uD504\uB294 \uBC18\uD658\uAC12\uC774 \uC22B\uC790\uAC00 \uC544\uB2C8\uBA74 \uC774\uAC78 \uD568\uC218 \uC885\uB8CC \uC2E0\uD638\uB85C \uBCF4\uACE0
|
|
3007
|
+
-- table.unpack(next, 1, next.n)\uC73C\uB85C \uD480\uC5B4 \uCD5C\uC885 \uBC18\uD658\uD55C\uB2E4. table.pack\uC774 n\uC744 \uCC44\uC6CC \uC8FC\uBBC0\uB85C
|
|
3008
|
+
-- \uC911\uAC04 nil\uC774 \uC788\uC5B4\uB3C4 \uBC18\uD658 \uAC1C\uC218\uAC00 \uC815\uD655\uD788 \uBCF4\uC874\uB41C\uB2E4.
|
|
2931
3009
|
${N.handlers}[${op(28 /* RETURN */)}] = function(${N.frame}, pc, instr)
|
|
2932
3010
|
local a, b = instr[2], instr[3]
|
|
2933
3011
|
if b == 0 then
|
|
2934
|
-
return table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1)
|
|
3012
|
+
return table.pack(table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1))
|
|
2935
3013
|
end
|
|
2936
|
-
return table.unpack(${N.frame}.${N.R}, a, a + b - 2)
|
|
3014
|
+
return table.pack(table.unpack(${N.frame}.${N.R}, a, a + b - 2))
|
|
2937
3015
|
end
|
|
2938
3016
|
|
|
2939
3017
|
-- FORPREP
|
|
2940
3018
|
${N.handlers}[${op(29 /* FORPREP */)}] = function(${N.frame}, pc, instr)
|
|
2941
3019
|
local a = instr[2]
|
|
2942
3020
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] - ${N.frame}.${N.R}[a + 2]
|
|
2943
|
-
return
|
|
3021
|
+
return pc + instr[3]
|
|
2944
3022
|
end
|
|
2945
3023
|
|
|
2946
3024
|
-- FORLOOP
|
|
@@ -2956,9 +3034,9 @@ ${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
|
2956
3034
|
end
|
|
2957
3035
|
if ok then
|
|
2958
3036
|
${N.frame}.${N.R}[a + 3] = ${N.frame}.${N.R}[a]
|
|
2959
|
-
return
|
|
3037
|
+
return pc + instr[3]
|
|
2960
3038
|
end
|
|
2961
|
-
return
|
|
3039
|
+
return pc + 1
|
|
2962
3040
|
end
|
|
2963
3041
|
|
|
2964
3042
|
-- CLOSURE
|
|
@@ -2978,7 +3056,7 @@ ${N.handlers}[${op(31 /* CLOSURE */)}] = function(${N.frame}, pc, instr)
|
|
|
2978
3056
|
${N.frame}.${N.R}[instr[2]] = function(...)
|
|
2979
3057
|
return ${N.execute}(childProto, capturedUpvals, ...)
|
|
2980
3058
|
end
|
|
2981
|
-
return
|
|
3059
|
+
return pc + 1 + n
|
|
2982
3060
|
end
|
|
2983
3061
|
|
|
2984
3062
|
-- VARARG
|
|
@@ -2990,7 +3068,7 @@ ${N.handlers}[${op(32 /* VARARG */)}] = function(${N.frame}, pc, instr)
|
|
|
2990
3068
|
if instr[3] == 0 then
|
|
2991
3069
|
${N.frame}.${N.multiTop} = instr[2] + count
|
|
2992
3070
|
end
|
|
2993
|
-
return
|
|
3071
|
+
return pc + 1
|
|
2994
3072
|
end
|
|
2995
3073
|
|
|
2996
3074
|
-- SETLIST
|
|
@@ -3003,7 +3081,7 @@ ${N.handlers}[${op(33 /* SETLIST */)}] = function(${N.frame}, pc, instr)
|
|
|
3003
3081
|
for i = 0, n - 1 do
|
|
3004
3082
|
tbl[startIdx + i] = ${N.frame}.${N.R}[valuesBase + i]
|
|
3005
3083
|
end
|
|
3006
|
-
return
|
|
3084
|
+
return pc + 1
|
|
3007
3085
|
end
|
|
3008
3086
|
|
|
3009
3087
|
function ${N.execute}(proto, upvals, ...)
|
|
@@ -3092,19 +3170,20 @@ function markRuntimeNumbersAsStructural(body) {
|
|
|
3092
3170
|
function runVmify(program, options) {
|
|
3093
3171
|
const builtinGlobals = options.builtinGlobals ?? DEFAULT_BUILTIN_GLOBALS;
|
|
3094
3172
|
const random = options.random ?? Math.random;
|
|
3173
|
+
const debug = options.debug ?? false;
|
|
3095
3174
|
const opcodeMap = createOpcodeMap(random);
|
|
3096
3175
|
const names = generateVmNames(random);
|
|
3097
|
-
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap);
|
|
3176
|
+
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap, debug);
|
|
3098
3177
|
const topProto = compiler.compile();
|
|
3099
3178
|
const usedGlobalNames = compiler.getUsedGlobalNames();
|
|
3100
3179
|
const globalsTable = table(
|
|
3101
3180
|
usedGlobalNames.map((name) => namedField(name, identifier(name)))
|
|
3102
3181
|
);
|
|
3103
|
-
const runtimeSource = buildVmRuntimeSource(names, opcodeMap);
|
|
3182
|
+
const runtimeSource = buildVmRuntimeSource(names, opcodeMap, debug);
|
|
3104
3183
|
const runtimeProgram = luauparser2.parse(runtimeSource);
|
|
3105
3184
|
const runtimeStatements = runtimeProgram.body.statements;
|
|
3106
3185
|
markRuntimeNumbersAsStructural(runtimeProgram.body);
|
|
3107
|
-
const protoLiteral = serializeProto(topProto, names);
|
|
3186
|
+
const protoLiteral = serializeProto(topProto, names, debug);
|
|
3108
3187
|
const newBody = [
|
|
3109
3188
|
localStatement(names.globals, globalsTable),
|
|
3110
3189
|
...runtimeStatements,
|