luau-obfuscator 1.0.3 → 1.0.4
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 +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 +3480 -3309
- package/generated/tests.luau +1868 -1784
- package/package.json +1 -1
- package/src/passes/Vmify.ts +11 -3
- 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/dist/index.cjs
CHANGED
|
@@ -1766,9 +1766,10 @@ var DEFAULT_BUILTIN_GLOBALS = [
|
|
|
1766
1766
|
"next"
|
|
1767
1767
|
];
|
|
1768
1768
|
var VmCompiler = class {
|
|
1769
|
-
constructor(program, builtinGlobals = DEFAULT_BUILTIN_GLOBALS, opcodeMap) {
|
|
1769
|
+
constructor(program, builtinGlobals = DEFAULT_BUILTIN_GLOBALS, opcodeMap, debug = false) {
|
|
1770
1770
|
this.program = program;
|
|
1771
1771
|
this.opcodeMap = opcodeMap;
|
|
1772
|
+
this.debug = debug;
|
|
1772
1773
|
this.analysis = import_luau_parser3.luauparser.analyzeScopes(program, { builtinGlobals });
|
|
1773
1774
|
this.enclosing = buildEnclosingFunctionMap(program);
|
|
1774
1775
|
for (const [id, binding] of this.analysis.bindings) {
|
|
@@ -1778,6 +1779,7 @@ var VmCompiler = class {
|
|
|
1778
1779
|
}
|
|
1779
1780
|
program;
|
|
1780
1781
|
opcodeMap;
|
|
1782
|
+
debug;
|
|
1781
1783
|
analysis;
|
|
1782
1784
|
enclosing;
|
|
1783
1785
|
captured = /* @__PURE__ */ new Set();
|
|
@@ -1836,9 +1838,39 @@ var VmCompiler = class {
|
|
|
1836
1838
|
}
|
|
1837
1839
|
emit(state, op, a, b, c, comment) {
|
|
1838
1840
|
const physicalOp = this.opcodeMap ? this.opcodeMap[op] : op;
|
|
1839
|
-
state.proto.code.push({ op: physicalOp, a, b, c, comment });
|
|
1841
|
+
state.proto.code.push({ op: physicalOp, a, b, c, comment, dbgLine: this.debug ? state.curLine : void 0 });
|
|
1840
1842
|
return state.proto.code.length - 1;
|
|
1841
1843
|
}
|
|
1844
|
+
/** debug 빌드: 방금 emit한 CALL 인스트럭션에 "호출 대상 표현식 문자열"과 그 소스 줄을
|
|
1845
|
+
* 박아넣는다(런타임이 non-callable을 잡았을 때 사람이 읽을 위치를 찍게). */
|
|
1846
|
+
stampCall(state, idx, callExpr, calleeExpr) {
|
|
1847
|
+
if (!this.debug) return;
|
|
1848
|
+
const instr = state.proto.code[idx];
|
|
1849
|
+
instr.dbgName = this.describeExpr(calleeExpr);
|
|
1850
|
+
const line = calleeExpr.line ?? callExpr.line;
|
|
1851
|
+
if (line) instr.dbgLine = line.start;
|
|
1852
|
+
}
|
|
1853
|
+
/** 표현식을 사람이 읽을 짧은 문자열로. 위치 추적용이라 완벽할 필요는 없음. */
|
|
1854
|
+
describeExpr(e) {
|
|
1855
|
+
switch (e.type) {
|
|
1856
|
+
case "Identifier":
|
|
1857
|
+
return e.name;
|
|
1858
|
+
case "MemberExpression":
|
|
1859
|
+
return `${this.describeExpr(e.object)}.${e.property.name}`;
|
|
1860
|
+
case "MethodCallExpression":
|
|
1861
|
+
return `${this.describeExpr(e.object)}:${e.method.name}(...)`;
|
|
1862
|
+
case "IndexExpression":
|
|
1863
|
+
return `${this.describeExpr(e.object)}[...]`;
|
|
1864
|
+
case "CallExpression":
|
|
1865
|
+
return `${this.describeExpr(e.callee)}(...)`;
|
|
1866
|
+
case "ParenthesizedExpression":
|
|
1867
|
+
return `(${this.describeExpr(e.expression)})`;
|
|
1868
|
+
case "StringLiteral":
|
|
1869
|
+
return `"${String(e.value).slice(0, 20)}"`;
|
|
1870
|
+
default:
|
|
1871
|
+
return `<${e.type}>`;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1842
1874
|
konst(state, value) {
|
|
1843
1875
|
return internConst(state.proto, value);
|
|
1844
1876
|
}
|
|
@@ -1930,6 +1962,7 @@ var VmCompiler = class {
|
|
|
1930
1962
|
state.regs.releaseLocalsTo(saved);
|
|
1931
1963
|
}
|
|
1932
1964
|
compileStatement(stmt, state) {
|
|
1965
|
+
if (this.debug && stmt.line) state.curLine = stmt.line.start;
|
|
1933
1966
|
switch (stmt.type) {
|
|
1934
1967
|
case "LocalStatement": {
|
|
1935
1968
|
const bindingIds = stmt.names.map((n) => this.bindingIdOfDecl(n));
|
|
@@ -1955,11 +1988,26 @@ var VmCompiler = class {
|
|
|
1955
1988
|
return;
|
|
1956
1989
|
}
|
|
1957
1990
|
case "AssignmentStatement": {
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1991
|
+
const targets = stmt.targets;
|
|
1992
|
+
const values = stmt.values;
|
|
1993
|
+
const base = state.regs.top();
|
|
1994
|
+
if (values.length > 0) {
|
|
1995
|
+
for (let i = 0; i < values.length - 1; i++) {
|
|
1996
|
+
this.compileExprTo(values[i], state, base + i);
|
|
1997
|
+
}
|
|
1998
|
+
const lastIdx = values.length - 1;
|
|
1999
|
+
const remaining = Math.max(targets.length - lastIdx, 1);
|
|
2000
|
+
this.compileExprMultiInto(values[lastIdx], state, base + lastIdx, remaining);
|
|
2001
|
+
}
|
|
2002
|
+
const produced = values.length === 0 ? 0 : values.length - 1 + Math.max(targets.length - (values.length - 1), 1);
|
|
2003
|
+
for (let i = produced; i < targets.length; i++) {
|
|
2004
|
+
this.emit(state, 3 /* LOADNIL */, base + i, base + i, 0);
|
|
2005
|
+
}
|
|
2006
|
+
for (let i = 0; i < targets.length; i++) {
|
|
2007
|
+
state.regs.freeTemp(base + targets.length);
|
|
2008
|
+
this.compileAssignTarget(targets[i], base + i, state);
|
|
2009
|
+
}
|
|
2010
|
+
state.regs.freeTemp(base);
|
|
1963
2011
|
return;
|
|
1964
2012
|
}
|
|
1965
2013
|
case "CompoundAssignmentStatement":
|
|
@@ -2303,7 +2351,7 @@ var VmCompiler = class {
|
|
|
2303
2351
|
compileCallMultiInto(callExpr, state, wantCount) {
|
|
2304
2352
|
const base = this.compileExpr(callExpr.callee, state);
|
|
2305
2353
|
this.compileArgsContiguous(callExpr.arguments, state, base + 1);
|
|
2306
|
-
this.emit(state, 27 /* CALL */, base, callExpr.arguments.length + 1, wantCount + 1);
|
|
2354
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, callExpr.arguments.length + 1, wantCount + 1), callExpr, callExpr.callee);
|
|
2307
2355
|
return base;
|
|
2308
2356
|
}
|
|
2309
2357
|
loadConst(state, value) {
|
|
@@ -2354,7 +2402,7 @@ var VmCompiler = class {
|
|
|
2354
2402
|
if (expr.type === "CallExpression") {
|
|
2355
2403
|
const calleeReg = this.compileExpr(expr.callee, state);
|
|
2356
2404
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, calleeReg + 1);
|
|
2357
|
-
this.emit(state, 27 /* CALL */, calleeReg, b, wantCount + 1);
|
|
2405
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, calleeReg, b, wantCount + 1), expr, expr.callee);
|
|
2358
2406
|
for (let i = 0; i < wantCount; i++) {
|
|
2359
2407
|
if (calleeReg + i !== base + i) this.emit(state, 0 /* MOVE */, base + i, calleeReg + i, 0);
|
|
2360
2408
|
}
|
|
@@ -2369,7 +2417,7 @@ var VmCompiler = class {
|
|
|
2369
2417
|
state.regs.allocTemp();
|
|
2370
2418
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, fnSlot + 2);
|
|
2371
2419
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2372
|
-
this.emit(state, 27 /* CALL */, fnSlot, b, wantCount + 1);
|
|
2420
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, fnSlot, b, wantCount + 1), expr, expr);
|
|
2373
2421
|
for (let i = 0; i < wantCount; i++) {
|
|
2374
2422
|
if (fnSlot + i !== base + i) this.emit(state, 0 /* MOVE */, base + i, fnSlot + i, 0);
|
|
2375
2423
|
}
|
|
@@ -2395,7 +2443,7 @@ var VmCompiler = class {
|
|
|
2395
2443
|
if (calleeReg !== base) this.emit(state, 0 /* MOVE */, base, calleeReg, 0);
|
|
2396
2444
|
state.regs.freeTemp(base + 1);
|
|
2397
2445
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, base + 1);
|
|
2398
|
-
this.emit(state, 27 /* CALL */, base, b, 0);
|
|
2446
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, b, 0), expr, expr.callee);
|
|
2399
2447
|
return;
|
|
2400
2448
|
}
|
|
2401
2449
|
if (expr.type === "MethodCallExpression") {
|
|
@@ -2411,7 +2459,7 @@ var VmCompiler = class {
|
|
|
2411
2459
|
state.regs.freeTemp(base + 2);
|
|
2412
2460
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, base + 2);
|
|
2413
2461
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2414
|
-
this.emit(state, 27 /* CALL */, base, b, 0);
|
|
2462
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, b, 0), expr, expr);
|
|
2415
2463
|
return;
|
|
2416
2464
|
}
|
|
2417
2465
|
if (expr.type === "VarargExpression") {
|
|
@@ -2438,6 +2486,7 @@ var VmCompiler = class {
|
|
|
2438
2486
|
return args.length + 1;
|
|
2439
2487
|
}
|
|
2440
2488
|
compileExpr(expr, state, discard = false) {
|
|
2489
|
+
if (this.debug && expr.line) state.curLine = expr.line.start;
|
|
2441
2490
|
switch (expr.type) {
|
|
2442
2491
|
case "NilLiteral": {
|
|
2443
2492
|
const r = state.regs.allocTemp();
|
|
@@ -2595,7 +2644,8 @@ var VmCompiler = class {
|
|
|
2595
2644
|
case "CallExpression": {
|
|
2596
2645
|
const base = this.compileExpr(expr.callee, state);
|
|
2597
2646
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, base + 1);
|
|
2598
|
-
this.emit(state, 27 /* CALL */, base, b, discard ? 1 : 2);
|
|
2647
|
+
const callIdx = this.emit(state, 27 /* CALL */, base, b, discard ? 1 : 2);
|
|
2648
|
+
this.stampCall(state, callIdx, expr, expr.callee);
|
|
2599
2649
|
state.regs.freeTemp(base + (discard ? 0 : 1));
|
|
2600
2650
|
return base;
|
|
2601
2651
|
}
|
|
@@ -2607,7 +2657,8 @@ var VmCompiler = class {
|
|
|
2607
2657
|
state.regs.allocTemp();
|
|
2608
2658
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, fnSlot + 2);
|
|
2609
2659
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2610
|
-
this.emit(state, 27 /* CALL */, fnSlot, b, discard ? 1 : 2);
|
|
2660
|
+
const callIdx = this.emit(state, 27 /* CALL */, fnSlot, b, discard ? 1 : 2);
|
|
2661
|
+
this.stampCall(state, callIdx, expr, expr);
|
|
2611
2662
|
state.regs.freeTemp(fnSlot + (discard ? 0 : 1));
|
|
2612
2663
|
return fnSlot;
|
|
2613
2664
|
}
|
|
@@ -2720,15 +2771,20 @@ function serializeConstValue(v) {
|
|
|
2720
2771
|
if (typeof v === "number") return numberLiteral(v);
|
|
2721
2772
|
return booleanLiteral(v);
|
|
2722
2773
|
}
|
|
2723
|
-
function serializeInstrs(proto) {
|
|
2724
|
-
const fields = proto.code.map((instr) =>
|
|
2725
|
-
|
|
2774
|
+
function serializeInstrs(proto, debug) {
|
|
2775
|
+
const fields = proto.code.map((instr) => {
|
|
2776
|
+
const tuple = [
|
|
2726
2777
|
positionalField(vmNumberLiteral(instr.op)),
|
|
2727
2778
|
positionalField(vmNumberLiteral(instr.a)),
|
|
2728
2779
|
positionalField(vmNumberLiteral(instr.b)),
|
|
2729
2780
|
positionalField(vmNumberLiteral(instr.c))
|
|
2730
|
-
]
|
|
2731
|
-
|
|
2781
|
+
];
|
|
2782
|
+
if (debug) {
|
|
2783
|
+
tuple.push(positionalField(numberLiteral(instr.dbgLine ?? 0)));
|
|
2784
|
+
tuple.push(positionalField(stringLiteral(instr.dbgName ?? "")));
|
|
2785
|
+
}
|
|
2786
|
+
return positionalField(table(tuple));
|
|
2787
|
+
});
|
|
2732
2788
|
return table(fields);
|
|
2733
2789
|
}
|
|
2734
2790
|
function serializeUpvalDescs(proto, names) {
|
|
@@ -2740,29 +2796,41 @@ function serializeUpvalDescs(proto, names) {
|
|
|
2740
2796
|
));
|
|
2741
2797
|
return table(fields);
|
|
2742
2798
|
}
|
|
2743
|
-
function serializeProto(proto, names) {
|
|
2799
|
+
function serializeProto(proto, names, debug = false) {
|
|
2744
2800
|
const fields = [
|
|
2745
2801
|
namedField(names.numParams, vmNumberLiteral(proto.numParams)),
|
|
2746
2802
|
namedField(names.hasVarargs, booleanLiteral(proto.hasVarargs)),
|
|
2747
2803
|
namedField(names.maxRegs, vmNumberLiteral(proto.maxRegs)),
|
|
2748
|
-
namedField(names.code, serializeInstrs(proto)),
|
|
2804
|
+
namedField(names.code, serializeInstrs(proto, debug)),
|
|
2749
2805
|
namedField(names.consts, serializeConsts(proto.consts)),
|
|
2750
2806
|
namedField(names.upvalDescs, serializeUpvalDescs(proto, names)),
|
|
2751
|
-
namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names)))))
|
|
2807
|
+
namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names, debug)))))
|
|
2752
2808
|
];
|
|
2753
2809
|
return table(fields);
|
|
2754
2810
|
}
|
|
2755
2811
|
|
|
2756
2812
|
// src/passes/vmify/runtime.ts
|
|
2757
|
-
function buildVmRuntimeSource(names, opcodeMap) {
|
|
2813
|
+
function buildVmRuntimeSource(names, opcodeMap, debug = false) {
|
|
2758
2814
|
const N = names;
|
|
2759
2815
|
const op = (o) => opcodeMap[o];
|
|
2760
2816
|
return `
|
|
2761
2817
|
local ${N.handlers} = {}
|
|
2762
2818
|
|
|
2819
|
+
-- \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
|
|
2820
|
+
-- \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
|
|
2821
|
+
-- \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
|
|
2822
|
+
-- \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.
|
|
2763
2823
|
local function ${N.dispatch}(${N.frame}, pc)
|
|
2764
|
-
local
|
|
2765
|
-
|
|
2824
|
+
local ${N.handlers} = ${N.handlers}
|
|
2825
|
+
while true do
|
|
2826
|
+
local instr = ${N.frame}.${N.code}[pc]
|
|
2827
|
+
local next = ${N.handlers}[instr[1]](${N.frame}, pc, instr)
|
|
2828
|
+
if type(next) == "number" then
|
|
2829
|
+
pc = next
|
|
2830
|
+
else
|
|
2831
|
+
return table.unpack(next, 1, next.n)
|
|
2832
|
+
end
|
|
2833
|
+
end
|
|
2766
2834
|
end
|
|
2767
2835
|
|
|
2768
2836
|
local function ${N.rk}(${N.frame}, x)
|
|
@@ -2776,67 +2844,67 @@ end
|
|
|
2776
2844
|
-- MOVE
|
|
2777
2845
|
${N.handlers}[${op(0 /* MOVE */)}] = function(${N.frame}, pc, instr)
|
|
2778
2846
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]]
|
|
2779
|
-
return
|
|
2847
|
+
return pc + 1
|
|
2780
2848
|
end
|
|
2781
2849
|
|
|
2782
2850
|
-- LOADK
|
|
2783
2851
|
${N.handlers}[${op(1 /* LOADK */)}] = function(${N.frame}, pc, instr)
|
|
2784
2852
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.K}[instr[3] + 1]
|
|
2785
|
-
return
|
|
2853
|
+
return pc + 1
|
|
2786
2854
|
end
|
|
2787
2855
|
|
|
2788
2856
|
-- LOADBOOL
|
|
2789
2857
|
${N.handlers}[${op(2 /* LOADBOOL */)}] = function(${N.frame}, pc, instr)
|
|
2790
2858
|
${N.frame}.${N.R}[instr[2]] = (instr[3] ~= 0)
|
|
2791
|
-
return
|
|
2859
|
+
return pc + 1
|
|
2792
2860
|
end
|
|
2793
2861
|
|
|
2794
2862
|
-- LOADNIL
|
|
2795
2863
|
${N.handlers}[${op(3 /* LOADNIL */)}] = function(${N.frame}, pc, instr)
|
|
2796
2864
|
for i = instr[2], instr[3] do ${N.frame}.${N.R}[i] = nil end
|
|
2797
|
-
return
|
|
2865
|
+
return pc + 1
|
|
2798
2866
|
end
|
|
2799
2867
|
|
|
2800
2868
|
-- GETUPVAL
|
|
2801
2869
|
${N.handlers}[${op(4 /* GETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2802
2870
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.upvals}[instr[3] + 1]
|
|
2803
|
-
return
|
|
2871
|
+
return pc + 1
|
|
2804
2872
|
end
|
|
2805
2873
|
|
|
2806
2874
|
-- SETUPVAL
|
|
2807
2875
|
${N.handlers}[${op(5 /* SETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2808
2876
|
${N.frame}.${N.upvals}[instr[3] + 1] = ${N.frame}.${N.R}[instr[2]]
|
|
2809
|
-
return
|
|
2877
|
+
return pc + 1
|
|
2810
2878
|
end
|
|
2811
2879
|
|
|
2812
2880
|
-- GETGLOBAL (b\uB294 RK\uAC00 \uC544\uB2C8\uB77C \uC0C1\uC218\uD480 \uC21C\uC218 \uC778\uB371\uC2A4)
|
|
2813
2881
|
${N.handlers}[${op(6 /* GETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2814
2882
|
${N.frame}.${N.R}[instr[2]] = ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]]
|
|
2815
|
-
return
|
|
2883
|
+
return pc + 1
|
|
2816
2884
|
end
|
|
2817
2885
|
|
|
2818
2886
|
-- SETGLOBAL
|
|
2819
2887
|
${N.handlers}[${op(7 /* SETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2820
2888
|
${N.globals}[${N.frame}.${N.K}[instr[3] + 1]] = ${N.frame}.${N.R}[instr[2]]
|
|
2821
|
-
return
|
|
2889
|
+
return pc + 1
|
|
2822
2890
|
end
|
|
2823
2891
|
|
|
2824
2892
|
-- GETTABLE
|
|
2825
2893
|
${N.handlers}[${op(8 /* GETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2826
2894
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]][${N.rk}(${N.frame}, instr[4])]
|
|
2827
|
-
return
|
|
2895
|
+
return pc + 1
|
|
2828
2896
|
end
|
|
2829
2897
|
|
|
2830
2898
|
-- SETTABLE
|
|
2831
2899
|
${N.handlers}[${op(9 /* SETTABLE */)}] = 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
|
|
|
2836
2904
|
-- NEWTABLE
|
|
2837
2905
|
${N.handlers}[${op(10 /* NEWTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2838
2906
|
${N.frame}.${N.R}[instr[2]] = {}
|
|
2839
|
-
return
|
|
2907
|
+
return pc + 1
|
|
2840
2908
|
end
|
|
2841
2909
|
|
|
2842
2910
|
-- SELF (R[a] = R[b][key]; R[a+1] = R[b])
|
|
@@ -2844,90 +2912,90 @@ ${N.handlers}[${op(11 /* SELF */)}] = function(${N.frame}, pc, instr)
|
|
|
2844
2912
|
local obj = ${N.frame}.${N.R}[instr[3]]
|
|
2845
2913
|
${N.frame}.${N.R}[instr[2]] = obj[${N.rk}(${N.frame}, instr[4])]
|
|
2846
2914
|
${N.frame}.${N.R}[instr[2] + 1] = obj
|
|
2847
|
-
return
|
|
2915
|
+
return pc + 1
|
|
2848
2916
|
end
|
|
2849
2917
|
|
|
2850
2918
|
-- ADD SUB MUL DIV MOD POW
|
|
2851
2919
|
${N.handlers}[${op(12 /* ADD */)}] = function(${N.frame}, pc, instr)
|
|
2852
2920
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) + ${N.rk}(${N.frame}, instr[4])
|
|
2853
|
-
return
|
|
2921
|
+
return pc + 1
|
|
2854
2922
|
end
|
|
2855
2923
|
${N.handlers}[${op(13 /* SUB */)}] = 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
|
${N.handlers}[${op(14 /* MUL */)}] = function(${N.frame}, pc, instr)
|
|
2860
2928
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) * ${N.rk}(${N.frame}, instr[4])
|
|
2861
|
-
return
|
|
2929
|
+
return pc + 1
|
|
2862
2930
|
end
|
|
2863
2931
|
${N.handlers}[${op(15 /* DIV */)}] = function(${N.frame}, pc, instr)
|
|
2864
2932
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) / ${N.rk}(${N.frame}, instr[4])
|
|
2865
|
-
return
|
|
2933
|
+
return pc + 1
|
|
2866
2934
|
end
|
|
2867
2935
|
${N.handlers}[${op(16 /* MOD */)}] = function(${N.frame}, pc, instr)
|
|
2868
2936
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) % ${N.rk}(${N.frame}, instr[4])
|
|
2869
|
-
return
|
|
2937
|
+
return pc + 1
|
|
2870
2938
|
end
|
|
2871
2939
|
${N.handlers}[${op(17 /* POW */)}] = function(${N.frame}, pc, instr)
|
|
2872
2940
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) ^ ${N.rk}(${N.frame}, instr[4])
|
|
2873
|
-
return
|
|
2941
|
+
return pc + 1
|
|
2874
2942
|
end
|
|
2875
2943
|
|
|
2876
2944
|
-- IDIV (Luau \uBC14\uB2E5 \uB098\uB217\uC148)
|
|
2877
2945
|
${N.handlers}[${op(34 /* IDIV */)}] = function(${N.frame}, pc, instr)
|
|
2878
2946
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) // ${N.rk}(${N.frame}, instr[4])
|
|
2879
|
-
return
|
|
2947
|
+
return pc + 1
|
|
2880
2948
|
end
|
|
2881
2949
|
|
|
2882
2950
|
-- CONCAT
|
|
2883
2951
|
${N.handlers}[${op(18 /* CONCAT */)}] = function(${N.frame}, pc, instr)
|
|
2884
2952
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) .. ${N.rk}(${N.frame}, instr[4])
|
|
2885
|
-
return
|
|
2953
|
+
return pc + 1
|
|
2886
2954
|
end
|
|
2887
2955
|
|
|
2888
2956
|
-- UNM
|
|
2889
2957
|
${N.handlers}[${op(19 /* UNM */)}] = function(${N.frame}, pc, instr)
|
|
2890
2958
|
${N.frame}.${N.R}[instr[2]] = -${N.frame}.${N.R}[instr[3]]
|
|
2891
|
-
return
|
|
2959
|
+
return pc + 1
|
|
2892
2960
|
end
|
|
2893
2961
|
|
|
2894
2962
|
-- NOT
|
|
2895
2963
|
${N.handlers}[${op(20 /* NOT */)}] = function(${N.frame}, pc, instr)
|
|
2896
2964
|
${N.frame}.${N.R}[instr[2]] = not ${N.frame}.${N.R}[instr[3]]
|
|
2897
|
-
return
|
|
2965
|
+
return pc + 1
|
|
2898
2966
|
end
|
|
2899
2967
|
|
|
2900
2968
|
-- LEN
|
|
2901
2969
|
${N.handlers}[${op(21 /* LEN */)}] = function(${N.frame}, pc, instr)
|
|
2902
2970
|
${N.frame}.${N.R}[instr[2]] = #${N.frame}.${N.R}[instr[3]]
|
|
2903
|
-
return
|
|
2971
|
+
return pc + 1
|
|
2904
2972
|
end
|
|
2905
2973
|
|
|
2906
2974
|
-- JMP (pc += a)
|
|
2907
2975
|
${N.handlers}[${op(22 /* JMP */)}] = function(${N.frame}, pc, instr)
|
|
2908
|
-
return
|
|
2976
|
+
return pc + instr[2]
|
|
2909
2977
|
end
|
|
2910
2978
|
|
|
2911
2979
|
-- EQ LT LE (\uAC12 \uC0DD\uC131\uD615 \uBE44\uAD50)
|
|
2912
2980
|
${N.handlers}[${op(23 /* EQ */)}] = function(${N.frame}, pc, instr)
|
|
2913
2981
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) == ${N.rk}(${N.frame}, instr[4])
|
|
2914
|
-
return
|
|
2982
|
+
return pc + 1
|
|
2915
2983
|
end
|
|
2916
2984
|
${N.handlers}[${op(24 /* LT */)}] = function(${N.frame}, pc, instr)
|
|
2917
2985
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) < ${N.rk}(${N.frame}, instr[4])
|
|
2918
|
-
return
|
|
2986
|
+
return pc + 1
|
|
2919
2987
|
end
|
|
2920
2988
|
${N.handlers}[${op(25 /* LE */)}] = function(${N.frame}, pc, instr)
|
|
2921
2989
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) <= ${N.rk}(${N.frame}, instr[4])
|
|
2922
|
-
return
|
|
2990
|
+
return pc + 1
|
|
2923
2991
|
end
|
|
2924
2992
|
|
|
2925
2993
|
-- TEST (if (not R[a]) != (c!=0) then pc += 2 else pc += 1 \u2014 \uBCF4\uD1B5 \uB2E4\uC74C \uBA85\uB839\uC740 JMP)
|
|
2926
2994
|
${N.handlers}[${op(26 /* TEST */)}] = function(${N.frame}, pc, instr)
|
|
2927
2995
|
if (not ${N.frame}.${N.R}[instr[2]]) ~= (instr[4] ~= 0) then
|
|
2928
|
-
return
|
|
2996
|
+
return pc + 2
|
|
2929
2997
|
end
|
|
2930
|
-
return
|
|
2998
|
+
return pc + 1
|
|
2931
2999
|
end
|
|
2932
3000
|
|
|
2933
3001
|
-- CALL
|
|
@@ -2942,7 +3010,13 @@ ${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
|
2942
3010
|
end
|
|
2943
3011
|
local args = table.create(nargs)
|
|
2944
3012
|
for i = 1, nargs do args[i] = ${N.frame}.${N.R}[a + i] end
|
|
2945
|
-
|
|
3013
|
+
${debug ? ` if type(fn) ~= "function" then
|
|
3014
|
+
local mt = getmetatable(fn)
|
|
3015
|
+
if not (mt and mt.__call) then
|
|
3016
|
+
error(("[vmdebug] attempt to call a %s value: '%s' (source line %d)"):format(type(fn), tostring(instr[6]), tonumber(instr[5]) or -1), 0)
|
|
3017
|
+
end
|
|
3018
|
+
end
|
|
3019
|
+
` : ""} local rets = table.pack(fn(table.unpack(args, 1, nargs)))
|
|
2946
3020
|
if c == 1 then
|
|
2947
3021
|
-- discard
|
|
2948
3022
|
elseif c == 0 then
|
|
@@ -2952,23 +3026,27 @@ ${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
|
2952
3026
|
local want = c - 1
|
|
2953
3027
|
for i = 1, want do ${N.frame}.${N.R}[a + i - 1] = rets[i] end
|
|
2954
3028
|
end
|
|
2955
|
-
return
|
|
3029
|
+
return pc + 1
|
|
2956
3030
|
end
|
|
2957
3031
|
|
|
2958
3032
|
-- RETURN
|
|
3033
|
+
-- \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
|
|
3034
|
+
-- \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
|
|
3035
|
+
-- 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
|
|
3036
|
+
-- \uC911\uAC04 nil\uC774 \uC788\uC5B4\uB3C4 \uBC18\uD658 \uAC1C\uC218\uAC00 \uC815\uD655\uD788 \uBCF4\uC874\uB41C\uB2E4.
|
|
2959
3037
|
${N.handlers}[${op(28 /* RETURN */)}] = function(${N.frame}, pc, instr)
|
|
2960
3038
|
local a, b = instr[2], instr[3]
|
|
2961
3039
|
if b == 0 then
|
|
2962
|
-
return table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1)
|
|
3040
|
+
return table.pack(table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1))
|
|
2963
3041
|
end
|
|
2964
|
-
return table.unpack(${N.frame}.${N.R}, a, a + b - 2)
|
|
3042
|
+
return table.pack(table.unpack(${N.frame}.${N.R}, a, a + b - 2))
|
|
2965
3043
|
end
|
|
2966
3044
|
|
|
2967
3045
|
-- FORPREP
|
|
2968
3046
|
${N.handlers}[${op(29 /* FORPREP */)}] = function(${N.frame}, pc, instr)
|
|
2969
3047
|
local a = instr[2]
|
|
2970
3048
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] - ${N.frame}.${N.R}[a + 2]
|
|
2971
|
-
return
|
|
3049
|
+
return pc + instr[3]
|
|
2972
3050
|
end
|
|
2973
3051
|
|
|
2974
3052
|
-- FORLOOP
|
|
@@ -2984,9 +3062,9 @@ ${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
|
2984
3062
|
end
|
|
2985
3063
|
if ok then
|
|
2986
3064
|
${N.frame}.${N.R}[a + 3] = ${N.frame}.${N.R}[a]
|
|
2987
|
-
return
|
|
3065
|
+
return pc + instr[3]
|
|
2988
3066
|
end
|
|
2989
|
-
return
|
|
3067
|
+
return pc + 1
|
|
2990
3068
|
end
|
|
2991
3069
|
|
|
2992
3070
|
-- CLOSURE
|
|
@@ -3006,7 +3084,7 @@ ${N.handlers}[${op(31 /* CLOSURE */)}] = function(${N.frame}, pc, instr)
|
|
|
3006
3084
|
${N.frame}.${N.R}[instr[2]] = function(...)
|
|
3007
3085
|
return ${N.execute}(childProto, capturedUpvals, ...)
|
|
3008
3086
|
end
|
|
3009
|
-
return
|
|
3087
|
+
return pc + 1 + n
|
|
3010
3088
|
end
|
|
3011
3089
|
|
|
3012
3090
|
-- VARARG
|
|
@@ -3018,7 +3096,7 @@ ${N.handlers}[${op(32 /* VARARG */)}] = function(${N.frame}, pc, instr)
|
|
|
3018
3096
|
if instr[3] == 0 then
|
|
3019
3097
|
${N.frame}.${N.multiTop} = instr[2] + count
|
|
3020
3098
|
end
|
|
3021
|
-
return
|
|
3099
|
+
return pc + 1
|
|
3022
3100
|
end
|
|
3023
3101
|
|
|
3024
3102
|
-- SETLIST
|
|
@@ -3031,7 +3109,7 @@ ${N.handlers}[${op(33 /* SETLIST */)}] = function(${N.frame}, pc, instr)
|
|
|
3031
3109
|
for i = 0, n - 1 do
|
|
3032
3110
|
tbl[startIdx + i] = ${N.frame}.${N.R}[valuesBase + i]
|
|
3033
3111
|
end
|
|
3034
|
-
return
|
|
3112
|
+
return pc + 1
|
|
3035
3113
|
end
|
|
3036
3114
|
|
|
3037
3115
|
function ${N.execute}(proto, upvals, ...)
|
|
@@ -3120,19 +3198,20 @@ function markRuntimeNumbersAsStructural(body) {
|
|
|
3120
3198
|
function runVmify(program, options) {
|
|
3121
3199
|
const builtinGlobals = options.builtinGlobals ?? DEFAULT_BUILTIN_GLOBALS;
|
|
3122
3200
|
const random = options.random ?? Math.random;
|
|
3201
|
+
const debug = options.debug ?? false;
|
|
3123
3202
|
const opcodeMap = createOpcodeMap(random);
|
|
3124
3203
|
const names = generateVmNames(random);
|
|
3125
|
-
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap);
|
|
3204
|
+
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap, debug);
|
|
3126
3205
|
const topProto = compiler.compile();
|
|
3127
3206
|
const usedGlobalNames = compiler.getUsedGlobalNames();
|
|
3128
3207
|
const globalsTable = table(
|
|
3129
3208
|
usedGlobalNames.map((name) => namedField(name, identifier(name)))
|
|
3130
3209
|
);
|
|
3131
|
-
const runtimeSource = buildVmRuntimeSource(names, opcodeMap);
|
|
3210
|
+
const runtimeSource = buildVmRuntimeSource(names, opcodeMap, debug);
|
|
3132
3211
|
const runtimeProgram = import_luau_parser4.luauparser.parse(runtimeSource);
|
|
3133
3212
|
const runtimeStatements = runtimeProgram.body.statements;
|
|
3134
3213
|
markRuntimeNumbersAsStructural(runtimeProgram.body);
|
|
3135
|
-
const protoLiteral = serializeProto(topProto, names);
|
|
3214
|
+
const protoLiteral = serializeProto(topProto, names, debug);
|
|
3136
3215
|
const newBody = [
|
|
3137
3216
|
localStatement(names.globals, globalsTable),
|
|
3138
3217
|
...runtimeStatements,
|
package/dist/index.d.cts
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.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> = ({
|