luau-obfuscator 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,7 +30,7 @@ local ${N.handlers} = {}
30
30
 
31
31
  local function ${N.dispatch}(${N.frame}, pc)
32
32
  local instr = ${N.frame}.${N.code}[pc]
33
- return ${N.handlers}[instr.${N.op}](${N.frame}, pc, instr)
33
+ return ${N.handlers}[instr[1]](${N.frame}, pc, instr)
34
34
  end
35
35
 
36
36
  local function ${N.rk}(${N.frame}, x)
@@ -43,156 +43,156 @@ end
43
43
 
44
44
  -- MOVE
45
45
  ${N.handlers}[${op(Opcode.MOVE)}] = function(${N.frame}, pc, instr)
46
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.frame}.${N.R}[instr.${N.b}]
46
+ ${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]]
47
47
  return ${N.dispatch}(${N.frame}, pc + 1)
48
48
  end
49
49
 
50
50
  -- LOADK
51
51
  ${N.handlers}[${op(Opcode.LOADK)}] = function(${N.frame}, pc, instr)
52
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.frame}.${N.K}[instr.${N.b} + 1]
52
+ ${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.K}[instr[3] + 1]
53
53
  return ${N.dispatch}(${N.frame}, pc + 1)
54
54
  end
55
55
 
56
56
  -- LOADBOOL
57
57
  ${N.handlers}[${op(Opcode.LOADBOOL)}] = function(${N.frame}, pc, instr)
58
- ${N.frame}.${N.R}[instr.${N.a}] = (instr.${N.b} ~= 0)
58
+ ${N.frame}.${N.R}[instr[2]] = (instr[3] ~= 0)
59
59
  return ${N.dispatch}(${N.frame}, pc + 1)
60
60
  end
61
61
 
62
62
  -- LOADNIL
63
63
  ${N.handlers}[${op(Opcode.LOADNIL)}] = function(${N.frame}, pc, instr)
64
- for i = instr.${N.a}, instr.${N.b} do ${N.frame}.${N.R}[i] = nil end
64
+ for i = instr[2], instr[3] do ${N.frame}.${N.R}[i] = nil end
65
65
  return ${N.dispatch}(${N.frame}, pc + 1)
66
66
  end
67
67
 
68
68
  -- GETUPVAL
69
69
  ${N.handlers}[${op(Opcode.GETUPVAL)}] = function(${N.frame}, pc, instr)
70
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.frame}.${N.upvals}[instr.${N.b} + 1]
70
+ ${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.upvals}[instr[3] + 1]
71
71
  return ${N.dispatch}(${N.frame}, pc + 1)
72
72
  end
73
73
 
74
74
  -- SETUPVAL
75
75
  ${N.handlers}[${op(Opcode.SETUPVAL)}] = function(${N.frame}, pc, instr)
76
- ${N.frame}.${N.upvals}[instr.${N.b} + 1] = ${N.frame}.${N.R}[instr.${N.a}]
76
+ ${N.frame}.${N.upvals}[instr[3] + 1] = ${N.frame}.${N.R}[instr[2]]
77
77
  return ${N.dispatch}(${N.frame}, pc + 1)
78
78
  end
79
79
 
80
80
  -- GETGLOBAL (b는 RK가 아니라 상수풀 순수 인덱스)
81
81
  ${N.handlers}[${op(Opcode.GETGLOBAL)}] = function(${N.frame}, pc, instr)
82
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.globals}[${N.frame}.${N.K}[instr.${N.b} + 1]]
82
+ ${N.frame}.${N.R}[instr[2]] = ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]]
83
83
  return ${N.dispatch}(${N.frame}, pc + 1)
84
84
  end
85
85
 
86
86
  -- SETGLOBAL
87
87
  ${N.handlers}[${op(Opcode.SETGLOBAL)}] = function(${N.frame}, pc, instr)
88
- ${N.globals}[${N.frame}.${N.K}[instr.${N.b} + 1]] = ${N.frame}.${N.R}[instr.${N.a}]
88
+ ${N.globals}[${N.frame}.${N.K}[instr[3] + 1]] = ${N.frame}.${N.R}[instr[2]]
89
89
  return ${N.dispatch}(${N.frame}, pc + 1)
90
90
  end
91
91
 
92
92
  -- GETTABLE
