luau-obfuscator 1.0.2 → 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 +171 -74
- package/dist/index.d.cts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +171 -74
- package/generated/grammers.luau +3654 -5314
- package/generated/tests.luau +2196 -3349
- package/package.json +1 -1
- package/scripts/test.js +1 -3
- package/src/passes/StringsToExpressions.ts +6 -6
- package/src/passes/Vmify.ts +24 -11
- package/src/passes/vmify/compiler.ts +90 -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
|
@@ -594,7 +594,7 @@ function splitByteChunks(bytes, min, max) {
|
|
|
594
594
|
return chunks.length > 0 ? chunks : [bytes];
|
|
595
595
|
}
|
|
596
596
|
function stringCharCall(bytes) {
|
|
597
|
-
return call(member(identifier("string"), "char"), bytes.map(
|
|
597
|
+
return call(member(identifier("string"), "char"), bytes.map(vmNumberLiteral));
|
|
598
598
|
}
|
|
599
599
|
function buildConcatChain(chunks) {
|
|
600
600
|
function build(lo, hi) {
|
|
@@ -625,7 +625,7 @@ function buildShuffledTables(chunks) {
|
|
|
625
625
|
for (let slot = 0; slot < n; slot++) {
|
|
626
626
|
order[storageOrder[slot]] = slot + 1;
|
|
627
627
|
}
|
|
628
|
-
const orderFields = order.map((slot) => positionalField(
|
|
628
|
+
const orderFields = order.map((slot) => positionalField(vmNumberLiteral(slot)));
|
|
629
629
|
return {
|
|
630
630
|
storeVar,
|
|
631
631
|
orderVar,
|
|
@@ -644,7 +644,7 @@ function buildForLoopStringExpr(chunks) {
|
|
|
644
644
|
localStatement(accVar, stringLiteral("")),
|
|
645
645
|
numericForStatement(
|
|
646
646
|
iVar,
|
|
647
|
-
|
|
647
|
+
vmNumberLiteral(1),
|
|
648
648
|
unary("#", identifier(orderVar)),
|
|
649
649
|
block([
|
|
650
650
|
assignmentStatement(
|
|
@@ -676,7 +676,7 @@ function buildRecursiveStringExpr(chunks) {
|
|
|
676
676
|
]),
|
|
677
677
|
returnStatement([
|
|
678
678
|
call(identifier(recName), [
|
|
679
|
-
binary("+", identifier(iParam),
|
|
679
|
+
binary("+", identifier(iParam), vmNumberLiteral(1)),
|
|
680
680
|
binary(
|
|
681
681
|
"..",
|
|
682
682
|
identifier(accParam),
|
|
@@ -688,7 +688,7 @@ function buildRecursiveStringExpr(chunks) {
|
|
|
688
688
|
return iife([
|
|
689
689
|
...stmts,
|
|
690
690
|
localFunctionStatement(recName, functionBody([functionParam(iParam), functionParam(accParam)], body)),
|
|
691
|
-
returnStatement([call(identifier(recName), [
|
|
691
|
+
returnStatement([call(identifier(recName), [vmNumberLiteral(1), stringLiteral("")])])
|
|
692
692
|
]);
|
|
693
693
|
}
|
|
694
694
|
function pickControlFlowStrategy() {
|
|
@@ -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,11 +1779,29 @@ 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();
|
|
1784
1786
|
protoIdCounter = 0;
|
|
1785
1787
|
declToBinding = /* @__PURE__ */ new Map();
|
|
1788
|
+
/**
|
|
1789
|
+
* 실제로 프로그램이 참조하는 모든 전역 이름(사전 등록된 builtinGlobals +
|
|
1790
|
+
* 스코프 분석이 자동으로 찾아낸, 로컬/파라미터/업밸류로 안 풀리는 나머지 식별자
|
|
1791
|
+
* 전부)을 돌려준다. Vmify.ts가 이 목록으로 브릿지 테이블(globals)을 만든다.
|
|
1792
|
+
*
|
|
1793
|
+
* 예전엔 호출자가 넘긴 builtinGlobals 목록만 브릿지에 넣었는데, 그 목록에
|
|
1794
|
+
* 없는 전역(예: Roblox API나 실행기 전용 전역을 다 못 채운 경우)을 참조하면
|
|
1795
|
+
* 컴파일은 성공하지만 GETGLOBAL이 조용히 nil을 반환해서 "attempt to call a
|
|
1796
|
+
* nil value"로 터진다 — 게다가 원인이 VM 디스패치 쪽 스택에서만 보여서
|
|
1797
|
+
* "opcode 매핑이 깨졌나?" 하고 엉뚱한 곳을 의심하게 만든다. analyzeScopes는
|
|
1798
|
+
* 어차피 로컬/업밸류로 못 푸는 식별자를 전부 global 바인딩으로 잡아두므로,
|
|
1799
|
+
* builtinGlobals에 없던 이름도 여기 globalsByName에는 다 들어있다 —
|
|
1800
|
+
* 하드코딩된 허용목록 대신 이걸 그대로 쓰면 커버리지 문제 자체가 사라진다.
|
|
1801
|
+
*/
|
|
1802
|
+
getUsedGlobalNames() {
|
|
1803
|
+
return Array.from(this.analysis.globalsByName.keys());
|
|
1804
|
+
}
|
|
1786
1805
|
computeCaptured() {
|
|
1787
1806
|
for (const binding of this.analysis.bindings.values()) {
|
|
1788
1807
|
if (binding.kind === "global") continue;
|
|
@@ -1819,9 +1838,39 @@ var VmCompiler = class {
|
|
|
1819
1838
|
}
|
|
1820
1839
|
emit(state, op, a, b, c, comment) {
|
|
1821
1840
|
const physicalOp = this.opcodeMap ? this.opcodeMap[op] : op;
|
|
1822
|
-
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 });
|
|
1823
1842
|
return state.proto.code.length - 1;
|
|
1824
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
|
+
}
|
|
1825
1874
|
konst(state, value) {
|
|
1826
1875
|
return internConst(state.proto, value);
|
|
1827
1876
|
}
|
|
@@ -1913,6 +1962,7 @@ var VmCompiler = class {
|
|
|
1913
1962
|
state.regs.releaseLocalsTo(saved);
|
|
1914
1963
|
}
|
|
1915
1964
|
compileStatement(stmt, state) {
|
|
1965
|
+
if (this.debug && stmt.line) state.curLine = stmt.line.start;
|
|
1916
1966
|
switch (stmt.type) {
|
|
1917
1967
|
case "LocalStatement": {
|
|
1918
1968
|
const bindingIds = stmt.names.map((n) => this.bindingIdOfDecl(n));
|
|
@@ -1938,11 +1988,26 @@ var VmCompiler = class {
|
|
|
1938
1988
|
return;
|
|
1939
1989
|
}
|
|
1940
1990
|
case "AssignmentStatement": {
|
|
1941
|
-
const
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
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);
|
|
1946
2011
|
return;
|
|
1947
2012
|
}
|
|
1948
2013
|
case "CompoundAssignmentStatement":
|
|
@@ -2286,7 +2351,7 @@ var VmCompiler = class {
|
|
|
2286
2351
|
compileCallMultiInto(callExpr, state, wantCount) {
|
|
2287
2352
|
const base = this.compileExpr(callExpr.callee, state);
|
|
2288
2353
|
this.compileArgsContiguous(callExpr.arguments, state, base + 1);
|
|
2289
|
-
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);
|
|
2290
2355
|
return base;
|
|
2291
2356
|
}
|
|
2292
2357
|
loadConst(state, value) {
|
|
@@ -2337,7 +2402,7 @@ var VmCompiler = class {
|
|
|
2337
2402
|
if (expr.type === "CallExpression") {
|
|
2338
2403
|
const calleeReg = this.compileExpr(expr.callee, state);
|
|
2339
2404
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, calleeReg + 1);
|
|
2340
|
-
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);
|
|
2341
2406
|
for (let i = 0; i < wantCount; i++) {
|
|
2342
2407
|
if (calleeReg + i !== base + i) this.emit(state, 0 /* MOVE */, base + i, calleeReg + i, 0);
|
|
2343
2408
|
}
|
|
@@ -2352,7 +2417,7 @@ var VmCompiler = class {
|
|
|
2352
2417
|
state.regs.allocTemp();
|
|
2353
2418
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, fnSlot + 2);
|
|
2354
2419
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2355
|
-
this.emit(state, 27 /* CALL */, fnSlot, b, wantCount + 1);
|
|
2420
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, fnSlot, b, wantCount + 1), expr, expr);
|
|
2356
2421
|
for (let i = 0; i < wantCount; i++) {
|
|
2357
2422
|
if (fnSlot + i !== base + i) this.emit(state, 0 /* MOVE */, base + i, fnSlot + i, 0);
|
|
2358
2423
|
}
|
|
@@ -2378,7 +2443,7 @@ var VmCompiler = class {
|
|
|
2378
2443
|
if (calleeReg !== base) this.emit(state, 0 /* MOVE */, base, calleeReg, 0);
|
|
2379
2444
|
state.regs.freeTemp(base + 1);
|
|
2380
2445
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, base + 1);
|
|
2381
|
-
this.emit(state, 27 /* CALL */, base, b, 0);
|
|
2446
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, b, 0), expr, expr.callee);
|
|
2382
2447
|
return;
|
|
2383
2448
|
}
|
|
2384
2449
|
if (expr.type === "MethodCallExpression") {
|
|
@@ -2394,7 +2459,7 @@ var VmCompiler = class {
|
|
|
2394
2459
|
state.regs.freeTemp(base + 2);
|
|
2395
2460
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, base + 2);
|
|
2396
2461
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2397
|
-
this.emit(state, 27 /* CALL */, base, b, 0);
|
|
2462
|
+
this.stampCall(state, this.emit(state, 27 /* CALL */, base, b, 0), expr, expr);
|
|
2398
2463
|
return;
|
|
2399
2464
|
}
|
|
2400
2465
|
if (expr.type === "VarargExpression") {
|
|
@@ -2421,6 +2486,7 @@ var VmCompiler = class {
|
|
|
2421
2486
|
return args.length + 1;
|
|
2422
2487
|
}
|
|
2423
2488
|
compileExpr(expr, state, discard = false) {
|
|
2489
|
+
if (this.debug && expr.line) state.curLine = expr.line.start;
|
|
2424
2490
|
switch (expr.type) {
|
|
2425
2491
|
case "NilLiteral": {
|
|
2426
2492
|
const r = state.regs.allocTemp();
|
|
@@ -2578,7 +2644,8 @@ var VmCompiler = class {
|
|
|
2578
2644
|
case "CallExpression": {
|
|
2579
2645
|
const base = this.compileExpr(expr.callee, state);
|
|
2580
2646
|
const b = this.compileCallArgsAndGetB(expr.arguments, state, base + 1);
|
|
2581
|
-
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);
|
|
2582
2649
|
state.regs.freeTemp(base + (discard ? 0 : 1));
|
|
2583
2650
|
return base;
|
|
2584
2651
|
}
|
|
@@ -2590,7 +2657,8 @@ var VmCompiler = class {
|
|
|
2590
2657
|
state.regs.allocTemp();
|
|
2591
2658
|
const argB = this.compileCallArgsAndGetB(expr.arguments, state, fnSlot + 2);
|
|
2592
2659
|
const b = argB === 0 ? 0 : expr.arguments.length + 2;
|
|
2593
|
-
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);
|
|
2594
2662
|
state.regs.freeTemp(fnSlot + (discard ? 0 : 1));
|
|
2595
2663
|
return fnSlot;
|
|
2596
2664
|
}
|
|
@@ -2703,15 +2771,20 @@ function serializeConstValue(v) {
|
|
|
2703
2771
|
if (typeof v === "number") return numberLiteral(v);
|
|
2704
2772
|
return booleanLiteral(v);
|
|
2705
2773
|
}
|
|
2706
|
-
function serializeInstrs(proto) {
|
|
2707
|
-
const fields = proto.code.map((instr) =>
|
|
2708
|
-
|
|
2774
|
+
function serializeInstrs(proto, debug) {
|
|
2775
|
+
const fields = proto.code.map((instr) => {
|
|
2776
|
+
const tuple = [
|
|
2709
2777
|
positionalField(vmNumberLiteral(instr.op)),
|
|
2710
2778
|
positionalField(vmNumberLiteral(instr.a)),
|
|
2711
2779
|
positionalField(vmNumberLiteral(instr.b)),
|
|
2712
2780
|
positionalField(vmNumberLiteral(instr.c))
|
|
2713
|
-
]
|
|
2714
|
-
|
|
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
|
+
});
|
|
2715
2788
|
return table(fields);
|
|
2716
2789
|
}
|
|
2717
2790
|
function serializeUpvalDescs(proto, names) {
|
|
@@ -2723,29 +2796,41 @@ function serializeUpvalDescs(proto, names) {
|
|
|
2723
2796
|
));
|
|
2724
2797
|
return table(fields);
|
|
2725
2798
|
}
|
|
2726
|
-
function serializeProto(proto, names) {
|
|
2799
|
+
function serializeProto(proto, names, debug = false) {
|
|
2727
2800
|
const fields = [
|
|
2728
2801
|
namedField(names.numParams, vmNumberLiteral(proto.numParams)),
|
|
2729
2802
|
namedField(names.hasVarargs, booleanLiteral(proto.hasVarargs)),
|
|
2730
2803
|
namedField(names.maxRegs, vmNumberLiteral(proto.maxRegs)),
|
|
2731
|
-
namedField(names.code, serializeInstrs(proto)),
|
|
2804
|
+
namedField(names.code, serializeInstrs(proto, debug)),
|
|
2732
2805
|
namedField(names.consts, serializeConsts(proto.consts)),
|
|
2733
2806
|
namedField(names.upvalDescs, serializeUpvalDescs(proto, names)),
|
|
2734
|
-
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)))))
|
|
2735
2808
|
];
|
|
2736
2809
|
return table(fields);
|
|
2737
2810
|
}
|
|
2738
2811
|
|
|
2739
2812
|
// src/passes/vmify/runtime.ts
|
|
2740
|
-
function buildVmRuntimeSource(names, opcodeMap) {
|
|
2813
|
+
function buildVmRuntimeSource(names, opcodeMap, debug = false) {
|
|
2741
2814
|
const N = names;
|
|
2742
2815
|
const op = (o) => opcodeMap[o];
|
|
2743
2816
|
return `
|
|
2744
2817
|
local ${N.handlers} = {}
|
|
2745
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.
|
|
2746
2823
|
local function ${N.dispatch}(${N.frame}, pc)
|
|
2747
|
-
local
|
|
2748
|
-
|
|
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
|
|
2749
2834
|
end
|
|
2750
2835
|
|
|
2751
2836
|
local function ${N.rk}(${N.frame}, x)
|
|
@@ -2759,67 +2844,67 @@ end
|
|
|
2759
2844
|
-- MOVE
|
|
2760
2845
|
${N.handlers}[${op(0 /* MOVE */)}] = function(${N.frame}, pc, instr)
|
|
2761
2846
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]]
|
|
2762
|
-
return
|
|
2847
|
+
return pc + 1
|
|
2763
2848
|
end
|
|
2764
2849
|
|
|
2765
2850
|
-- LOADK
|
|
2766
2851
|
${N.handlers}[${op(1 /* LOADK */)}] = function(${N.frame}, pc, instr)
|
|
2767
2852
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.K}[instr[3] + 1]
|
|
2768
|
-
return
|
|
2853
|
+
return pc + 1
|
|
2769
2854
|
end
|
|
2770
2855
|
|
|
2771
2856
|
-- LOADBOOL
|
|
2772
2857
|
${N.handlers}[${op(2 /* LOADBOOL */)}] = function(${N.frame}, pc, instr)
|
|
2773
2858
|
${N.frame}.${N.R}[instr[2]] = (instr[3] ~= 0)
|
|
2774
|
-
return
|
|
2859
|
+
return pc + 1
|
|
2775
2860
|
end
|
|
2776
2861
|
|
|
2777
2862
|
-- LOADNIL
|
|
2778
2863
|
${N.handlers}[${op(3 /* LOADNIL */)}] = function(${N.frame}, pc, instr)
|
|
2779
2864
|
for i = instr[2], instr[3] do ${N.frame}.${N.R}[i] = nil end
|
|
2780
|
-
return
|
|
2865
|
+
return pc + 1
|
|
2781
2866
|
end
|
|
2782
2867
|
|
|
2783
2868
|
-- GETUPVAL
|
|
2784
2869
|
${N.handlers}[${op(4 /* GETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2785
2870
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.upvals}[instr[3] + 1]
|
|
2786
|
-
return
|
|
2871
|
+
return pc + 1
|
|
2787
2872
|
end
|
|
2788
2873
|
|
|
2789
2874
|
-- SETUPVAL
|
|
2790
2875
|
${N.handlers}[${op(5 /* SETUPVAL */)}] = function(${N.frame}, pc, instr)
|
|
2791
2876
|
${N.frame}.${N.upvals}[instr[3] + 1] = ${N.frame}.${N.R}[instr[2]]
|
|
2792
|
-
return
|
|
2877
|
+
return pc + 1
|
|
2793
2878
|
end
|
|
2794
2879
|
|
|
2795
2880
|
-- GETGLOBAL (b\uB294 RK\uAC00 \uC544\uB2C8\uB77C \uC0C1\uC218\uD480 \uC21C\uC218 \uC778\uB371\uC2A4)
|
|
2796
2881
|
${N.handlers}[${op(6 /* GETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2797
2882
|
${N.frame}.${N.R}[instr[2]] = ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]]
|
|
2798
|
-
return
|
|
2883
|
+
return pc + 1
|
|
2799
2884
|
end
|
|
2800
2885
|
|
|
2801
2886
|
-- SETGLOBAL
|
|
2802
2887
|
${N.handlers}[${op(7 /* SETGLOBAL */)}] = function(${N.frame}, pc, instr)
|
|
2803
2888
|
${N.globals}[${N.frame}.${N.K}[instr[3] + 1]] = ${N.frame}.${N.R}[instr[2]]
|
|
2804
|
-
return
|
|
2889
|
+
return pc + 1
|
|
2805
2890
|
end
|
|
2806
2891
|
|
|
2807
2892
|
-- GETTABLE
|
|
2808
2893
|
${N.handlers}[${op(8 /* GETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2809
2894
|
${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]][${N.rk}(${N.frame}, instr[4])]
|
|
2810
|
-
return
|
|
2895
|
+
return pc + 1
|
|
2811
2896
|
end
|
|
2812
2897
|
|
|
2813
2898
|
-- SETTABLE
|
|
2814
2899
|
${N.handlers}[${op(9 /* SETTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2815
2900
|
${N.frame}.${N.R}[instr[2]][${N.rk}(${N.frame}, instr[3])] = ${N.rk}(${N.frame}, instr[4])
|
|
2816
|
-
return
|
|
2901
|
+
return pc + 1
|
|
2817
2902
|
end
|
|
2818
2903
|
|
|
2819
2904
|
-- NEWTABLE
|
|
2820
2905
|
${N.handlers}[${op(10 /* NEWTABLE */)}] = function(${N.frame}, pc, instr)
|
|
2821
2906
|
${N.frame}.${N.R}[instr[2]] = {}
|
|
2822
|
-
return
|
|
2907
|
+
return pc + 1
|
|
2823
2908
|
end
|
|
2824
2909
|
|
|
2825
2910
|
-- SELF (R[a] = R[b][key]; R[a+1] = R[b])
|
|
@@ -2827,90 +2912,90 @@ ${N.handlers}[${op(11 /* SELF */)}] = function(${N.frame}, pc, instr)
|
|
|
2827
2912
|
local obj = ${N.frame}.${N.R}[instr[3]]
|
|
2828
2913
|
${N.frame}.${N.R}[instr[2]] = obj[${N.rk}(${N.frame}, instr[4])]
|
|
2829
2914
|
${N.frame}.${N.R}[instr[2] + 1] = obj
|
|
2830
|
-
return
|
|
2915
|
+
return pc + 1
|
|
2831
2916
|
end
|
|
2832
2917
|
|
|
2833
2918
|
-- ADD SUB MUL DIV MOD POW
|
|
2834
2919
|
${N.handlers}[${op(12 /* ADD */)}] = function(${N.frame}, pc, instr)
|
|
2835
2920
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) + ${N.rk}(${N.frame}, instr[4])
|
|
2836
|
-
return
|
|
2921
|
+
return pc + 1
|
|
2837
2922
|
end
|
|
2838
2923
|
${N.handlers}[${op(13 /* SUB */)}] = function(${N.frame}, pc, instr)
|
|
2839
2924
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) - ${N.rk}(${N.frame}, instr[4])
|
|
2840
|
-
return
|
|
2925
|
+
return pc + 1
|
|
2841
2926
|
end
|
|
2842
2927
|
${N.handlers}[${op(14 /* MUL */)}] = function(${N.frame}, pc, instr)
|
|
2843
2928
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) * ${N.rk}(${N.frame}, instr[4])
|
|
2844
|
-
return
|
|
2929
|
+
return pc + 1
|
|
2845
2930
|
end
|
|
2846
2931
|
${N.handlers}[${op(15 /* DIV */)}] = function(${N.frame}, pc, instr)
|
|
2847
2932
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) / ${N.rk}(${N.frame}, instr[4])
|
|
2848
|
-
return
|
|
2933
|
+
return pc + 1
|
|
2849
2934
|
end
|
|
2850
2935
|
${N.handlers}[${op(16 /* MOD */)}] = function(${N.frame}, pc, instr)
|
|
2851
2936
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) % ${N.rk}(${N.frame}, instr[4])
|
|
2852
|
-
return
|
|
2937
|
+
return pc + 1
|
|
2853
2938
|
end
|
|
2854
2939
|
${N.handlers}[${op(17 /* POW */)}] = function(${N.frame}, pc, instr)
|
|
2855
2940
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) ^ ${N.rk}(${N.frame}, instr[4])
|
|
2856
|
-
return
|
|
2941
|
+
return pc + 1
|
|
2857
2942
|
end
|
|
2858
2943
|
|
|
2859
2944
|
-- IDIV (Luau \uBC14\uB2E5 \uB098\uB217\uC148)
|
|
2860
2945
|
${N.handlers}[${op(34 /* IDIV */)}] = function(${N.frame}, pc, instr)
|
|
2861
2946
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) // ${N.rk}(${N.frame}, instr[4])
|
|
2862
|
-
return
|
|
2947
|
+
return pc + 1
|
|
2863
2948
|
end
|
|
2864
2949
|
|
|
2865
2950
|
-- CONCAT
|
|
2866
2951
|
${N.handlers}[${op(18 /* CONCAT */)}] = function(${N.frame}, pc, instr)
|
|
2867
2952
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) .. ${N.rk}(${N.frame}, instr[4])
|
|
2868
|
-
return
|
|
2953
|
+
return pc + 1
|
|
2869
2954
|
end
|
|
2870
2955
|
|
|
2871
2956
|
-- UNM
|
|
2872
2957
|
${N.handlers}[${op(19 /* UNM */)}] = function(${N.frame}, pc, instr)
|
|
2873
2958
|
${N.frame}.${N.R}[instr[2]] = -${N.frame}.${N.R}[instr[3]]
|
|
2874
|
-
return
|
|
2959
|
+
return pc + 1
|
|
2875
2960
|
end
|
|
2876
2961
|
|
|
2877
2962
|
-- NOT
|
|
2878
2963
|
${N.handlers}[${op(20 /* NOT */)}] = function(${N.frame}, pc, instr)
|
|
2879
2964
|
${N.frame}.${N.R}[instr[2]] = not ${N.frame}.${N.R}[instr[3]]
|
|
2880
|
-
return
|
|
2965
|
+
return pc + 1
|
|
2881
2966
|
end
|
|
2882
2967
|
|
|
2883
2968
|
-- LEN
|
|
2884
2969
|
${N.handlers}[${op(21 /* LEN */)}] = function(${N.frame}, pc, instr)
|
|
2885
2970
|
${N.frame}.${N.R}[instr[2]] = #${N.frame}.${N.R}[instr[3]]
|
|
2886
|
-
return
|
|
2971
|
+
return pc + 1
|
|
2887
2972
|
end
|
|
2888
2973
|
|
|
2889
2974
|
-- JMP (pc += a)
|
|
2890
2975
|
${N.handlers}[${op(22 /* JMP */)}] = function(${N.frame}, pc, instr)
|
|
2891
|
-
return
|
|
2976
|
+
return pc + instr[2]
|
|
2892
2977
|
end
|
|
2893
2978
|
|
|
2894
2979
|
-- EQ LT LE (\uAC12 \uC0DD\uC131\uD615 \uBE44\uAD50)
|
|
2895
2980
|
${N.handlers}[${op(23 /* EQ */)}] = function(${N.frame}, pc, instr)
|
|
2896
2981
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) == ${N.rk}(${N.frame}, instr[4])
|
|
2897
|
-
return
|
|
2982
|
+
return pc + 1
|
|
2898
2983
|
end
|
|
2899
2984
|
${N.handlers}[${op(24 /* LT */)}] = function(${N.frame}, pc, instr)
|
|
2900
2985
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) < ${N.rk}(${N.frame}, instr[4])
|
|
2901
|
-
return
|
|
2986
|
+
return pc + 1
|
|
2902
2987
|
end
|
|
2903
2988
|
${N.handlers}[${op(25 /* LE */)}] = function(${N.frame}, pc, instr)
|
|
2904
2989
|
${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) <= ${N.rk}(${N.frame}, instr[4])
|
|
2905
|
-
return
|
|
2990
|
+
return pc + 1
|
|
2906
2991
|
end
|
|
2907
2992
|
|
|
2908
2993
|
-- TEST (if (not R[a]) != (c!=0) then pc += 2 else pc += 1 \u2014 \uBCF4\uD1B5 \uB2E4\uC74C \uBA85\uB839\uC740 JMP)
|
|
2909
2994
|
${N.handlers}[${op(26 /* TEST */)}] = function(${N.frame}, pc, instr)
|
|
2910
2995
|
if (not ${N.frame}.${N.R}[instr[2]]) ~= (instr[4] ~= 0) then
|
|
2911
|
-
return
|
|
2996
|
+
return pc + 2
|
|
2912
2997
|
end
|
|
2913
|
-
return
|
|
2998
|
+
return pc + 1
|
|
2914
2999
|
end
|
|
2915
3000
|
|
|
2916
3001
|
-- CALL
|
|
@@ -2925,7 +3010,13 @@ ${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
|
2925
3010
|
end
|
|
2926
3011
|
local args = table.create(nargs)
|
|
2927
3012
|
for i = 1, nargs do args[i] = ${N.frame}.${N.R}[a + i] end
|
|
2928
|
-
|
|
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)))
|
|
2929
3020
|
if c == 1 then
|
|
2930
3021
|
-- discard
|
|
2931
3022
|
elseif c == 0 then
|
|
@@ -2935,23 +3026,27 @@ ${N.handlers}[${op(27 /* CALL */)}] = function(${N.frame}, pc, instr)
|
|
|
2935
3026
|
local want = c - 1
|
|
2936
3027
|
for i = 1, want do ${N.frame}.${N.R}[a + i - 1] = rets[i] end
|
|
2937
3028
|
end
|
|
2938
|
-
return
|
|
3029
|
+
return pc + 1
|
|
2939
3030
|
end
|
|
2940
3031
|
|
|
2941
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.
|
|
2942
3037
|
${N.handlers}[${op(28 /* RETURN */)}] = function(${N.frame}, pc, instr)
|
|
2943
3038
|
local a, b = instr[2], instr[3]
|
|
2944
3039
|
if b == 0 then
|
|
2945
|
-
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))
|
|
2946
3041
|
end
|
|
2947
|
-
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))
|
|
2948
3043
|
end
|
|
2949
3044
|
|
|
2950
3045
|
-- FORPREP
|
|
2951
3046
|
${N.handlers}[${op(29 /* FORPREP */)}] = function(${N.frame}, pc, instr)
|
|
2952
3047
|
local a = instr[2]
|
|
2953
3048
|
${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] - ${N.frame}.${N.R}[a + 2]
|
|
2954
|
-
return
|
|
3049
|
+
return pc + instr[3]
|
|
2955
3050
|
end
|
|
2956
3051
|
|
|
2957
3052
|
-- FORLOOP
|
|
@@ -2967,9 +3062,9 @@ ${N.handlers}[${op(30 /* FORLOOP */)}] = function(${N.frame}, pc, instr)
|
|
|
2967
3062
|
end
|
|
2968
3063
|
if ok then
|
|
2969
3064
|
${N.frame}.${N.R}[a + 3] = ${N.frame}.${N.R}[a]
|
|
2970
|
-
return
|
|
3065
|
+
return pc + instr[3]
|
|
2971
3066
|
end
|
|
2972
|
-
return
|
|
3067
|
+
return pc + 1
|
|
2973
3068
|
end
|
|
2974
3069
|
|
|
2975
3070
|
-- CLOSURE
|
|
@@ -2989,7 +3084,7 @@ ${N.handlers}[${op(31 /* CLOSURE */)}] = function(${N.frame}, pc, instr)
|
|
|
2989
3084
|
${N.frame}.${N.R}[instr[2]] = function(...)
|
|
2990
3085
|
return ${N.execute}(childProto, capturedUpvals, ...)
|
|
2991
3086
|
end
|
|
2992
|
-
return
|
|
3087
|
+
return pc + 1 + n
|
|
2993
3088
|
end
|
|
2994
3089
|
|
|
2995
3090
|
-- VARARG
|
|
@@ -3001,7 +3096,7 @@ ${N.handlers}[${op(32 /* VARARG */)}] = function(${N.frame}, pc, instr)
|
|
|
3001
3096
|
if instr[3] == 0 then
|
|
3002
3097
|
${N.frame}.${N.multiTop} = instr[2] + count
|
|
3003
3098
|
end
|
|
3004
|
-
return
|
|
3099
|
+
return pc + 1
|
|
3005
3100
|
end
|
|
3006
3101
|
|
|
3007
3102
|
-- SETLIST
|
|
@@ -3014,7 +3109,7 @@ ${N.handlers}[${op(33 /* SETLIST */)}] = function(${N.frame}, pc, instr)
|
|
|
3014
3109
|
for i = 0, n - 1 do
|
|
3015
3110
|
tbl[startIdx + i] = ${N.frame}.${N.R}[valuesBase + i]
|
|
3016
3111
|
end
|
|
3017
|
-
return
|
|
3112
|
+
return pc + 1
|
|
3018
3113
|
end
|
|
3019
3114
|
|
|
3020
3115
|
function ${N.execute}(proto, upvals, ...)
|
|
@@ -3101,20 +3196,22 @@ function markRuntimeNumbersAsStructural(body) {
|
|
|
3101
3196
|
});
|
|
3102
3197
|
}
|
|
3103
3198
|
function runVmify(program, options) {
|
|
3104
|
-
const
|
|
3199
|
+
const builtinGlobals = options.builtinGlobals ?? DEFAULT_BUILTIN_GLOBALS;
|
|
3105
3200
|
const random = options.random ?? Math.random;
|
|
3201
|
+
const debug = options.debug ?? false;
|
|
3106
3202
|
const opcodeMap = createOpcodeMap(random);
|
|
3107
3203
|
const names = generateVmNames(random);
|
|
3108
|
-
const compiler = new VmCompiler(program,
|
|
3204
|
+
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap, debug);
|
|
3109
3205
|
const topProto = compiler.compile();
|
|
3206
|
+
const usedGlobalNames = compiler.getUsedGlobalNames();
|
|
3110
3207
|
const globalsTable = table(
|
|
3111
|
-
|
|
3208
|
+
usedGlobalNames.map((name) => namedField(name, identifier(name)))
|
|
3112
3209
|
);
|
|
3113
|
-
const runtimeSource = buildVmRuntimeSource(names, opcodeMap);
|
|
3210
|
+
const runtimeSource = buildVmRuntimeSource(names, opcodeMap, debug);
|
|
3114
3211
|
const runtimeProgram = import_luau_parser4.luauparser.parse(runtimeSource);
|
|
3115
3212
|
const runtimeStatements = runtimeProgram.body.statements;
|
|
3116
3213
|
markRuntimeNumbersAsStructural(runtimeProgram.body);
|
|
3117
|
-
const protoLiteral = serializeProto(topProto, names);
|
|
3214
|
+
const protoLiteral = serializeProto(topProto, names, debug);
|
|
3118
3215
|
const newBody = [
|
|
3119
3216
|
localStatement(names.globals, globalsTable),
|
|
3120
3217
|
...runtimeStatements,
|
package/dist/index.d.cts
CHANGED
|
@@ -47,12 +47,12 @@ interface EncryptNumbersOptions {
|
|
|
47
47
|
|
|
48
48
|
interface VmifyOptions {
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
50
|
+
* 스코프 분석에 미리 등록해둘 전역 이름 시드 목록. 여기 없는 이름이라도 프로그램에서
|
|
51
|
+
* 실제로 참조되면(로컬/파라미터/업밸류로 안 풀리는 식별자는 전부) 스코프 분석이
|
|
52
|
+
* 자동으로 전역으로 잡아내므로, 브릿지 테이블은 이 옵션이 아니라
|
|
53
|
+
* VmCompiler.getUsedGlobalNames()(실제 사용된 전역 전체)로 만든다 — 옛날엔 이 목록에
|
|
54
|
+
* 없는 이름을 쓰면 컴파일은 되는데 GETGLOBAL이 조용히 nil을 반환해서
|
|
55
|
+
* "attempt to call a nil value"로 터졌었음.
|
|
56
56
|
*/
|
|
57
57
|
builtinGlobals?: readonly string[];
|
|
58
58
|
/**
|
|
@@ -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> = ({
|