swf-toolkit 0.0.0-stage → 0.1.0

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.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -2
  3. package/README.tr.md +126 -0
  4. package/dist/abc/abc-file.d.ts +168 -0
  5. package/dist/abc/abc-file.js +656 -0
  6. package/dist/abc/analysis.d.ts +36 -0
  7. package/dist/abc/analysis.js +213 -0
  8. package/dist/abc/code.d.ts +59 -0
  9. package/dist/abc/code.js +379 -0
  10. package/dist/abc/constants.d.ts +80 -0
  11. package/dist/abc/constants.js +93 -0
  12. package/dist/abc/interner.d.ts +46 -0
  13. package/dist/abc/interner.js +168 -0
  14. package/dist/abc/model.d.ts +40 -0
  15. package/dist/abc/model.js +146 -0
  16. package/dist/abc/opcodes.d.ts +39 -0
  17. package/dist/abc/opcodes.js +196 -0
  18. package/dist/abc/pcode/assembler.d.ts +42 -0
  19. package/dist/abc/pcode/assembler.js +635 -0
  20. package/dist/abc/pcode/disassembler.d.ts +17 -0
  21. package/dist/abc/pcode/disassembler.js +147 -0
  22. package/dist/abc/pcode/format.d.ts +23 -0
  23. package/dist/abc/pcode/format.js +131 -0
  24. package/dist/abc/pcode/lexer.d.ts +34 -0
  25. package/dist/abc/pcode/lexer.js +183 -0
  26. package/dist/assets/assets.d.ts +26 -0
  27. package/dist/assets/assets.js +151 -0
  28. package/dist/assets/images.d.ts +31 -0
  29. package/dist/assets/images.js +238 -0
  30. package/dist/assets/png.d.ts +12 -0
  31. package/dist/assets/png.js +196 -0
  32. package/dist/assets/sounds.d.ts +37 -0
  33. package/dist/assets/sounds.js +236 -0
  34. package/dist/avm1/avm1.d.ts +55 -0
  35. package/dist/avm1/avm1.js +642 -0
  36. package/dist/avm1/project.d.ts +16 -0
  37. package/dist/avm1/project.js +81 -0
  38. package/dist/cjs/abc/abc-file.d.ts +168 -0
  39. package/dist/cjs/abc/abc-file.js +663 -0
  40. package/dist/cjs/abc/analysis.d.ts +36 -0
  41. package/dist/cjs/abc/analysis.js +221 -0
  42. package/dist/cjs/abc/code.d.ts +59 -0
  43. package/dist/cjs/abc/code.js +387 -0
  44. package/dist/cjs/abc/constants.d.ts +80 -0
  45. package/dist/cjs/abc/constants.js +97 -0
  46. package/dist/cjs/abc/interner.d.ts +46 -0
  47. package/dist/cjs/abc/interner.js +172 -0
  48. package/dist/cjs/abc/model.d.ts +40 -0
  49. package/dist/cjs/abc/model.js +155 -0
  50. package/dist/cjs/abc/opcodes.d.ts +39 -0
  51. package/dist/cjs/abc/opcodes.js +201 -0
  52. package/dist/cjs/abc/pcode/assembler.d.ts +42 -0
  53. package/dist/cjs/abc/pcode/assembler.js +640 -0
  54. package/dist/cjs/abc/pcode/disassembler.d.ts +17 -0
  55. package/dist/cjs/abc/pcode/disassembler.js +152 -0
  56. package/dist/cjs/abc/pcode/format.d.ts +23 -0
  57. package/dist/cjs/abc/pcode/format.js +141 -0
  58. package/dist/cjs/abc/pcode/lexer.d.ts +34 -0
  59. package/dist/cjs/abc/pcode/lexer.js +188 -0
  60. package/dist/cjs/assets/assets.d.ts +26 -0
  61. package/dist/cjs/assets/assets.js +159 -0
  62. package/dist/cjs/assets/images.d.ts +31 -0
  63. package/dist/cjs/assets/images.js +246 -0
  64. package/dist/cjs/assets/png.d.ts +12 -0
  65. package/dist/cjs/assets/png.js +202 -0
  66. package/dist/cjs/assets/sounds.d.ts +37 -0
  67. package/dist/cjs/assets/sounds.js +246 -0
  68. package/dist/cjs/avm1/avm1.d.ts +55 -0
  69. package/dist/cjs/avm1/avm1.js +649 -0
  70. package/dist/cjs/avm1/project.d.ts +16 -0
  71. package/dist/cjs/avm1/project.js +88 -0
  72. package/dist/cjs/compiler/ast.d.ts +285 -0
  73. package/dist/cjs/compiler/ast.js +3 -0
  74. package/dist/cjs/compiler/class-compiler.d.ts +34 -0
  75. package/dist/cjs/compiler/class-compiler.js +440 -0
  76. package/dist/cjs/compiler/codegen.d.ts +144 -0
  77. package/dist/cjs/compiler/codegen.js +1589 -0
  78. package/dist/cjs/compiler/lexer.d.ts +27 -0
  79. package/dist/cjs/compiler/lexer.js +254 -0
  80. package/dist/cjs/compiler/parser.d.ts +58 -0
  81. package/dist/cjs/compiler/parser.js +989 -0
  82. package/dist/cjs/compiler/program.d.ts +29 -0
  83. package/dist/cjs/compiler/program.js +36 -0
  84. package/dist/cjs/compiler/scope.d.ts +22 -0
  85. package/dist/cjs/compiler/scope.js +242 -0
  86. package/dist/cjs/compression/lzma.d.ts +37 -0
  87. package/dist/cjs/compression/lzma.js +575 -0
  88. package/dist/cjs/compression/swf-container.d.ts +19 -0
  89. package/dist/cjs/compression/swf-container.js +92 -0
  90. package/dist/cjs/decompiler/ast.d.ts +228 -0
  91. package/dist/cjs/decompiler/ast.js +455 -0
  92. package/dist/cjs/decompiler/cfg.d.ts +40 -0
  93. package/dist/cjs/decompiler/cfg.js +236 -0
  94. package/dist/cjs/decompiler/class-printer.d.ts +9 -0
  95. package/dist/cjs/decompiler/class-printer.js +173 -0
  96. package/dist/cjs/decompiler/method.d.ts +41 -0
  97. package/dist/cjs/decompiler/method.js +436 -0
  98. package/dist/cjs/decompiler/simulate.d.ts +55 -0
  99. package/dist/cjs/decompiler/simulate.js +591 -0
  100. package/dist/cjs/decompiler/structure.d.ts +48 -0
  101. package/dist/cjs/decompiler/structure.js +764 -0
  102. package/dist/cjs/errors.d.ts +11 -0
  103. package/dist/cjs/errors.js +25 -0
  104. package/dist/cjs/index.d.ts +41 -0
  105. package/dist/cjs/index.js +82 -0
  106. package/dist/cjs/io/reader.d.ts +48 -0
  107. package/dist/cjs/io/reader.js +180 -0
  108. package/dist/cjs/io/writer.d.ts +37 -0
  109. package/dist/cjs/io/writer.js +175 -0
  110. package/dist/cjs/package.json +1 -0
  111. package/dist/cjs/project/pcode-project.d.ts +32 -0
  112. package/dist/cjs/project/pcode-project.js +210 -0
  113. package/dist/cjs/project/source-export.d.ts +12 -0
  114. package/dist/cjs/project/source-export.js +51 -0
  115. package/dist/cjs/project/source-import.d.ts +13 -0
  116. package/dist/cjs/project/source-import.js +73 -0
  117. package/dist/cjs/swf/swf.d.ts +63 -0
  118. package/dist/cjs/swf/swf.js +147 -0
  119. package/dist/cjs/swf/tag-codes.d.ts +77 -0
  120. package/dist/cjs/swf/tag-codes.js +109 -0
  121. package/dist/cjs/swf/tags.d.ts +118 -0
  122. package/dist/cjs/swf/tags.js +330 -0
  123. package/dist/cli.d.ts +2 -0
  124. package/dist/cli.js +296 -0
  125. package/dist/compiler/ast.d.ts +285 -0
  126. package/dist/compiler/ast.js +2 -0
  127. package/dist/compiler/class-compiler.d.ts +34 -0
  128. package/dist/compiler/class-compiler.js +437 -0
  129. package/dist/compiler/codegen.d.ts +144 -0
  130. package/dist/compiler/codegen.js +1585 -0
  131. package/dist/compiler/lexer.d.ts +27 -0
  132. package/dist/compiler/lexer.js +249 -0
  133. package/dist/compiler/parser.d.ts +58 -0
  134. package/dist/compiler/parser.js +984 -0
  135. package/dist/compiler/program.d.ts +29 -0
  136. package/dist/compiler/program.js +32 -0
  137. package/dist/compiler/scope.d.ts +22 -0
  138. package/dist/compiler/scope.js +238 -0
  139. package/dist/compression/lzma.d.ts +37 -0
  140. package/dist/compression/lzma.js +569 -0
  141. package/dist/compression/swf-container.d.ts +19 -0
  142. package/dist/compression/swf-container.js +87 -0
  143. package/dist/decompiler/ast.d.ts +228 -0
  144. package/dist/decompiler/ast.js +441 -0
  145. package/dist/decompiler/cfg.d.ts +40 -0
  146. package/dist/decompiler/cfg.js +231 -0
  147. package/dist/decompiler/class-printer.d.ts +9 -0
  148. package/dist/decompiler/class-printer.js +170 -0
  149. package/dist/decompiler/method.d.ts +41 -0
  150. package/dist/decompiler/method.js +427 -0
  151. package/dist/decompiler/simulate.d.ts +55 -0
  152. package/dist/decompiler/simulate.js +586 -0
  153. package/dist/decompiler/structure.d.ts +48 -0
  154. package/dist/decompiler/structure.js +760 -0
  155. package/dist/errors.d.ts +11 -0
  156. package/dist/errors.js +20 -0
  157. package/dist/index.d.ts +41 -0
  158. package/dist/index.js +49 -0
  159. package/dist/io/reader.d.ts +48 -0
  160. package/dist/io/reader.js +176 -0
  161. package/dist/io/writer.d.ts +37 -0
  162. package/dist/io/writer.js +169 -0
  163. package/dist/project/pcode-project.d.ts +32 -0
  164. package/dist/project/pcode-project.js +203 -0
  165. package/dist/project/source-export.d.ts +12 -0
  166. package/dist/project/source-export.js +47 -0
  167. package/dist/project/source-import.d.ts +13 -0
  168. package/dist/project/source-import.js +70 -0
  169. package/dist/swf/swf.d.ts +63 -0
  170. package/dist/swf/swf.js +143 -0
  171. package/dist/swf/tag-codes.d.ts +77 -0
  172. package/dist/swf/tag-codes.js +105 -0
  173. package/dist/swf/tags.d.ts +118 -0
  174. package/dist/swf/tags.js +316 -0
  175. package/package.json +71 -4