93
93
  ${N.handlers}[${op(Opcode.GETTABLE)}] = function(${N.frame}, pc, instr)
94
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.frame}.${N.R}[instr.${N.b}][${N.rk}(${N.frame}, instr.${N.c})]
94
+ ${N.frame}.${N.R}[instr[2]] = ${N.frame}.${N.R}[instr[3]][${N.rk}(${N.frame}, instr[4])]
95
95
  return ${N.dispatch}(${N.frame}, pc + 1)
96
96
  end
97
97
 
98
98
  -- SETTABLE
99
99
  ${N.handlers}[${op(Opcode.SETTABLE)}] = function(${N.frame}, pc, instr)
100
- ${N.frame}.${N.R}[instr.${N.a}][${N.rk}(${N.frame}, instr.${N.b})] = ${N.rk}(${N.frame}, instr.${N.c})
100
+ ${N.frame}.${N.R}[instr[2]][${N.rk}(${N.frame}, instr[3])] = ${N.rk}(${N.frame}, instr[4])
101
101
  return ${N.dispatch}(${N.frame}, pc + 1)
102
102
  end
103
103
 
104
104
  -- NEWTABLE
105
105
  ${N.handlers}[${op(Opcode.NEWTABLE)}] = function(${N.frame}, pc, instr)
106
- ${N.frame}.${N.R}[instr.${N.a}] = {}
106
+ ${N.frame}.${N.R}[instr[2]] = {}
107
107
  return ${N.dispatch}(${N.frame}, pc + 1)
108
108
  end
109
109
 
110
110
  -- SELF (R[a] = R[b][key]; R[a+1] = R[b])
111
111
  ${N.handlers}[${op(Opcode.SELF)}] = function(${N.frame}, pc, instr)
112
- local obj = ${N.frame}.${N.R}[instr.${N.b}]
113
- ${N.frame}.${N.R}[instr.${N.a}] = obj[${N.rk}(${N.frame}, instr.${N.c})]
114
- ${N.frame}.${N.R}[instr.${N.a} + 1] = obj
112
+ local obj = ${N.frame}.${N.R}[instr[3]]
113
+ ${N.frame}.${N.R}[instr[2]] = obj[${N.rk}(${N.frame}, instr[4])]
114
+ ${N.frame}.${N.R}[instr[2] + 1] = obj
115
115
  return ${N.dispatch}(${N.frame}, pc + 1)
116
116
  end
117
117
 
118
118
  -- ADD SUB MUL DIV MOD POW
119
119
  ${N.handlers}[${op(Opcode.ADD)}] = function(${N.frame}, pc, instr)
120
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) + ${N.rk}(${N.frame}, instr.${N.c})
120
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) + ${N.rk}(${N.frame}, instr[4])
121
121
  return ${N.dispatch}(${N.frame}, pc + 1)
122
122
  end
123
123
  ${N.handlers}[${op(Opcode.SUB)}] = function(${N.frame}, pc, instr)
124
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) - ${N.rk}(${N.frame}, instr.${N.c})
124
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) - ${N.rk}(${N.frame}, instr[4])
125
125
  return ${N.dispatch}(${N.frame}, pc + 1)
126
126
  end
127
127
  ${N.handlers}[${op(Opcode.MUL)}] = function(${N.frame}, pc, instr)
128
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) * ${N.rk}(${N.frame}, instr.${N.c})
128
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) * ${N.rk}(${N.frame}, instr[4])
129
129
  return ${N.dispatch}(${N.frame}, pc + 1)
130
130
  end
131
131
  ${N.handlers}[${op(Opcode.DIV)}] = function(${N.frame}, pc, instr)
132
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) / ${N.rk}(${N.frame}, instr.${N.c})
132
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) / ${N.rk}(${N.frame}, instr[4])
133
133
  return ${N.dispatch}(${N.frame}, pc + 1)
134
134
  end
135
135
  ${N.handlers}[${op(Opcode.MOD)}] = function(${N.frame}, pc, instr)
136
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) % ${N.rk}(${N.frame}, instr.${N.c})
136
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) % ${N.rk}(${N.frame}, instr[4])
137
137
  return ${N.dispatch}(${N.frame}, pc + 1)
138
138
  end
139
139
  ${N.handlers}[${op(Opcode.POW)}] = function(${N.frame}, pc, instr)
