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/src/passes/StripTypes.ts
CHANGED
|
@@ -1,192 +1,192 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Program, Block, Statement, Expression, FunctionBody,
|
|
3
|
-
} from "luau-parser"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 파이프라인 맨 앞에서 실행되어야 하는 패스.
|
|
7
|
-
*
|
|
8
|
-
* 이후 패스들(GlobalMapping, RenameVariables 등)은 값 트리만 순회하고
|
|
9
|
-
* `typeof(x)`, `x :: T` 같은 타입 노드 내부의 expression은 건드리지 않는다.
|
|
10
|
-
* 타입을 나중까지 들고 있으면 값 쪽 식별자는 바뀌는데 타입 쪽 참조는 원래
|
|
11
|
-
* 이름 그대로 남아 출력물이 타입체크 불가능한 상태가 된다
|
|
12
|
-
* (`typeof(runtimeValue)`가 obfuscate 이후에도 원본 이름을 참조하는 것이 그 예).
|
|
13
|
-
*
|
|
14
|
-
* 그래서 타입 정보 자체를 파이프라인 시작 시점에 전부 지워서 이 클래스의
|
|
15
|
-
* 버그를 원천 차단한다:
|
|
16
|
-
* - TypeAliasStatement / ExportTypeAliasStatement 문 자체를 블록에서 제거
|
|
17
|
-
* - TypedIdentifier / FunctionParameter 의 typeAnnotation 제거
|
|
18
|
-
* - FunctionBody 의 generics / varargTypeAnnotation / returnType 제거
|
|
19
|
-
* - `expr :: T` (TypeAssertionExpression) 은 T를 버리고 expr로 치환
|
|
20
|
-
*/
|
|
21
|
-
export function runStripTypes(program: Program): void {
|
|
22
|
-
stripBlock(program.body)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function stripBlock(block: Block): void {
|
|
26
|
-
const kept: Statement[] = []
|
|
27
|
-
for (const stmt of block.statements) {
|
|
28
|
-
if (stmt.type === "TypeAliasStatement" || stmt.type === "ExportTypeAliasStatement") {
|
|
29
|
-
continue
|
|
30
|
-
}
|
|
31
|
-
stripStatement(stmt)
|
|
32
|
-
kept.push(stmt)
|
|
33
|
-
}
|
|
34
|
-
block.statements = kept
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function stripFunctionBody(func: FunctionBody): void {
|
|
38
|
-
func.generics = []
|
|
39
|
-
for (const param of func.params) delete param.typeAnnotation
|
|
40
|
-
delete func.varargTypeAnnotation
|
|
41
|
-
delete func.returnType
|
|
42
|
-
stripBlock(func.body)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function stripStatement(stmt: Statement): void {
|
|
46
|
-
switch (stmt.type) {
|
|
47
|
-
case "LocalStatement":
|
|
48
|
-
for (const name of stmt.names) delete name.typeAnnotation
|
|
49
|
-
for (let i = 0; i < stmt.init.length; i++) stmt.init[i] = stripExpr(stmt.init[i])
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
case "LocalFunctionStatement":
|
|
53
|
-
stripFunctionBody(stmt.func)
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
case "FunctionDeclarationStatement":
|
|
57
|
-
stripFunctionBody(stmt.func)
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
case "AssignmentStatement":
|
|
61
|
-
for (let i = 0; i < stmt.targets.length; i++) stmt.targets[i] = stripExpr(stmt.targets[i])
|
|
62
|
-
for (let i = 0; i < stmt.values.length; i++) stmt.values[i] = stripExpr(stmt.values[i])
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
case "CompoundAssignmentStatement":
|
|
66
|
-
stmt.target = stripExpr(stmt.target)
|
|
67
|
-
stmt.value = stripExpr(stmt.value)
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
case "CallStatement":
|
|
71
|
-
stmt.expression = stripExpr(stmt.expression) as typeof stmt.expression
|
|
72
|
-
return
|
|
73
|
-
|
|
74
|
-
case "DoStatement":
|
|
75
|
-
stripBlock(stmt.body)
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
case "WhileStatement":
|
|
79
|
-
stmt.condition = stripExpr(stmt.condition)
|
|
80
|
-
stripBlock(stmt.body)
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
case "RepeatStatement":
|
|
84
|
-
stripBlock(stmt.body)
|
|
85
|
-
stmt.condition = stripExpr(stmt.condition)
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
case "IfStatement":
|
|
89
|
-
for (const clause of stmt.clauses) {
|
|
90
|
-
clause.condition = stripExpr(clause.condition)
|
|
91
|
-
stripBlock(clause.body)
|
|
92
|
-
}
|
|
93
|
-
if (stmt.alternate) stripBlock(stmt.alternate)
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
case "NumericForStatement":
|
|
97
|
-
delete stmt.variable.typeAnnotation
|
|
98
|
-
stmt.start = stripExpr(stmt.start)
|
|
99
|
-
stmt.end = stripExpr(stmt.end)
|
|
100
|
-
if (stmt.step) stmt.step = stripExpr(stmt.step)
|
|
101
|
-
stripBlock(stmt.body)
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
case "GenericForStatement":
|
|
105
|
-
for (const v of stmt.variables) delete v.typeAnnotation
|
|
106
|
-
for (let i = 0; i < stmt.iterators.length; i++) stmt.iterators[i] = stripExpr(stmt.iterators[i])
|
|
107
|
-
stripBlock(stmt.body)
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
case "ReturnStatement":
|
|
111
|
-
for (let i = 0; i < stmt.arguments.length; i++) stmt.arguments[i] = stripExpr(stmt.arguments[i])
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
case "BreakStatement":
|
|
115
|
-
case "ContinueStatement":
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function stripExpr(expr: Expression): Expression {
|
|
121
|
-
switch (expr.type) {
|
|
122
|
-
case "InterpolatedStringExpression":
|
|
123
|
-
for (const part of expr.parts) {
|
|
124
|
-
if (part.kind === "expression") part.expression = stripExpr(part.expression)
|
|
125
|
-
}
|
|
126
|
-
return expr
|
|
127
|
-
|
|
128
|
-
case "FunctionExpression":
|
|
129
|
-
stripFunctionBody(expr.func)
|
|
130
|
-
return expr
|
|
131
|
-
|
|
132
|
-
case "TableExpression":
|
|
133
|
-
for (const field of expr.fields) {
|
|
134
|
-
if (field.type === "TableFieldPositional") {
|
|
135
|
-
field.value = stripExpr(field.value)
|
|
136
|
-
} else if (field.type === "TableFieldNamed") {
|
|
137
|
-
field.value = stripExpr(field.value)
|
|
138
|
-
} else if (field.type === "TableFieldComputed") {
|
|
139
|
-
field.key = stripExpr(field.key)
|
|
140
|
-
field.value = stripExpr(field.value)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return expr
|
|
144
|
-
|
|
145
|
-
case "BinaryExpression":
|
|
146
|
-
expr.left = stripExpr(expr.left)
|
|
147
|
-
expr.right = stripExpr(expr.right)
|
|
148
|
-
return expr
|
|
149
|
-
|
|
150
|
-
case "UnaryExpression":
|
|
151
|
-
expr.argument = stripExpr(expr.argument)
|
|
152
|
-
return expr
|
|
153
|
-
|
|
154
|
-
case "MemberExpression":
|
|
155
|
-
expr.object = stripExpr(expr.object)
|
|
156
|
-
return expr
|
|
157
|
-
|
|
158
|
-
case "IndexExpression":
|
|
159
|
-
expr.object = stripExpr(expr.object)
|
|
160
|
-
expr.index = stripExpr(expr.index)
|
|
161
|
-
return expr
|
|
162
|
-
|
|
163
|
-
case "CallExpression":
|
|
164
|
-
expr.callee = stripExpr(expr.callee)
|
|
165
|
-
for (let i = 0; i < expr.arguments.length; i++) expr.arguments[i] = stripExpr(expr.arguments[i])
|
|
166
|
-
return expr
|
|
167
|
-
|
|
168
|
-
case "MethodCallExpression":
|
|
169
|
-
expr.object = stripExpr(expr.object)
|
|
170
|
-
for (let i = 0; i < expr.arguments.length; i++) expr.arguments[i] = stripExpr(expr.arguments[i])
|
|
171
|
-
return expr
|
|
172
|
-
|
|
173
|
-
case "ParenthesizedExpression":
|
|
174
|
-
expr.expression = stripExpr(expr.expression)
|
|
175
|
-
return expr
|
|
176
|
-
|
|
177
|
-
case "TypeAssertionExpression":
|
|
178
|
-
// `expr :: T` -> T를 버리고 expr만 남김 (괄호로 감싸서 우선순위 보존)
|
|
179
|
-
return stripExpr(expr.expression)
|
|
180
|
-
|
|
181
|
-
case "IfElseExpression":
|
|
182
|
-
for (const clause of expr.clauses) {
|
|
183
|
-
clause.condition = stripExpr(clause.condition)
|
|
184
|
-
clause.body = stripExpr(clause.body)
|
|
185
|
-
}
|
|
186
|
-
expr.alternate = stripExpr(expr.alternate)
|
|
187
|
-
return expr
|
|
188
|
-
|
|
189
|
-
default:
|
|
190
|
-
return expr
|
|
191
|
-
}
|
|
1
|
+
import type {
|
|
2
|
+
Program, Block, Statement, Expression, FunctionBody,
|
|
3
|
+
} from "luau-parser"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 파이프라인 맨 앞에서 실행되어야 하는 패스.
|
|
7
|
+
*
|
|
8
|
+
* 이후 패스들(GlobalMapping, RenameVariables 등)은 값 트리만 순회하고
|
|
9
|
+
* `typeof(x)`, `x :: T` 같은 타입 노드 내부의 expression은 건드리지 않는다.
|
|
10
|
+
* 타입을 나중까지 들고 있으면 값 쪽 식별자는 바뀌는데 타입 쪽 참조는 원래
|
|
11
|
+
* 이름 그대로 남아 출력물이 타입체크 불가능한 상태가 된다
|
|
12
|
+
* (`typeof(runtimeValue)`가 obfuscate 이후에도 원본 이름을 참조하는 것이 그 예).
|
|
13
|
+
*
|
|
14
|
+
* 그래서 타입 정보 자체를 파이프라인 시작 시점에 전부 지워서 이 클래스의
|
|
15
|
+
* 버그를 원천 차단한다:
|
|
16
|
+
* - TypeAliasStatement / ExportTypeAliasStatement 문 자체를 블록에서 제거
|
|
17
|
+
* - TypedIdentifier / FunctionParameter 의 typeAnnotation 제거
|
|
18
|
+
* - FunctionBody 의 generics / varargTypeAnnotation / returnType 제거
|
|
19
|
+
* - `expr :: T` (TypeAssertionExpression) 은 T를 버리고 expr로 치환
|
|
20
|
+
*/
|
|
21
|
+
export function runStripTypes(program: Program): void {
|
|
22
|
+
stripBlock(program.body)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function stripBlock(block: Block): void {
|
|
26
|
+
const kept: Statement[] = []
|
|
27
|
+
for (const stmt of block.statements) {
|
|
28
|
+
if (stmt.type === "TypeAliasStatement" || stmt.type === "ExportTypeAliasStatement") {
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
stripStatement(stmt)
|
|
32
|
+
kept.push(stmt)
|
|
33
|
+
}
|
|
34
|
+
block.statements = kept
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function stripFunctionBody(func: FunctionBody): void {
|
|
38
|
+
func.generics = []
|
|
39
|
+
for (const param of func.params) delete param.typeAnnotation
|
|
40
|
+
delete func.varargTypeAnnotation
|
|
41
|
+
delete func.returnType
|
|
42
|
+
stripBlock(func.body)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function stripStatement(stmt: Statement): void {
|
|
46
|
+
switch (stmt.type) {
|
|
47
|
+
case "LocalStatement":
|
|
48
|
+
for (const name of stmt.names) delete name.typeAnnotation
|
|
49
|
+
for (let i = 0; i < stmt.init.length; i++) stmt.init[i] = stripExpr(stmt.init[i])
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
case "LocalFunctionStatement":
|
|
53
|
+
stripFunctionBody(stmt.func)
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
case "FunctionDeclarationStatement":
|
|
57
|
+
stripFunctionBody(stmt.func)
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
case "AssignmentStatement":
|
|
61
|
+
for (let i = 0; i < stmt.targets.length; i++) stmt.targets[i] = stripExpr(stmt.targets[i])
|
|
62
|
+
for (let i = 0; i < stmt.values.length; i++) stmt.values[i] = stripExpr(stmt.values[i])
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
case "CompoundAssignmentStatement":
|
|
66
|
+
stmt.target = stripExpr(stmt.target)
|
|
67
|
+
stmt.value = stripExpr(stmt.value)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
case "CallStatement":
|
|
71
|
+
stmt.expression = stripExpr(stmt.expression) as typeof stmt.expression
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
case "DoStatement":
|
|
75
|
+
stripBlock(stmt.body)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
case "WhileStatement":
|
|
79
|
+
stmt.condition = stripExpr(stmt.condition)
|
|
80
|
+
stripBlock(stmt.body)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
case "RepeatStatement":
|
|
84
|
+
stripBlock(stmt.body)
|
|
85
|
+
stmt.condition = stripExpr(stmt.condition)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
case "IfStatement":
|
|
89
|
+
for (const clause of stmt.clauses) {
|
|
90
|
+
clause.condition = stripExpr(clause.condition)
|
|
91
|
+
stripBlock(clause.body)
|
|
92
|
+
}
|
|
93
|
+
if (stmt.alternate) stripBlock(stmt.alternate)
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
case "NumericForStatement":
|
|
97
|
+
delete stmt.variable.typeAnnotation
|
|
98
|
+
stmt.start = stripExpr(stmt.start)
|
|
99
|
+
stmt.end = stripExpr(stmt.end)
|
|
100
|
+
if (stmt.step) stmt.step = stripExpr(stmt.step)
|
|
101
|
+
stripBlock(stmt.body)
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
case "GenericForStatement":
|
|
105
|
+
for (const v of stmt.variables) delete v.typeAnnotation
|
|
106
|
+
for (let i = 0; i < stmt.iterators.length; i++) stmt.iterators[i] = stripExpr(stmt.iterators[i])
|
|
107
|
+
stripBlock(stmt.body)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
case "ReturnStatement":
|
|
111
|
+
for (let i = 0; i < stmt.arguments.length; i++) stmt.arguments[i] = stripExpr(stmt.arguments[i])
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
case "BreakStatement":
|
|
115
|
+
case "ContinueStatement":
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function stripExpr(expr: Expression): Expression {
|
|
121
|
+
switch (expr.type) {
|
|
122
|
+
case "InterpolatedStringExpression":
|
|
123
|
+
for (const part of expr.parts) {
|
|
124
|
+
if (part.kind === "expression") part.expression = stripExpr(part.expression)
|
|
125
|
+
}
|
|
126
|
+
return expr
|
|
127
|
+
|
|
128
|
+
case "FunctionExpression":
|
|
129
|
+
stripFunctionBody(expr.func)
|
|
130
|
+
return expr
|
|
131
|
+
|
|
132
|
+
case "TableExpression":
|
|
133
|
+
for (const field of expr.fields) {
|
|
134
|
+
if (field.type === "TableFieldPositional") {
|
|
135
|
+
field.value = stripExpr(field.value)
|
|
136
|
+
} else if (field.type === "TableFieldNamed") {
|
|
137
|
+
field.value = stripExpr(field.value)
|
|
138
|
+
} else if (field.type === "TableFieldComputed") {
|
|
139
|
+
field.key = stripExpr(field.key)
|
|
140
|
+
field.value = stripExpr(field.value)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return expr
|
|
144
|
+
|
|
145
|
+
case "BinaryExpression":
|
|
146
|
+
expr.left = stripExpr(expr.left)
|
|
147
|
+
expr.right = stripExpr(expr.right)
|
|
148
|
+
return expr
|
|
149
|
+
|
|
150
|
+
case "UnaryExpression":
|
|
151
|
+
expr.argument = stripExpr(expr.argument)
|
|
152
|
+
return expr
|
|
153
|
+
|
|
154
|
+
case "MemberExpression":
|
|
155
|
+
expr.object = stripExpr(expr.object)
|
|
156
|
+
return expr
|
|
157
|
+
|
|
158
|
+
case "IndexExpression":
|
|
159
|
+
expr.object = stripExpr(expr.object)
|
|
160
|
+
expr.index = stripExpr(expr.index)
|
|
161
|
+
return expr
|
|
162
|
+
|
|
163
|
+
case "CallExpression":
|
|
164
|
+
expr.callee = stripExpr(expr.callee)
|
|
165
|
+
for (let i = 0; i < expr.arguments.length; i++) expr.arguments[i] = stripExpr(expr.arguments[i])
|
|
166
|
+
return expr
|
|
167
|
+
|
|
168
|
+
case "MethodCallExpression":
|
|
169
|
+
expr.object = stripExpr(expr.object)
|
|
170
|
+
for (let i = 0; i < expr.arguments.length; i++) expr.arguments[i] = stripExpr(expr.arguments[i])
|
|
171
|
+
return expr
|
|
172
|
+
|
|
173
|
+
case "ParenthesizedExpression":
|
|
174
|
+
expr.expression = stripExpr(expr.expression)
|
|
175
|
+
return expr
|
|
176
|
+
|
|
177
|
+
case "TypeAssertionExpression":
|
|
178
|
+
// `expr :: T` -> T를 버리고 expr만 남김 (괄호로 감싸서 우선순위 보존)
|
|
179
|
+
return stripExpr(expr.expression)
|
|
180
|
+
|
|
181
|
+
case "IfElseExpression":
|
|
182
|
+
for (const clause of expr.clauses) {
|
|
183
|
+
clause.condition = stripExpr(clause.condition)
|
|
184
|
+
clause.body = stripExpr(clause.body)
|
|
185
|
+
}
|
|
186
|
+
expr.alternate = stripExpr(expr.alternate)
|
|
187
|
+
return expr
|
|
188
|
+
|
|
189
|
+
default:
|
|
190
|
+
return expr
|
|
191
|
+
}
|
|
192
192
|
}
|
package/src/passes/Vmify.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface VmifyOptions {
|
|
|
37
37
|
* Math.random. 재현 가능한 빌드가 필요하면(테스트 등) 시드 고정 PRNG를 넘길 것.
|
|
38
38
|
*/
|
|
39
39
|
random?: () => number
|
|
40
|
+
/**
|
|
41
|
+
* 디버그 빌드. 켜면 각 인스트럭션에 원본 소스 줄 번호를, CALL에는 호출 대상 이름을
|
|
42
|
+
* 함께 직렬화하고, 런타임 CALL 핸들러가 non-callable 값을 부르려 할 때 그냥
|
|
43
|
+
* "attempt to call a nil value"가 아니라 "[vmdebug] ... '대상이름' (source line N)"으로
|
|
44
|
+
* 원본 위치를 찍어준다. 산출물이 조금 커지고 VM 시그니처가 노출되므로 진단용으로만 쓸 것.
|
|
45
|
+
*/
|
|
46
|
+
debug?: boolean
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
@@ -59,10 +66,11 @@ export function runVmify(program: Program, options: VmifyOptions): void {
|
|
|
59
66
|
const builtinGlobals = options.builtinGlobals ?? DEFAULT_BUILTIN_GLOBALS
|
|
60
67
|
const random = options.random ?? Math.random
|
|
61
68
|
|
|
69
|
+
const debug = options.debug ?? false
|
|
62
70
|
const opcodeMap = createOpcodeMap(random)
|
|
63
71
|
const names = generateVmNames(random)
|
|
64
72
|
|
|
65
|
-
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap)
|
|
73
|
+
const compiler = new VmCompiler(program, builtinGlobals, opcodeMap, debug)
|
|
66
74
|
const topProto = compiler.compile()
|
|
67
75
|
|
|
68
76
|
// builtinGlobals(시드 목록)가 아니라 실제로 프로그램이 참조한 전역 전체로 브릿지를
|
|
@@ -74,12 +82,12 @@ export function runVmify(program: Program, options: VmifyOptions): void {
|
|
|
74
82
|
usedGlobalNames.map((name) => namedField(name, identifier(name))),
|
|
75
83
|
)
|
|
76
84
|
|
|
77
|
-
const runtimeSource = buildVmRuntimeSource(names, opcodeMap)
|
|
85
|
+
const runtimeSource = buildVmRuntimeSource(names, opcodeMap, debug)
|
|
78
86
|
const runtimeProgram = luauparser.parse(runtimeSource)
|
|
79
87
|
const runtimeStatements: Statement[] = runtimeProgram.body.statements
|
|
80
88
|
markRuntimeNumbersAsStructural(runtimeProgram.body)
|
|
81
89
|
|
|
82
|
-
const protoLiteral = serializeProto(topProto, names)
|
|
90
|
+
const protoLiteral = serializeProto(topProto, names, debug)
|
|
83
91
|
|
|
84
92
|
const newBody: Statement[] = [
|
|
85
93
|
localStatement(names.globals, globalsTable),
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import type { Program } from "luau-parser"
|
|
2
|
-
import {
|
|
3
|
-
block, functionBody, functionExpression, paren, call, vararg, returnStatement,
|
|
4
|
-
} from "./nodeFactory"
|
|
5
|
-
|
|
6
|
-
export interface WrapInFunctionOptions {}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 전체 프로그램을:
|
|
10
|
-
* return (function(...)
|
|
11
|
-
* <원래 코드>
|
|
12
|
-
* end)(...)
|
|
13
|
-
* 로 감쌈.
|
|
14
|
-
*
|
|
15
|
-
* - `return`을 쓰는 이유: Script/LocalScript에서는 top-level return이 그냥 청크를
|
|
16
|
-
* 조기 종료시킬 뿐 무해하고, ModuleScript라면 IIFE의 결과값이 그대로 require()
|
|
17
|
-
* 호출자에게 전달돼야 하므로 필요함. 즉 스크립트 종류를 가리지 않고 안전.
|
|
18
|
-
* - `...`을 파라미터로 받아서 다시 그대로 넘겨주는 이유: 원본 청크가 최상위에서
|
|
19
|
-
* `...`(스크립트 인자)을 참조하는 경우를 대비. 그냥 지워버리면 그런 코드가
|
|
20
|
-
* 깨짐.
|
|
21
|
-
*
|
|
22
|
-
* 반드시 파이프라인 맨 마지막 근처에서 실행돼야 함 — 이 패스 이후에 실행되는
|
|
23
|
-
* 다른 패스가 top-level 스코프를 순회/변형한다면 이미 감싸인 함수 내부까지
|
|
24
|
-
* 안 보고 지나칠 수 있음.
|
|
25
|
-
*/
|
|
26
|
-
export function runWrapInFunction(program: Program, _options: WrapInFunctionOptions): void {
|
|
27
|
-
const innerBody = program.body
|
|
28
|
-
const wrapper = functionExpression(functionBody([], innerBody, true))
|
|
29
|
-
const iife = call(paren(wrapper), [vararg()])
|
|
30
|
-
|
|
31
|
-
program.body = block([returnStatement([iife])])
|
|
1
|
+
import type { Program } from "luau-parser"
|
|
2
|
+
import {
|
|
3
|
+
block, functionBody, functionExpression, paren, call, vararg, returnStatement,
|
|
4
|
+
} from "./nodeFactory"
|
|
5
|
+
|
|
6
|
+
export interface WrapInFunctionOptions {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 전체 프로그램을:
|
|
10
|
+
* return (function(...)
|
|
11
|
+
* <원래 코드>
|
|
12
|
+
* end)(...)
|
|
13
|
+
* 로 감쌈.
|
|
14
|
+
*
|
|
15
|
+
* - `return`을 쓰는 이유: Script/LocalScript에서는 top-level return이 그냥 청크를
|
|
16
|
+
* 조기 종료시킬 뿐 무해하고, ModuleScript라면 IIFE의 결과값이 그대로 require()
|
|
17
|
+
* 호출자에게 전달돼야 하므로 필요함. 즉 스크립트 종류를 가리지 않고 안전.
|
|
18
|
+
* - `...`을 파라미터로 받아서 다시 그대로 넘겨주는 이유: 원본 청크가 최상위에서
|
|
19
|
+
* `...`(스크립트 인자)을 참조하는 경우를 대비. 그냥 지워버리면 그런 코드가
|
|
20
|
+
* 깨짐.
|
|
21
|
+
*
|
|
22
|
+
* 반드시 파이프라인 맨 마지막 근처에서 실행돼야 함 — 이 패스 이후에 실행되는
|
|
23
|
+
* 다른 패스가 top-level 스코프를 순회/변형한다면 이미 감싸인 함수 내부까지
|
|
24
|
+
* 안 보고 지나칠 수 있음.
|
|
25
|
+
*/
|
|
26
|
+
export function runWrapInFunction(program: Program, _options: WrapInFunctionOptions): void {
|
|
27
|
+
const innerBody = program.body
|
|
28
|
+
const wrapper = functionExpression(functionBody([], innerBody, true))
|
|
29
|
+
const iife = call(paren(wrapper), [vararg()])
|
|
30
|
+
|
|
31
|
+
program.body = block([returnStatement([iife])])
|
|
32
32
|
}
|