@@ -0,0 +1,231 @@
1
+ import { stackEffect } from '../abc/analysis.js';
2
+ import { opInfo } from '../abc/opcodes.js';
3
+ export function buildCfg(abc, items, exceptions) {
4
+ const blocks = [];
5
+ const labelBlock = new Map();
6
+ let cur = null;
7
+ const newBlock = () => {
8
+ const b = { id: blocks.length, insns: [], labels: [], kind: 'fall', succs: [], preds: [], entryHeight: -1 };
9
+ blocks.push(b);
10
+ return b;
11
+ };
12
+ for (const it of items) {
13
+ if (it.type === 'label') {
14
+ if (!cur || cur.insns.length > 0)
15
+ cur = newBlock();
16
+ cur.labels.push(it.id);
17
+ labelBlock.set(it.id, cur.id);
18
+ continue;
19
+ }
20
+ if (!cur)
21
+ cur = newBlock();
22
+ if (it.type === 'bytes') {
23
+ if (cur.insns.length > 0)
24
+ cur = newBlock();
25
+ cur.kind = 'raw';
26
+ cur = null;
27
+ continue;
28
+ }
29
+ cur.insns.push(it);
30
+ const flow = opInfo(it.op)?.flow;
31
+ if (flow) {
32
+ cur.kind = flow === 'return' ? 'return' : flow === 'throw' ? 'throw' : flow;
33
+ cur = null;
34
+ }
35
+ }
36
+ if (blocks.length === 0)
37
+ newBlock().kind = 'end';
38
+ // Successors
39
+ for (let i = 0; i < blocks.length; i++) {
40
+ const b = blocks[i];
41
+ const last = b.insns[b.insns.length - 1];
42
+ const next = i + 1 < blocks.length ? i + 1 : -1;
43
+ switch (b.kind) {
44
+ case 'fall':
45
+ if (next >= 0)
46
+ b.succs = [next];
47
+ else
48
+ b.kind = 'end';
49
+ break;
50
+ case 'jump':
51
+ b.succs = [labelBlock.get(last.targets[0])];
52
+ break;
53
+ case 'cond':
54
+ b.succs = [labelBlock.get(last.targets[0]), ...(next >= 0 ? [next] : [])];
55
+ if (b.succs.length < 2)
56
+ b.succs.push(b.succs[0]);
57
+ break;
58
+ case 'switch':
59
+ b.succs = last.targets.map((t) => labelBlock.get(t));
60
+ break;
61
+ default:
62
+ break;
63
+ }
64
+ }
65
+ for (const b of blocks)
66
+ for (const s of new Set(b.succs))
67
+ blocks[s].preds.push(b.id);
68
+ // Stack heights
69
+ const handlerBlocks = exceptions.map((e) => labelBlock.get(e.target)).filter((x) => x !== undefined);
70
+ const work = [[0, 0], ...handlerBlocks.map((h) => [h, 1])];
71
+ while (work.length) {
72
+ const [id, h] = work.pop();
73
+ const b = blocks[id];
74
+ if (b.entryHeight >= 0)
75
+ continue;
76
+ b.entryHeight = h;
77
+ let height = h;
78
+ for (const ins of b.insns) {
79
+ const { pop, push } = stackEffect(abc, ins);
80
+ height = Math.max(0, height - pop) + push;
81
+ }
82
+ for (const s of b.succs)
83
+ work.push([s, height]);
84
+ }
85
+ // Drop edges from unreachable (dead) blocks so they do not disturb structuring.
86
+ const handlerSet = new Set(handlerBlocks);
87
+ for (const b of blocks) {
88
+ if (b.entryHeight >= 0 || handlerSet.has(b.id))
89
+ continue;
90
+ for (const s of b.succs) {
91
+ const t = blocks[s];
92
+ t.preds = t.preds.filter((p) => p !== b.id);
93
+ }
94
+ b.succs = [];
95
+ }
96
+ // Dominators (Cooper, Harvey & Kennedy) with exception edges from try starts.
97
+ const extraSuccs = new Map();
98
+ for (const e of exceptions) {
99
+ const from = labelBlock.get(e.from);
100
+ const target = labelBlock.get(e.target);
101
+ if (from === undefined || target === undefined)
102
+ continue;
103
+ // Every block inside the try range may throw; using the first one is
104
+ // enough for reachability and keeps handlers dominated by the try start.
105
+ extraSuccs.set(from, [...(extraSuccs.get(from) ?? []), target]);
106
+ }
107
+ const succsOf = (id) => [...blocks[id].succs, ...(extraSuccs.get(id) ?? [])];
108
+ const predsOf = blocks.map(() => []);
109
+ for (const b of blocks)
110
+ for (const s of new Set(succsOf(b.id)))
111
+ predsOf[s].push(b.id);
112
+ const order = rpo(blocks.length, [0], succsOf);
113
+ const idom = dominators(blocks.length, 0, order, (id) => predsOf[id]);
114
+ // Post-dominators: reverse graph with a virtual exit node (index n).
115
+ const n = blocks.length;
116
+ const exitPreds = [];
117
+ for (const b of blocks)
118
+ if (b.succs.length === 0)
119
+ exitPreds.push(b.id);
120
+ const rsuccs = (id) => (id === n ? exitPreds : blocks[id].preds);
121
+ const rpreds = (id) => {
122
+ if (id === n)
123
+ return [];
124
+ const b = blocks[id];
125
+ return b.succs.length === 0 ? [n] : [...new Set(b.succs)];
126
+ };
127
+ const rorder = rpo(n + 1, [n], rsuccs);
128
+ const pdom = dominators(n + 1, n, rorder, rpreds);
129
+ const ipdom = blocks.map((b) => {
130
+ const p = pdom[b.id];
131
+ return p === n || p === undefined ? -1 : p;
132
+ });
133
+ return { blocks, labelBlock, exceptions: [...exceptions], idom, ipdom, order };
134
+ }
135
+ function rpo(n, roots, succs) {
136
+ const order = new Array(n).fill(-1);
137
+ const visited = new Uint8Array(n);
138
+ const post = [];
139
+ for (const root of roots) {
140
+ const stack = [[root, 0]];
141
+ visited[root] = 1;
142
+ while (stack.length) {
143
+ const top = stack[stack.length - 1];
144
+ const ss = succs(top[0]);
145
+ if (top[1] < ss.length) {
146
+ const s = ss[top[1]++];
147
+ if (!visited[s]) {
148
+ visited[s] = 1;
149
+ stack.push([s, 0]);
150
+ }
151
+ }
152
+ else {
153
+ post.push(top[0]);
154
+ stack.pop();
155
+ }
156
+ }
157
+ }
158
+ post.reverse().forEach((id, i) => (order[id] = i));
159
+ return order;
160
+ }
161
+ function dominators(n, root, order, preds) {
162
+ const idom = new Array(n).fill(-1);
163
+ idom[root] = root;
164
+ const nodes = [...Array(n).keys()].filter((i) => order[i] >= 0).sort((a, b) => order[a] - order[b]);
165
+ const intersect = (a, b) => {
166
+ while (a !== b) {
167
+ while (order[a] > order[b])
168
+ a = idom[a];
169
+ while (order[b] > order[a])
170
+ b = idom[b];
171
+ }
172
+ return a;
173
+ };
174
+ let changed = true;
175
+ while (changed) {
176
+ changed = false;
177
+ for (const b of nodes) {
178
+ if (b === root)
179
+ continue;
180
+ let nd = -1;
181
+ for (const p of preds(b)) {
182
+ if (idom[p] === -1 || order[p] < 0)
183
+ continue;
184
+ nd = nd === -1 ? p : intersect(p, nd);
185
+ }
186
+ if (nd !== -1 && idom[b] !== nd) {
187
+ idom[b] = nd;
188
+ changed = true;
189
+ }
190
+ }
191
+ }
192
+ idom[root] = -1;
193
+ return idom;
194
+ }
195
+ /** True if `a` dominates `b`. */
196
+ export function dominates(cfg, a, b) {
197
+ let x = b;
198
+ for (let guard = 0; x !== -1 && guard < 100000; guard++) {
199
+ if (x === a)
200
+ return true;
201
+ x = cfg.idom[x];
202
+ }
203
+ return false;
204
+ }
205
+ /** Natural loops keyed by header block. */
206
+ export function findLoops(cfg) {
207
+ const loops = new Map();
208
+ for (const b of cfg.blocks) {
209
+ for (const s of b.succs) {
210
+ if (!dominates(cfg, s, b.id))
211
+ continue;
212
+ let loop = loops.get(s);
213
+ if (!loop) {
214
+ loop = { header: s, body: new Set([s]), latches: [] };
215
+ loops.set(s, loop);
216
+ }
217
+ if (!loop.latches.includes(b.id))
218
+ loop.latches.push(b.id);
219
+ const stack = [b.id];
220
+ while (stack.length) {
221
+ const x = stack.pop();
222
+ if (loop.body.has(x))
223
+ continue;
224
+ loop.body.add(x);
225
+ for (const p of cfg.blocks[x].preds)
226
+ stack.push(p);
227
+ }
228
+ }
229
+ }
230
+ return loops;
231
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Prints a whole AS3 class (package, imports, fields, methods) from ABC.
3
+ */
4
+ import type { AbcFile } from '../abc/abc-file.js';
5
+ export interface DecompileClassOptions {
6
+ indentUnit?: string;
7
+ }
8
+ /** Decompiles class `classIndex` to AS3 source text. */
9
+ export declare function decompileClass(abc: AbcFile, classIndex: number, options?: DecompileClassOptions): string;
@@ -0,0 +1,170 @@
1
+ import { InstanceFlags, NamespaceKind, TraitAttr, TraitKind } from '../abc/constants.js';
2
+ import { scriptOfClass } from '../abc/model.js';
3
+ import { decompileMethodBody, decompileMethodStatements, formatDefault, formatParams, typeNameOf } from './method.js';
4
+ function modifier(abc, mn, isInterface) {
5
+ if (isInterface)
6
+ return '';
7
+ const m = abc.multinames[mn];
8
+ const ns = m ? abc.namespaces[m.ns] : undefined;
9
+ switch (ns?.kind) {
10
+ case NamespaceKind.PrivateNs:
11
+ return 'private ';
12
+ case NamespaceKind.ProtectedNamespace:
13
+ case NamespaceKind.StaticProtectedNs:
14
+ return 'protected ';
15
+ case NamespaceKind.PackageInternalNs:
16
+ return 'internal ';
17
+ case NamespaceKind.Namespace: {
18
+ const uri = abc.strings[ns.name] ?? '';
19
+ const tail = uri.split(/[/:]/).filter(Boolean).pop() ?? 'ns';
20
+ return `${/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(tail) ? tail : 'ns'} `;
21
+ }
22
+ default:
23
+ return 'public ';
24
+ }
25
+ }
26
+ function metadataLines(abc, t, indent) {
27
+ const out = [];
28
+ for (const mi of t.metadata) {
29
+ const md = abc.metadata[mi];
30
+ if (!md)
31
+ continue;
32
+ const items = md.keys.map((k, i) => {
33
+ const v = JSON.stringify(abc.strings[md.values[i]] ?? '');
34
+ return k ? `${abc.strings[k]}=${v}` : v;
35
+ });
36
+ out.push(`${indent}[${abc.strings[md.name]}${items.length ? `(${items.join(',')})` : ''}]`);
37
+ }
38
+ return out;
39
+ }
40
+ function traitLines(env, t, isStatic, isInterface, indent, initText) {
41
+ const { abc } = env;
42
+ const name = abc.multinameName(t.name);
43
+ const mods = `${t.attrs & TraitAttr.Override ? 'override ' : ''}${t.attrs & TraitAttr.Final && !isStatic ? 'final ' : ''}${modifier(abc, t.name, isInterface)}${isStatic ? 'static ' : ''}`;
44
+ const out = metadataLines(abc, t, indent);
45
+ switch (t.kind) {
46
+ case TraitKind.Slot:
47
+ case TraitKind.Const: {
48
+ const kw = t.kind === TraitKind.Const ? 'const' : 'var';
49
+ const init = t.valueIndex ? ` = ${formatDefault(abc, t.valueKind, t.valueIndex)}` : initText !== undefined ? ` = ${initText}` : '';
50
+ out.push(`${indent}${mods}${kw} ${name}:${typeNameOf(env, t.typeName)}${init};`);
51
+ return out;
52
+ }
53
+ case TraitKind.Method:
54
+ case TraitKind.Getter:
55
+ case TraitKind.Setter:
56
+ case TraitKind.Function: {
57
+ const accessor = t.kind === TraitKind.Getter ? 'get ' : t.kind === TraitKind.Setter ? 'set ' : '';
58
+ const m = abc.methods[t.method];
59
+ const ret = m ? typeNameOf(env, m.returnType) : '*';
60
+ const sig = `${indent}${mods}function ${accessor}${name}(${formatParams(env, t.method)}):${ret}`;
61
+ if (isInterface || !abc.methodBody(t.method)) {
62
+ out.push(`${sig};`);
63
+ return out;
64
+ }
65
+ const body = decompileMethodBody(env, t.method, indent);
66
+ out.push(sig, `${indent}{`, ...body.lines, `${indent}}`);
67
+ return out;
68
+ }
69
+ case TraitKind.Class:
70
+ out.push(`${indent}// class ${name}`);
71
+ return out;
72
+ }
73
+ return out;
74
+ }
75
+ /** Decompiles class `classIndex` to AS3 source text. */
76
+ export function decompileClass(abc, classIndex, options = {}) {
77
+ const unit = options.indentUnit ?? ' ';
78
+ const inst = abc.instances[classIndex];
79
+ const cls = abc.classes[classIndex];
80
+ if (!inst || !cls)
81
+ throw new Error(`Class index ${classIndex} out of range`);
82
+ const qualified = abc.className(classIndex);
83
+ const dot = qualified.lastIndexOf('.');
84
+ const pkg = dot >= 0 ? qualified.slice(0, dot) : '';
85
+ const shortName = abc.multinameName(inst.name);
86
+ const isInterface = (inst.flags & InstanceFlags.Interface) !== 0;
87
+ const script = scriptOfClass(abc, classIndex);
88
+ const imports = new Set();
89
+ const env = {
90
+ abc,
91
+ thisTraits: inst.traits,
92
+ globalTraits: script >= 0 ? abc.scripts[script].traits : [],
93
+ imports,
94
+ currentPackage: pkg,
95
+ indentUnit: unit,
96
+ };
97
+ const I1 = unit;
98
+ const I2 = unit + unit;
99
+ const body = [];
100
+ // Fields first (static, then instance), then methods.
101
+ const slots = (traits) => traits.filter((t) => t.kind === TraitKind.Slot || t.kind === TraitKind.Const);
102
+ const methods = (traits) => traits.filter((t) => t.kind !== TraitKind.Slot && t.kind !== TraitKind.Const);
103
+ // Static initialiser: leading `NAME = value` assignments become field initialisers.
104
+ const staticInits = new Map();
105
+ let cinitLines = [];
106
+ if (!isInterface && abc.methodBody(cls.cinit)) {
107
+ const cenv = { ...env, thisTraits: cls.traits };
108
+ const r = decompileMethodStatements(cenv, cls.cinit);
109
+ if ('error' in r) {
110
+ cinitLines = decompileMethodBody(cenv, cls.cinit, I2).lines;
111
+ }
112
+ else {
113
+ const slotNames = new Set(slots(cls.traits).filter((t) => !t.valueIndex).map((t) => abc.multinameName(t.name)));
114
+ let k = 0;
115
+ for (; k < r.stmts.length; k++) {
116
+ const s = r.stmts[k];
117
+ if (s.k !== 'expr' || s.e.k !== 'assign' || s.e.op)
118
+ break;
119
+ const tgt = s.e.target;
120
+ const name = tgt.k === 'name' ? tgt.name : tgt.k === 'member' && (tgt.obj.k === 'this' || tgt.obj.k === 'findprop' || tgt.obj.k === 'global') ? tgt.name : undefined;
121
+ if (!name || !slotNames.has(name) || staticInits.has(name))
122
+ break;
123
+ staticInits.set(name, r.printer.expr(s.e.v, 1, I2));
124
+ }
125
+ cinitLines = r.printer.stmts(r.stmts.slice(k), I2 + unit);
126
+ }
127
+ }
128
+ for (const t of slots(cls.traits))
129
+ body.push(...traitLines(env, t, true, isInterface, I2, staticInits.get(abc.multinameName(t.name))));
130
+ if (slots(cls.traits).length)
131
+ body.push('');
132
+ for (const t of slots(inst.traits))
133
+ body.push(...traitLines(env, t, false, isInterface, I2));
134
+ if (slots(inst.traits).length)
135
+ body.push('');
136
+ if (cinitLines.length)
137
+ body.push(`${I2}{`, ...cinitLines, `${I2}}`, '');
138
+ // Constructor
139
+ if (!isInterface) {
140
+ const ctor = decompileMethodBody(env, inst.iinit, I2);
141
+ const lines = ctor.lines.filter((l, i, all) => !(all.length === 1 && /^\s*super\(\);$/.test(l)));
142
+ body.push(`${I2}public function ${shortName}(${formatParams(env, inst.iinit)})`, `${I2}{`, ...lines, `${I2}}`, '');
143
+ }
144
+ for (const t of methods(cls.traits))
145
+ body.push(...traitLines(env, t, true, isInterface, I2), '');
146
+ for (const t of methods(inst.traits))
147
+ body.push(...traitLines(env, t, false, isInterface, I2), '');
148
+ while (body.length && body[body.length - 1] === '')
149
+ body.pop();
150
+ // Header (computed after the body so imports are complete).
151
+ const superName = inst.superName ? typeNameOf(env, inst.superName) : '';
152
+ const ifaces = inst.interfaces.map((i) => typeNameOf(env, i));
153
+ imports.delete(qualified);
154
+ const mods = `${modifier(abc, inst.name, false)}${inst.flags & InstanceFlags.Final ? 'final ' : ''}${!(inst.flags & InstanceFlags.Sealed) && !isInterface ? 'dynamic ' : ''}`;
155
+ let header;
156
+ if (isInterface) {
157
+ header = `${I1}${mods}interface ${shortName}${ifaces.length ? ` extends ${ifaces.join(', ')}` : ''}`;
158
+ }
159
+ else {
160
+ header = `${I1}${mods}class ${shortName}${superName && superName !== 'Object' ? ` extends ${superName}` : ''}${ifaces.length ? ` implements ${ifaces.join(', ')}` : ''}`;
161
+ }
162
+ const out = [`package${pkg ? ' ' + pkg : ''}`, '{'];
163
+ const sortedImports = [...imports].sort();
164
+ for (const imp of sortedImports)
165
+ out.push(`${I1}import ${imp};`);
166
+ if (sortedImports.length)
167
+ out.push('');
168
+ out.push(header, `${I1}{`, '', ...body, `${I1}}`, '}', '');
169
+ return out.join('\n');
170
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Method-level decompilation: bytecode → structured AS3 statements → text.
3
+ */
4
+ import type { AbcFile, MethodInfo, Trait } from '../abc/abc-file.js';
5
+ import { Printer, type Stmt } from './ast.js';
6
+ export interface DecompileEnv {
7
+ abc: AbcFile;
8
+ /** Instance traits (for getslot on `this`). */
9
+ thisTraits?: Trait[];
10
+ /** Script traits (for getglobalslot). */
11
+ globalTraits?: Trait[];
12
+ /** Fully qualified names referenced by the code (filled in). */
13
+ imports?: Set<string>;
14
+ /** Package of the class being decompiled (no import needed for it). */
15
+ currentPackage?: string;
16
+ indentUnit?: string;
17
+ }
18
+ /** Short type name for a multiname; records an import when needed. */
19
+ export declare function typeNameOf(env: DecompileEnv, mn: number): string;
20
+ export declare function useNameOf(env: DecompileEnv, mn: number): void;
21
+ export declare function paramNames(abc: AbcFile, method: MethodInfo): string[];
22
+ /** Parameter list text: `a:int, b:String = "x", ...rest`. */
23
+ export declare function formatParams(env: DecompileEnv, methodIndex: number, printer?: Printer): string;
24
+ /** Default value literal for optional parameters and slots. */
25
+ export declare function formatDefault(abc: AbcFile, kind: number, index: number): string;
26
+ export interface DecompiledMethod {
27
+ /** Body lines (already indented with `indent + indentUnit`), without braces. */
28
+ lines: string[];
29
+ /** Set when decompilation failed and P-code was emitted instead. */
30
+ error?: string;
31
+ }
32
+ export interface DecompiledStatements {
33
+ stmts: Stmt[];
34
+ printer: Printer;
35
+ }
36
+ /** Decompiles a method body into lines of AS3. */
37
+ export declare function decompileMethodBody(env: DecompileEnv, methodIndex: number, indent: string): DecompiledMethod;
38
+ /** Decompiles a method body to statements plus a printer bound to its local names. */
39
+ export declare function decompileMethodStatements(env: DecompileEnv, methodIndex: number): DecompiledStatements | {
40
+ error: string;
41
+ };