140
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) ^ ${N.rk}(${N.frame}, instr.${N.c})
140
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) ^ ${N.rk}(${N.frame}, instr[4])
141
141
  return ${N.dispatch}(${N.frame}, pc + 1)
142
142
  end
143
143
 
144
144
  -- IDIV (Luau 바닥 나눗셈)
145
145
  ${N.handlers}[${op(Opcode.IDIV)}] = function(${N.frame}, pc, instr)
146
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) // ${N.rk}(${N.frame}, instr.${N.c})
146
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) // ${N.rk}(${N.frame}, instr[4])
147
147
  return ${N.dispatch}(${N.frame}, pc + 1)
148
148
  end
149
149
 
150
150
  -- CONCAT
151
151
  ${N.handlers}[${op(Opcode.CONCAT)}] = function(${N.frame}, pc, instr)
152
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) .. ${N.rk}(${N.frame}, instr.${N.c})
152
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) .. ${N.rk}(${N.frame}, instr[4])
153
153
  return ${N.dispatch}(${N.frame}, pc + 1)
154
154
  end
155
155
 
156
156
  -- UNM
157
157
  ${N.handlers}[${op(Opcode.UNM)}] = function(${N.frame}, pc, instr)
158
- ${N.frame}.${N.R}[instr.${N.a}] = -${N.frame}.${N.R}[instr.${N.b}]
158
+ ${N.frame}.${N.R}[instr[2]] = -${N.frame}.${N.R}[instr[3]]
159
159
  return ${N.dispatch}(${N.frame}, pc + 1)
160
160
  end
161
161
 
162
162
  -- NOT
163
163
  ${N.handlers}[${op(Opcode.NOT)}] = function(${N.frame}, pc, instr)
164
- ${N.frame}.${N.R}[instr.${N.a}] = not ${N.frame}.${N.R}[instr.${N.b}]
164
+ ${N.frame}.${N.R}[instr[2]] = not ${N.frame}.${N.R}[instr[3]]
165
165
  return ${N.dispatch}(${N.frame}, pc + 1)
166
166
  end
167
167
 
168
168
  -- LEN
169
169
  ${N.handlers}[${op(Opcode.LEN)}] = function(${N.frame}, pc, instr)
170
- ${N.frame}.${N.R}[instr.${N.a}] = #${N.frame}.${N.R}[instr.${N.b}]
170
+ ${N.frame}.${N.R}[instr[2]] = #${N.frame}.${N.R}[instr[3]]
171
171
  return ${N.dispatch}(${N.frame}, pc + 1)
172
172
  end
173
173
 
174
174
  -- JMP (pc += a)
175
175
  ${N.handlers}[${op(Opcode.JMP)}] = function(${N.frame}, pc, instr)
176
- return ${N.dispatch}(${N.frame}, pc + instr.${N.a})
176
+ return ${N.dispatch}(${N.frame}, pc + instr[2])
177
177
  end
178
178
 
179
179
  -- EQ LT LE (값 생성형 비교)
180
180
  ${N.handlers}[${op(Opcode.EQ)}] = function(${N.frame}, pc, instr)
181
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) == ${N.rk}(${N.frame}, instr.${N.c})
181
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) == ${N.rk}(${N.frame}, instr[4])
182
182
  return ${N.dispatch}(${N.frame}, pc + 1)
183
183
  end
184
184
  ${N.handlers}[${op(Opcode.LT)}] = function(${N.frame}, pc, instr)
185
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) < ${N.rk}(${N.frame}, instr.${N.c})
185
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) < ${N.rk}(${N.frame}, instr[4])
186
186
  return ${N.dispatch}(${N.frame}, pc + 1)
187
187
  end
188
188
  ${N.handlers}[${op(Opcode.LE)}] = function(${N.frame}, pc, instr)
189
- ${N.frame}.${N.R}[instr.${N.a}] = ${N.rk}(${N.frame}, instr.${N.b}) <= ${N.rk}(${N.frame}, instr.${N.c})
189
+ ${N.frame}.${N.R}[instr[2]] = ${N.rk}(${N.frame}, instr[3]) <= ${N.rk}(${N.frame}, instr[4])
190
190
  return ${N.dispatch}(${N.frame}, pc + 1)
191
191
  end
192
192
 
193
193
  -- TEST (if (not R[a]) != (c!=0) then pc += 2 else pc += 1 — 보통 다음 명령은 JMP)
194
194
  ${N.handlers}[${op(Opcode.TEST)}] = function(${N.frame}, pc, instr)
195
- if (not ${N.frame}.${N.R}[instr.${N.a}]) ~= (instr.${N.c} ~= 0) then
195
+ if (not ${N.frame}.${N.R}[instr[2]]) ~= (instr[4] ~= 0) then
196
196
  return ${N.dispatch}(${N.frame}, pc + 2)
197
197
  end
198
198
  return ${N.dispatch}(${N.frame}, pc + 1)
@@ -200,7 +200,7 @@ end
200
200
 
201
201
  -- CALL
202
202
  ${N.handlers}[${op(Opcode.CALL)}] = function(${N.frame}, pc, instr)
203
- local a, b, c = instr.${N.a}, instr.${N.b}, instr.${N.c}
203
+ local a, b, c = instr[2], instr[3], instr[4]
204
204
  local fn = ${N.frame}.${N.R}[a]
205
205
  local nargs
206
206
  if b == 0 then
@@ -225,7 +225,7 @@ end
225
225
 
226
226
  -- RETURN
227
227
  ${N.handlers}[${op(Opcode.RETURN)}] = function(${N.frame}, pc, instr)
228
- local a, b = instr.${N.a}, instr.${N.b}
228
+ local a, b = instr[2], instr[3]
229
229
  if b == 0 then
230
230
  return table.unpack(${N.frame}.${N.R}, a, (${N.frame}.${N.multiTop} or (a + 1)) - 1)
231
231
  end
@@ -234,14 +234,14 @@ end
234
234
 
235
235
  -- FORPREP
236
236
  ${N.handlers}[${op(Opcode.FORPREP)}] = function(${N.frame}, pc, instr)
237
- local a = instr.${N.a}
237
+ local a = instr[2]
238
238
  ${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] - ${N.frame}.${N.R}[a + 2]
239
- return ${N.dispatch}(${N.frame}, pc + instr.${N.b})
239
+ return ${N.dispatch}(${N.frame}, pc + instr[3])
240
240
  end
241
241
 
242
242
  -- FORLOOP
243
243
  ${N.handlers}[${op(Opcode.FORLOOP)}] = function(${N.frame}, pc, instr)
244
- local a = instr.${N.a}
244
+ local a = instr[2]
245
245
  local step = ${N.frame}.${N.R}[a + 2]
246
246
  ${N.frame}.${N.R}[a] = ${N.frame}.${N.R}[a] + step
247
247
  local ok
@@ -252,26 +252,26 @@ ${N.handlers}[${op(Opcode.FORLOOP)}] = function(${N.frame}, pc, instr)
252
252
  end
253
253
  if ok then
254
254
  ${N.frame}.${N.R}[a + 3] = ${N.frame}.${N.R}[a]
255
- return ${N.dispatch}(${N.frame}, pc + instr.${N.b})
255
+ return ${N.dispatch}(${N.frame}, pc + instr[3])
256
256
  end
257
257
  return ${N.dispatch}(${N.frame}, pc + 1)
258
258
  end
259
259
 
260
260
  -- CLOSURE
261
261
  ${N.handlers}[${op(Opcode.CLOSURE)}] = function(${N.frame}, pc, instr)
262
- local childProto = ${N.frame}.${N.protos}[instr.${N.b} + 1]
262
+ local childProto = ${N.frame}.${N.protos}[instr[3] + 1]
263
263
  local n = #childProto.${N.upvalDescs}
264
264
  local capturedUpvals = table.create(n)
265
265
  for i = 1, n do
266
266
  local desc = childProto.${N.upvalDescs}[i]
267
267
  local captureInstr = ${N.frame}.${N.code}[pc + i]
268
268
  if desc.${N.kind} == "local" then
269
- capturedUpvals[i] = ${N.frame}.${N.R}[captureInstr.${N.b}]
269
+ capturedUpvals[i] = ${N.frame}.${N.R}[captureInstr[3]]
270
270
  else
271
- capturedUpvals[i] = ${N.frame}.${N.upvals}[captureInstr.${N.b} + 1]
271
+ capturedUpvals[i] = ${N.frame}.${N.upvals}[captureInstr[3] + 1]
272
272
  end
273
273
  end
274
- ${N.frame}.${N.R}[instr.${N.a}] = function(...)
274
+ ${N.frame}.${N.R}[instr[2]] = function(...)
275
275
  return ${N.execute}(childProto, capturedUpvals, ...)
276
276
  end
277
277
  return ${N.dispatch}(${N.frame}, pc + 1 + n)
@@ -279,22 +279,22 @@ end
279
279
 
280
280
  -- VARARG
281
281
  ${N.handlers}[${op(Opcode.VARARG)}] = function(${N.frame}, pc, instr)
282
- local count = instr.${N.b} == 0 and ${N.frame}.${N.nVarargs} or (instr.${N.b} - 1)
282
+ local count = instr[3] == 0 and ${N.frame}.${N.nVarargs} or (instr[3] - 1)
283
283
  for i = 1, count do
284
- ${N.frame}.${N.R}[instr.${N.a} + i - 1] = ${N.frame}.${N.varargs}[i]
284
+ ${N.frame}.${N.R}[instr[2] + i - 1] = ${N.frame}.${N.varargs}[i]
285
285
  end
286
- if instr.${N.b} == 0 then
287
- ${N.frame}.${N.multiTop} = instr.${N.a} + count
286
+ if instr[3] == 0 then
287
+ ${N.frame}.${N.multiTop} = instr[2] + count
288
288
  end
289
289
  return ${N.dispatch}(${N.frame}, pc + 1)
290
290
  end
291
291
 
292
292
  -- SETLIST
293
293
  ${N.handlers}[${op(Opcode.SETLIST)}] = function(${N.frame}, pc, instr)
294
- local tbl = ${N.frame}.${N.R}[instr.${N.a}]
295
- local valuesBase = instr.${N.b}
294
+ local tbl = ${N.frame}.${N.R}[instr[2]]
295
+ local valuesBase = instr[3]
296
296
  local top = ${N.frame}.${N.multiTop} or (valuesBase + 1)
297
- local startIdx = instr.${N.c}
297
+ local startIdx = instr[4]
298
298
  local n = top - valuesBase
299
299
  for i = 0, n - 1 do
300
300
  tbl[startIdx + i] = ${N.frame}.${N.R}[valuesBase + i]
@@ -331,4 +331,4 @@ function ${N.execute}(proto, upvals, ...)
331
331
  return ${N.dispatch}(${N.frame}, 1)
332
332
  end
333
333
  `
334
- }
334
+ }
@@ -3,7 +3,7 @@ import type { Proto, ConstValue } from "./chunk"
3
3
  import { Opcode } from "./opcodes"
4
4
  import type { VmNames } from "./names"
5
5
  import {
6
- table, positionalField, namedField, numberLiteral, stringLiteral, booleanLiteral, nilLiteral,
6
+ table, positionalField, namedField, numberLiteral, vmNumberLiteral, stringLiteral, booleanLiteral, nilLiteral,
7
7
  } from "../nodeFactory"
8
8
 
9
9
  /** proto.consts[]를 Luau 배열 리터럴로: { [1] = v1, [2] = v2, ... } (1-based는 그대로 유지) */
@@ -26,14 +26,14 @@ function serializeConstValue(v: ConstValue): Expression {
26
26
  * 테이블을 바로 찾아낼 수 있기 때문. runtime.ts가 같은 names로 instr.<key>를 읽으므로
27
27
  * 반드시 컴파일 시점에 쓴 것과 동일한 VmNames 인스턴스를 넘겨야 한다.
28
28
  */
29
- function serializeInstrs(proto: Proto, names: VmNames): Expression {
29
+ function serializeInstrs(proto: Proto): Expression {
30
30
  const fields: TableField[] = proto.code.map((instr) =>
31
31
  positionalField(
32
32
  table([
33
- namedField(names.op, numberLiteral(instr.op as number)),
34
- namedField(names.a, numberLiteral(instr.a)),
35
- namedField(names.b, numberLiteral(instr.b)),
36
- namedField(names.c, numberLiteral(instr.c)),
33
+ positionalField(vmNumberLiteral(instr.op as number)),
34
+ positionalField(vmNumberLiteral(instr.a)),
35
+ positionalField(vmNumberLiteral(instr.b)),
36
+ positionalField(vmNumberLiteral(instr.c)),
37
37
  ]),
38
38
  ))
39
39
  return table(fields)
@@ -44,7 +44,7 @@ function serializeUpvalDescs(proto: Proto, names: VmNames): Expression {
44
44
  positionalField(
45
45
  table([
46
46
  namedField(names.kind, stringLiteral(d.kind)),
47
- namedField(names.index, numberLiteral(d.index)),
47
+ namedField(names.index, vmNumberLiteral(d.index)),
48
48
  ]),
49
49
  ))
50
50
  return table(fields)
@@ -54,10 +54,10 @@ function serializeUpvalDescs(proto: Proto, names: VmNames): Expression {
54
54
  * (필드 키는 전부 names에서 온 무작위 이름). */
55
55
  export function serializeProto(proto: Proto, names: VmNames): Expression {
56
56
  const fields: TableField[] = [
57
- namedField(names.numParams, numberLiteral(proto.numParams)),
57
+ namedField(names.numParams, vmNumberLiteral(proto.numParams)),
58
58
  namedField(names.hasVarargs, booleanLiteral(proto.hasVarargs)),
59
- namedField(names.maxRegs, numberLiteral(proto.maxRegs)),
60
- namedField(names.code, serializeInstrs(proto, names)),
59
+ namedField(names.maxRegs, vmNumberLiteral(proto.maxRegs)),
60
+ namedField(names.code, serializeInstrs(proto)),
61
61
  namedField(names.consts, serializeConsts(proto.consts)),
62
62
  namedField(names.upvalDescs, serializeUpvalDescs(proto, names)),
63
63
  namedField(names.protos, table(proto.protos.map((p) => positionalField(serializeProto(p, names))))),
@@ -65,4 +65,4 @@ export function serializeProto(proto: Proto, names: VmNames): Expression {
65
65
  return table(fields)
66
66
  }
67
67
 
68
- export { Opcode }
68
+ export { Opcode }
@@ -10,79 +10,177 @@ function visitArray(arr: Expression[], visitor: ExpressionVisitor): void {
10
10
  }
11
11
  }
12
12
 
13
- export function mapExpression(expr: Expression, visitor: ExpressionVisitor): Expression {
13
+ interface ChildAccessor {
14
+ get(): Expression
15
+ set(v: Expression): void
16
+ }
17
+
18
+ /**
19
+ * expr의 "자식 Expression"들에 대한 getter/setter 목록을 반환한다.
20
+ * (FunctionExpression은 별도 처리하므로 여기서는 빈 배열을 반환한다.)
21
+ */
22
+ function getExpressionChildren(expr: Expression): ChildAccessor[] {
14
23
  switch (expr.type) {
15
- case "InterpolatedStringExpression":
24
+ case "InterpolatedStringExpression": {
25
+ const children: ChildAccessor[] = []
16
26
  for (const part of expr.parts) {
17
27
  if (part.kind === "expression") {
18
- part.expression = mapExpression(part.expression, visitor)
28
+ children.push({
29
+ get: () => part.expression,
30
+ set: (v) => { part.expression = v },
31
+ })
19
32
  }
20
33
  }
21
- break
34
+ return children
35
+ }
22
36
 
23
37
  case "FunctionExpression":
24
- walkFunctionBody(expr.func, visitor)
25
- break
38
+ return []
26
39
 
27
- case "TableExpression":
40
+ case "TableExpression": {
41
+ const children: ChildAccessor[] = []
28
42
  for (const field of expr.fields) {
29
43
  if (field.type === "TableFieldPositional") {
30
- field.value = mapExpression(field.value, visitor)
44
+ children.push({ get: () => field.value, set: (v) => { field.value = v } })
31
45
  } else if (field.type === "TableFieldNamed") {
32
- field.value = mapExpression(field.value, visitor)
46
+ children.push({ get: () => field.value, set: (v) => { field.value = v } })
33
47
  } else if (field.type === "TableFieldComputed") {
34
- field.key = mapExpression(field.key, visitor)
35
- field.value = mapExpression(field.value, visitor)
48
+ children.push({ get: () => field.key, set: (v) => { field.key = v } })
49
+ children.push({ get: () => field.value, set: (v) => { field.value = v } })
36
50
  }
37
51
  }
38
- break
52
+ return children
53
+ }
39
54
 
40
55
  case "BinaryExpression":
41
- expr.left = mapExpression(expr.left, visitor)
42
- expr.right = mapExpression(expr.right, visitor)
43
- break
56
+ return [
57
+ { get: () => expr.left, set: (v) => { expr.left = v } },
58
+ { get: () => expr.right, set: (v) => { expr.right = v } },
59
+ ]
44
60
 
45
61
  case "UnaryExpression":
46
- expr.argument = mapExpression(expr.argument, visitor)
47
- break
62
+ return [{ get: () => expr.argument, set: (v) => { expr.argument = v } }]
48
63
 
49
64
  case "MemberExpression":
50
- expr.object = mapExpression(expr.object, visitor)
51
- break
65
+ return [{ get: () => expr.object, set: (v) => { expr.object = v } }]
52
66
 
53
67
  case "IndexExpression":
54
- expr.object = mapExpression(expr.object, visitor)
55
- expr.index = mapExpression(expr.index, visitor)
56
- break
57
-
58
- case "CallExpression":
59
- expr.callee = mapExpression(expr.callee, visitor)
60
- visitArray(expr.arguments, visitor)
61
- break
62
-
63
- case "MethodCallExpression":
64
- expr.object = mapExpression(expr.object, visitor)
65
- visitArray(expr.arguments, visitor)
66
- break
68
+ return [
69
+ { get: () => expr.object, set: (v) => { expr.object = v } },
70
+ { get: () => expr.index, set: (v) => { expr.index = v } },
71
+ ]
72
+
73
+ case "CallExpression": {
74
+ const children: ChildAccessor[] = [
75
+ { get: () => expr.callee, set: (v) => { expr.callee = v } },
76
+ ]
77
+ for (let i = 0; i < expr.arguments.length; i++) {
78
+ const idx = i
79
+ children.push({
80
+ get: () => expr.arguments[idx],
81
+ set: (v) => { expr.arguments[idx] = v },
82
+ })
83
+ }
84
+ return children
85
+ }
86
+
87
+ case "MethodCallExpression": {
88
+ const children: ChildAccessor[] = [
89
+ { get: () => expr.object, set: (v) => { expr.object = v } },
90
+ ]
91
+ for (let i = 0; i < expr.arguments.length; i++) {
92
+ const idx = i
93
+ children.push({
94
+ get: () => expr.arguments[idx],
95
+ set: (v) => { expr.arguments[idx] = v },
96
+ })
97
+ }
98
+ return children
99
+ }
67
100
 
68
101
  case "ParenthesizedExpression":
69
- expr.expression = mapExpression(expr.expression, visitor)
70
- break
102
+ return [{ get: () => expr.expression, set: (v) => { expr.expression = v } }]
71
103
 
72
104
  case "TypeAssertionExpression":
73
- expr.expression = mapExpression(expr.expression, visitor)
74
- break
105
+ return [{ get: () => expr.expression, set: (v) => { expr.expression = v } }]
75
106
 
76
- case "IfElseExpression":
107
+ case "IfElseExpression": {
108
+ const children: ChildAccessor[] = []
77
109
  for (const clause of expr.clauses) {
78
- clause.condition = mapExpression(clause.condition, visitor)
79
- clause.body = mapExpression(clause.body, visitor)
110
+ children.push({ get: () => clause.condition, set: (v) => { clause.condition = v } })
111
+ children.push({ get: () => clause.body, set: (v) => { clause.body = v } })
112
+ }
113
+ children.push({ get: () => expr.alternate, set: (v) => { expr.alternate = v } })
114
+ return children
115
+ }
116
+
117
+ default:
118
+ return []
119
+ }
120
+ }
121
+
122
+ interface WalkFrame {
123
+ expr: Expression
124
+ children: ChildAccessor[]
125
+ index: number
126
+ setInParent: ((v: Expression) => void) | null
127
+ }
128
+
129
+ /**
130
+ * Expression 트리를 후위순회(post-order)로 변형한다.
131
+ *
132
+ * 원래는 재귀 함수였지만, 문자열/숫자 난독화 패스가 만들어내는 매우 깊은
133
+ * BinaryExpression 체인(예: 수천 개의 `..` concat)을 순회할 때 네이티브
134
+ * 콜스택이 트리 깊이만큼 쌓여 "Maximum call stack size exceeded"가
135
+ * 발생했다. 그래서 재귀 대신 힙에 할당되는 명시적 스택을 사용해
136
+ * 트리 깊이와 무관하게 동작하도록 구현했다.
137
+ */
138
+ export function mapExpression(root: Expression, visitor: ExpressionVisitor): Expression {
139
+ // FunctionExpression은 본문(statement 트리)을 별도로 순회해야 하므로 특별 취급한다.
140
+ // 함수 중첩 깊이는 보통 얕아서(재귀해도 안전) 그대로 재귀를 사용한다.
141
+ if (root.type === "FunctionExpression") {
142
+ walkFunctionBody(root.func, visitor)
143
+ return visitor(root) ?? root
144
+ }
145
+
146
+ let result: Expression = root
147
+ const stack: WalkFrame[] = [{
148
+ expr: root,
149
+ children: getExpressionChildren(root),
150
+ index: 0,
151
+ setInParent: (v) => { result = v },
152
+ }]
153
+
154
+ while (stack.length > 0) {
155
+ const frame = stack[stack.length - 1]
156
+
157
+ if (frame.index < frame.children.length) {
158
+ const child = frame.children[frame.index]
159
+ frame.index++
160
+ const childExpr = child.get()
161
+
162
+ if (childExpr.type === "FunctionExpression") {
163
+ walkFunctionBody(childExpr.func, visitor)
164
+ child.set(visitor(childExpr) ?? childExpr)
165
+ continue
80
166
  }
81
- expr.alternate = mapExpression(expr.alternate, visitor)
82
- break
167
+
168
+ stack.push({
169
+ expr: childExpr,
170
+ children: getExpressionChildren(childExpr),
171
+ index: 0,
172
+ setInParent: child.set,
173
+ })
174
+ continue
175
+ }
176
+
177
+ // 모든 자식 처리가 끝났으면 visitor를 실행하고, 부모에 반영한 뒤 pop한다.
178
+ const finished = visitor(frame.expr) ?? frame.expr
179
+ frame.setInParent?.(finished)
180
+ stack.pop()
83
181
  }
84
182
 
85
- return visitor(expr) ?? expr
183
+ return result
86
184
  }
87
185
 
88
186
  function walkFunctionBody(func: FunctionBody, visitor: ExpressionVisitor): void {
@@ -170,4 +268,4 @@ function walkStatement(stmt: Statement, visitor: ExpressionVisitor): void {
170
268
  /** Program 전체를 순회하며 모든 Expression 노드에 visitor를 적용(제자리 변형). */
171
269
  export function transformExpressions(program: Program, visitor: ExpressionVisitor): void {
172
270
  walkBlock(program.body, visitor)
173
- }
271
+ }
package/src/pipeline.ts CHANGED
@@ -17,10 +17,10 @@ type PassFn = (program: Program, options: any) => void
17
17
  export const PASS_ORDER: (keyof ObfuscateConfig)[] = [
18
18
  "Vmify",
19
19
  "GlobalMapping",
20
- "StringsToExpressions",
21
- "NumbersToExpressions",
22
20
  "RenameVariables",
23
21
  "ConstantArray",
22
+ "StringsToExpressions",
23
+ "NumbersToExpressions",
24
24
  "EncryptStrings",
25
25
  "EncryptNumbers",
26
26
  "InsertJunk",
@@ -38,13 +38,10 @@ export const PASS_MAP: Partial<Record<keyof ObfuscateConfig, PassFn>> = {
38
38
  EncryptNumbers: runEncryptNumbers,
39
39
  InsertJunk: runInsertJunk,
40
40
  WrapInFunction: runWrapInFunction,
41
- Vmify: runVmify
41
+ Vmify: runVmify,
42
42
  }
43
43
 
44
44
  export function runPipeline(program: Program, config: ObfuscateConfig): void {
45
- // 타입 주석/별칭은 값 트리와 독립적으로 원본 이름을 참조하고 있어서
46
- // (예: typeof(x)) 어떤 pass보다도 먼저 지워야 함. 끄면 무조건 깨지므로
47
- // config로 조절 가능한 옵션이 아니라 파이프라인 진입 시 항상 실행.
48
45
  runStripTypes(program)
49
46
 
50
47
  for (const key of PASS_ORDER) {