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.
- package/LICENSE +21 -0
- package/README.md +125 -2
- package/README.tr.md +126 -0
- package/dist/abc/abc-file.d.ts +168 -0
- package/dist/abc/abc-file.js +656 -0
- package/dist/abc/analysis.d.ts +36 -0
- package/dist/abc/analysis.js +213 -0
- package/dist/abc/code.d.ts +59 -0
- package/dist/abc/code.js +379 -0
- package/dist/abc/constants.d.ts +80 -0
- package/dist/abc/constants.js +93 -0
- package/dist/abc/interner.d.ts +46 -0
- package/dist/abc/interner.js +168 -0
- package/dist/abc/model.d.ts +40 -0
- package/dist/abc/model.js +146 -0
- package/dist/abc/opcodes.d.ts +39 -0
- package/dist/abc/opcodes.js +196 -0
- package/dist/abc/pcode/assembler.d.ts +42 -0
- package/dist/abc/pcode/assembler.js +635 -0
- package/dist/abc/pcode/disassembler.d.ts +17 -0
- package/dist/abc/pcode/disassembler.js +147 -0
- package/dist/abc/pcode/format.d.ts +23 -0
- package/dist/abc/pcode/format.js +131 -0
- package/dist/abc/pcode/lexer.d.ts +34 -0
- package/dist/abc/pcode/lexer.js +183 -0
- package/dist/assets/assets.d.ts +26 -0
- package/dist/assets/assets.js +151 -0
- package/dist/assets/images.d.ts +31 -0
- package/dist/assets/images.js +238 -0
- package/dist/assets/png.d.ts +12 -0
- package/dist/assets/png.js +196 -0
- package/dist/assets/sounds.d.ts +37 -0
- package/dist/assets/sounds.js +236 -0
- package/dist/avm1/avm1.d.ts +55 -0
- package/dist/avm1/avm1.js +642 -0
- package/dist/avm1/project.d.ts +16 -0
- package/dist/avm1/project.js +81 -0
- package/dist/cjs/abc/abc-file.d.ts +168 -0
- package/dist/cjs/abc/abc-file.js +663 -0
- package/dist/cjs/abc/analysis.d.ts +36 -0
- package/dist/cjs/abc/analysis.js +221 -0
- package/dist/cjs/abc/code.d.ts +59 -0
- package/dist/cjs/abc/code.js +387 -0
- package/dist/cjs/abc/constants.d.ts +80 -0
- package/dist/cjs/abc/constants.js +97 -0
- package/dist/cjs/abc/interner.d.ts +46 -0
- package/dist/cjs/abc/interner.js +172 -0
- package/dist/cjs/abc/model.d.ts +40 -0
- package/dist/cjs/abc/model.js +155 -0
- package/dist/cjs/abc/opcodes.d.ts +39 -0
- package/dist/cjs/abc/opcodes.js +201 -0
- package/dist/cjs/abc/pcode/assembler.d.ts +42 -0
- package/dist/cjs/abc/pcode/assembler.js +640 -0
- package/dist/cjs/abc/pcode/disassembler.d.ts +17 -0
- package/dist/cjs/abc/pcode/disassembler.js +152 -0
- package/dist/cjs/abc/pcode/format.d.ts +23 -0
- package/dist/cjs/abc/pcode/format.js +141 -0
- package/dist/cjs/abc/pcode/lexer.d.ts +34 -0
- package/dist/cjs/abc/pcode/lexer.js +188 -0
- package/dist/cjs/assets/assets.d.ts +26 -0
- package/dist/cjs/assets/assets.js +159 -0
- package/dist/cjs/assets/images.d.ts +31 -0
- package/dist/cjs/assets/images.js +246 -0
- package/dist/cjs/assets/png.d.ts +12 -0
- package/dist/cjs/assets/png.js +202 -0
- package/dist/cjs/assets/sounds.d.ts +37 -0
- package/dist/cjs/assets/sounds.js +246 -0
- package/dist/cjs/avm1/avm1.d.ts +55 -0
- package/dist/cjs/avm1/avm1.js +649 -0
- package/dist/cjs/avm1/project.d.ts +16 -0
- package/dist/cjs/avm1/project.js +88 -0
- package/dist/cjs/compiler/ast.d.ts +285 -0
- package/dist/cjs/compiler/ast.js +3 -0
- package/dist/cjs/compiler/class-compiler.d.ts +34 -0
- package/dist/cjs/compiler/class-compiler.js +440 -0
- package/dist/cjs/compiler/codegen.d.ts +144 -0
- package/dist/cjs/compiler/codegen.js +1589 -0
- package/dist/cjs/compiler/lexer.d.ts +27 -0
- package/dist/cjs/compiler/lexer.js +254 -0
- package/dist/cjs/compiler/parser.d.ts +58 -0
- package/dist/cjs/compiler/parser.js +989 -0
- package/dist/cjs/compiler/program.d.ts +29 -0
- package/dist/cjs/compiler/program.js +36 -0
- package/dist/cjs/compiler/scope.d.ts +22 -0
- package/dist/cjs/compiler/scope.js +242 -0
- package/dist/cjs/compression/lzma.d.ts +37 -0
- package/dist/cjs/compression/lzma.js +575 -0
- package/dist/cjs/compression/swf-container.d.ts +19 -0
- package/dist/cjs/compression/swf-container.js +92 -0
- package/dist/cjs/decompiler/ast.d.ts +228 -0
- package/dist/cjs/decompiler/ast.js +455 -0
- package/dist/cjs/decompiler/cfg.d.ts +40 -0
- package/dist/cjs/decompiler/cfg.js +236 -0
- package/dist/cjs/decompiler/class-printer.d.ts +9 -0
- package/dist/cjs/decompiler/class-printer.js +173 -0
- package/dist/cjs/decompiler/method.d.ts +41 -0
- package/dist/cjs/decompiler/method.js +436 -0
- package/dist/cjs/decompiler/simulate.d.ts +55 -0
- package/dist/cjs/decompiler/simulate.js +591 -0
- package/dist/cjs/decompiler/structure.d.ts +48 -0
- package/dist/cjs/decompiler/structure.js +764 -0
- package/dist/cjs/errors.d.ts +11 -0
- package/dist/cjs/errors.js +25 -0
- package/dist/cjs/index.d.ts +41 -0
- package/dist/cjs/index.js +82 -0
- package/dist/cjs/io/reader.d.ts +48 -0
- package/dist/cjs/io/reader.js +180 -0
- package/dist/cjs/io/writer.d.ts +37 -0
- package/dist/cjs/io/writer.js +175 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/project/pcode-project.d.ts +32 -0
- package/dist/cjs/project/pcode-project.js +210 -0
- package/dist/cjs/project/source-export.d.ts +12 -0
- package/dist/cjs/project/source-export.js +51 -0
- package/dist/cjs/project/source-import.d.ts +13 -0
- package/dist/cjs/project/source-import.js +73 -0
- package/dist/cjs/swf/swf.d.ts +63 -0
- package/dist/cjs/swf/swf.js +147 -0
- package/dist/cjs/swf/tag-codes.d.ts +77 -0
- package/dist/cjs/swf/tag-codes.js +109 -0
- package/dist/cjs/swf/tags.d.ts +118 -0
- package/dist/cjs/swf/tags.js +330 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +296 -0
- package/dist/compiler/ast.d.ts +285 -0
- package/dist/compiler/ast.js +2 -0
- package/dist/compiler/class-compiler.d.ts +34 -0
- package/dist/compiler/class-compiler.js +437 -0
- package/dist/compiler/codegen.d.ts +144 -0
- package/dist/compiler/codegen.js +1585 -0
- package/dist/compiler/lexer.d.ts +27 -0
- package/dist/compiler/lexer.js +249 -0
- package/dist/compiler/parser.d.ts +58 -0
- package/dist/compiler/parser.js +984 -0
- package/dist/compiler/program.d.ts +29 -0
- package/dist/compiler/program.js +32 -0
- package/dist/compiler/scope.d.ts +22 -0
- package/dist/compiler/scope.js +238 -0
- package/dist/compression/lzma.d.ts +37 -0
- package/dist/compression/lzma.js +569 -0
- package/dist/compression/swf-container.d.ts +19 -0
- package/dist/compression/swf-container.js +87 -0
- package/dist/decompiler/ast.d.ts +228 -0
- package/dist/decompiler/ast.js +441 -0
- package/dist/decompiler/cfg.d.ts +40 -0
- package/dist/decompiler/cfg.js +231 -0
- package/dist/decompiler/class-printer.d.ts +9 -0
- package/dist/decompiler/class-printer.js +170 -0
- package/dist/decompiler/method.d.ts +41 -0
- package/dist/decompiler/method.js +427 -0
- package/dist/decompiler/simulate.d.ts +55 -0
- package/dist/decompiler/simulate.js +586 -0
- package/dist/decompiler/structure.d.ts +48 -0
- package/dist/decompiler/structure.js +760 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +20 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +49 -0
- package/dist/io/reader.d.ts +48 -0
- package/dist/io/reader.js +176 -0
- package/dist/io/writer.d.ts +37 -0
- package/dist/io/writer.js +169 -0
- package/dist/project/pcode-project.d.ts +32 -0
- package/dist/project/pcode-project.js +203 -0
- package/dist/project/source-export.d.ts +12 -0
- package/dist/project/source-export.js +47 -0
- package/dist/project/source-import.d.ts +13 -0
- package/dist/project/source-import.js +70 -0
- package/dist/swf/swf.d.ts +63 -0
- package/dist/swf/swf.js +143 -0
- package/dist/swf/tag-codes.d.ts +77 -0
- package/dist/swf/tag-codes.js +105 -0
- package/dist/swf/tags.d.ts +118 -0
- package/dist/swf/tags.js +316 -0
- package/package.json +71 -4
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SimulationError = void 0;
|
|
4
|
+
exports.simulate = simulate;
|
|
5
|
+
const constants_js_1 = require("../abc/constants.js");
|
|
6
|
+
const opcodes_js_1 = require("../abc/opcodes.js");
|
|
7
|
+
const format_js_1 = require("../abc/pcode/format.js");
|
|
8
|
+
const ast_js_1 = require("./ast.js");
|
|
9
|
+
class SimulationError extends Error {
|
|
10
|
+
}
|
|
11
|
+
exports.SimulationError = SimulationError;
|
|
12
|
+
/** Expressions that were duplicated with `dup` (shared across blocks of a method). */
|
|
13
|
+
const duplicated = new WeakSet();
|
|
14
|
+
const BINOPS = {
|
|
15
|
+
[opcodes_js_1.Op.add]: '+',
|
|
16
|
+
[opcodes_js_1.Op.add_i]: '+',
|
|
17
|
+
[opcodes_js_1.Op.subtract]: '-',
|
|
18
|
+
[opcodes_js_1.Op.subtract_i]: '-',
|
|
19
|
+
[opcodes_js_1.Op.multiply]: '*',
|
|
20
|
+
[opcodes_js_1.Op.multiply_i]: '*',
|
|
21
|
+
[opcodes_js_1.Op.divide]: '/',
|
|
22
|
+
[opcodes_js_1.Op.modulo]: '%',
|
|
23
|
+
[opcodes_js_1.Op.lshift]: '<<',
|
|
24
|
+
[opcodes_js_1.Op.rshift]: '>>',
|
|
25
|
+
[opcodes_js_1.Op.urshift]: '>>>',
|
|
26
|
+
[opcodes_js_1.Op.bitand]: '&',
|
|
27
|
+
[opcodes_js_1.Op.bitor]: '|',
|
|
28
|
+
[opcodes_js_1.Op.bitxor]: '^',
|
|
29
|
+
[opcodes_js_1.Op.equals]: '==',
|
|
30
|
+
[opcodes_js_1.Op.strictequals]: '===',
|
|
31
|
+
[opcodes_js_1.Op.lessthan]: '<',
|
|
32
|
+
[opcodes_js_1.Op.lessequals]: '<=',
|
|
33
|
+
[opcodes_js_1.Op.greaterthan]: '>',
|
|
34
|
+
[opcodes_js_1.Op.greaterequals]: '>=',
|
|
35
|
+
[opcodes_js_1.Op.instanceof]: 'instanceof',
|
|
36
|
+
[opcodes_js_1.Op.istypelate]: 'is',
|
|
37
|
+
[opcodes_js_1.Op.astypelate]: 'as',
|
|
38
|
+
[opcodes_js_1.Op.in]: 'in',
|
|
39
|
+
};
|
|
40
|
+
const CONDOPS = {
|
|
41
|
+
[opcodes_js_1.Op.ifeq]: ['==', false],
|
|
42
|
+
[opcodes_js_1.Op.ifne]: ['!=', false],
|
|
43
|
+
[opcodes_js_1.Op.iflt]: ['<', false],
|
|
44
|
+
[opcodes_js_1.Op.ifle]: ['<=', false],
|
|
45
|
+
[opcodes_js_1.Op.ifgt]: ['>', false],
|
|
46
|
+
[opcodes_js_1.Op.ifge]: ['>=', false],
|
|
47
|
+
[opcodes_js_1.Op.ifstricteq]: ['===', false],
|
|
48
|
+
[opcodes_js_1.Op.ifstrictne]: ['!==', false],
|
|
49
|
+
[opcodes_js_1.Op.ifnlt]: ['<', true],
|
|
50
|
+
[opcodes_js_1.Op.ifnle]: ['<=', true],
|
|
51
|
+
[opcodes_js_1.Op.ifngt]: ['>', true],
|
|
52
|
+
[opcodes_js_1.Op.ifnge]: ['>=', true],
|
|
53
|
+
};
|
|
54
|
+
const COERCE_TYPES = {
|
|
55
|
+
[opcodes_js_1.Op.coerce_a]: '*',
|
|
56
|
+
[opcodes_js_1.Op.coerce_s]: 'String',
|
|
57
|
+
[opcodes_js_1.Op.coerce_b]: 'Boolean',
|
|
58
|
+
[opcodes_js_1.Op.coerce_i]: 'int',
|
|
59
|
+
[opcodes_js_1.Op.coerce_u]: 'uint',
|
|
60
|
+
[opcodes_js_1.Op.coerce_d]: 'Number',
|
|
61
|
+
[opcodes_js_1.Op.coerce_o]: 'Object',
|
|
62
|
+
[opcodes_js_1.Op.convert_s]: 'String',
|
|
63
|
+
[opcodes_js_1.Op.convert_b]: 'Boolean',
|
|
64
|
+
[opcodes_js_1.Op.convert_i]: 'int',
|
|
65
|
+
[opcodes_js_1.Op.convert_u]: 'uint',
|
|
66
|
+
[opcodes_js_1.Op.convert_d]: 'Number',
|
|
67
|
+
[opcodes_js_1.Op.convert_o]: 'Object',
|
|
68
|
+
};
|
|
69
|
+
function slotName(traits, abc, slot) {
|
|
70
|
+
const t = traits.find((x) => (x.kind === constants_js_1.TraitKind.Slot || x.kind === constants_js_1.TraitKind.Const || x.kind === constants_js_1.TraitKind.Class || x.kind === constants_js_1.TraitKind.Function) && x.id === slot);
|
|
71
|
+
return t ? abc.multinameName(t.name) : undefined;
|
|
72
|
+
}
|
|
73
|
+
/** Simulates `insns` with the given entry stack, appending statements to `out`. */
|
|
74
|
+
function simulate(ctx, insns, entryStack, out) {
|
|
75
|
+
const { abc, scope } = ctx;
|
|
76
|
+
const stack = [...entryStack];
|
|
77
|
+
const pop = () => {
|
|
78
|
+
const v = stack.pop();
|
|
79
|
+
if (!v)
|
|
80
|
+
throw new SimulationError('Stack underflow');
|
|
81
|
+
return v;
|
|
82
|
+
};
|
|
83
|
+
const popN = (n) => {
|
|
84
|
+
const xs = [];
|
|
85
|
+
for (let i = 0; i < n; i++)
|
|
86
|
+
xs.push(pop());
|
|
87
|
+
return xs.reverse();
|
|
88
|
+
};
|
|
89
|
+
const emit = (s) => {
|
|
90
|
+
out.push(s);
|
|
91
|
+
};
|
|
92
|
+
/** Builds the member expression for a multiname operand, popping runtime parts. */
|
|
93
|
+
const member = (obj, mnIndex, popObj) => {
|
|
94
|
+
const m = abc.multinames[mnIndex];
|
|
95
|
+
let nameExpr;
|
|
96
|
+
let nsExpr;
|
|
97
|
+
if (m) {
|
|
98
|
+
switch (m.kind) {
|
|
99
|
+
case constants_js_1.MultinameKind.RTQNameL:
|
|
100
|
+
case constants_js_1.MultinameKind.RTQNameLA:
|
|
101
|
+
nameExpr = pop();
|
|
102
|
+
nsExpr = pop();
|
|
103
|
+
break;
|
|
104
|
+
case constants_js_1.MultinameKind.MultinameL:
|
|
105
|
+
case constants_js_1.MultinameKind.MultinameLA:
|
|
106
|
+
nameExpr = pop();
|
|
107
|
+
break;
|
|
108
|
+
case constants_js_1.MultinameKind.RTQName:
|
|
109
|
+
case constants_js_1.MultinameKind.RTQNameA:
|
|
110
|
+
nsExpr = pop();
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const target = popObj ? pop() : (obj ?? { k: 'global' });
|
|
115
|
+
const attr = !!m && [constants_js_1.MultinameKind.QNameA, constants_js_1.MultinameKind.RTQNameA, constants_js_1.MultinameKind.RTQNameLA, constants_js_1.MultinameKind.MultinameA, constants_js_1.MultinameKind.MultinameLA].includes(m.kind);
|
|
116
|
+
if (nameExpr)
|
|
117
|
+
return { k: 'index', obj: target, index: nameExpr, attr };
|
|
118
|
+
if (m && (m.kind === constants_js_1.MultinameKind.QName || m.kind === constants_js_1.MultinameKind.QNameA)) {
|
|
119
|
+
const ns = abc.namespaces[m.ns];
|
|
120
|
+
if (ns && ns.kind === constants_js_1.NamespaceKind.Namespace) {
|
|
121
|
+
const uri = abc.strings[ns.name] ?? '';
|
|
122
|
+
// Hide AS3 builtin and interface ("pkg:Interface") namespaces.
|
|
123
|
+
if (uri !== 'http://adobe.com/AS3/2006/builtin' && !/^[\w.$]*:[\w$]+$/.test(uri)) {
|
|
124
|
+
return { k: 'member', obj: target, name: abc.multinameName(mnIndex), attr, ns: nsLabel(uri) };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (nsExpr) {
|
|
129
|
+
const ns = nsExpr.k === 'name' ? nsExpr.name : nsExpr.k === 'member' && nsExpr.obj.k !== 'findprop' ? undefined : nsExpr.k === 'member' ? nsExpr.name : undefined;
|
|
130
|
+
return { k: 'member', obj: target, name: abc.multinameName(mnIndex), attr, ns: ns ?? '*' };
|
|
131
|
+
}
|
|
132
|
+
return { k: 'member', obj: target, name: abc.multinameName(mnIndex), attr };
|
|
133
|
+
};
|
|
134
|
+
/** Simplify `findprop(x).x` → `x`. */
|
|
135
|
+
const access = (e) => {
|
|
136
|
+
if (e.k === 'member' && (e.obj.k === 'findprop' || e.obj.k === 'global') && !e.ns)
|
|
137
|
+
return { k: 'name', name: e.name };
|
|
138
|
+
return e;
|
|
139
|
+
};
|
|
140
|
+
/** Assignment with dup / postfix handling. */
|
|
141
|
+
const assign = (target, value, isLocalReg) => {
|
|
142
|
+
// Value duplicated on the stack: the remaining copies now equal the target.
|
|
143
|
+
let dup = false;
|
|
144
|
+
const bare = value.k === 'coerce' ? stripCoerceLocal(value) : value;
|
|
145
|
+
for (let i = 0; i < stack.length; i++) {
|
|
146
|
+
if (stack[i] === value || stack[i] === bare) {
|
|
147
|
+
stack[i] = target;
|
|
148
|
+
dup = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Postfix: old value still on the stack, new value = old ± 1.
|
|
152
|
+
const v = value.k === 'coerce' ? value.e : value;
|
|
153
|
+
if (!dup && v.k === 'binary' && (v.op === '+' || v.op === '-') && v.b.k === 'lit' && v.b.v === 1 && (0, ast_js_1.exprEquals)(v.a, target)) {
|
|
154
|
+
const idx = stack.findIndex((s) => (0, ast_js_1.exprEquals)(s, target));
|
|
155
|
+
if (idx >= 0) {
|
|
156
|
+
stack[idx] = { k: 'postfix', op: v.op === '+' ? '++' : '--', e: target };
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (isLocalReg !== undefined) {
|
|
161
|
+
// Old value of the register still referenced on the stack: freeze it.
|
|
162
|
+
for (let i = 0; i < stack.length; i++) {
|
|
163
|
+
if ((0, ast_js_1.readsLocal)(stack[i], isLocalReg) && stack[i] !== target) {
|
|
164
|
+
// Cannot express safely; keep textual order (rare in compiler output).
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
emit({ k: 'expr', e: { k: 'assign', target, v: value } });
|
|
169
|
+
};
|
|
170
|
+
const getLocal = (reg) => {
|
|
171
|
+
if (reg === 0)
|
|
172
|
+
return { k: 'this' };
|
|
173
|
+
return ctx.specialRegs.get(reg) ?? { k: 'local', reg };
|
|
174
|
+
};
|
|
175
|
+
const setLocal = (reg, value) => {
|
|
176
|
+
if (value.k === 'activation' || value.k === 'catchscope' || value.k === 'findprop' || value.k === 'global') {
|
|
177
|
+
ctx.specialRegs.set(reg, value);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
ctx.specialRegs.delete(reg);
|
|
181
|
+
assign({ k: 'local', reg }, value, reg);
|
|
182
|
+
};
|
|
183
|
+
const resolveSlot = (obj, slot) => {
|
|
184
|
+
switch (obj.k) {
|
|
185
|
+
case 'activation': {
|
|
186
|
+
const n = slotName(ctx.activationTraits, abc, slot);
|
|
187
|
+
return n ? { k: 'name', name: n } : { k: 'slot', obj, slot };
|
|
188
|
+
}
|
|
189
|
+
case 'catchscope':
|
|
190
|
+
return { k: 'name', name: ctx.catchNames.get(obj.index) ?? `e${obj.index}` };
|
|
191
|
+
case 'global': {
|
|
192
|
+
const n = slotName(ctx.globalTraits, abc, slot);
|
|
193
|
+
return n ? { k: 'name', name: n } : { k: 'slot', obj, slot };
|
|
194
|
+
}
|
|
195
|
+
case 'this': {
|
|
196
|
+
const n = slotName(ctx.thisTraits, abc, slot);
|
|
197
|
+
return n ? { k: 'member', obj, name: n } : { k: 'slot', obj, slot };
|
|
198
|
+
}
|
|
199
|
+
default:
|
|
200
|
+
return { k: 'slot', obj, slot };
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
for (let pc = 0; pc < insns.length; pc++) {
|
|
204
|
+
const ins = insns[pc];
|
|
205
|
+
const a = ins.args;
|
|
206
|
+
const op = ins.op;
|
|
207
|
+
const bin = BINOPS[op];
|
|
208
|
+
if (bin) {
|
|
209
|
+
const b = pop();
|
|
210
|
+
const x = pop();
|
|
211
|
+
stack.push({ k: 'binary', op: bin, a: x, b });
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const coerceType = COERCE_TYPES[op];
|
|
215
|
+
if (coerceType) {
|
|
216
|
+
stack.push({ k: 'coerce', type: coerceType, e: pop() });
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
switch (op) {
|
|
220
|
+
case opcodes_js_1.Op.nop:
|
|
221
|
+
case opcodes_js_1.Op.label:
|
|
222
|
+
case opcodes_js_1.Op.bkpt:
|
|
223
|
+
case opcodes_js_1.Op.debugline:
|
|
224
|
+
case opcodes_js_1.Op.debugfile:
|
|
225
|
+
case opcodes_js_1.Op.bkptline:
|
|
226
|
+
case opcodes_js_1.Op.timestamp:
|
|
227
|
+
case opcodes_js_1.Op.kill:
|
|
228
|
+
break;
|
|
229
|
+
case opcodes_js_1.Op.debug:
|
|
230
|
+
if (a[0] === 1 && a[1])
|
|
231
|
+
ctx.regNames.set((a[2] ?? 0) + 1, abc.strings[a[1]] ?? '');
|
|
232
|
+
break;
|
|
233
|
+
case opcodes_js_1.Op.throw:
|
|
234
|
+
return { stack, term: { k: 'throw', e: pop() } };
|
|
235
|
+
case opcodes_js_1.Op.returnvoid:
|
|
236
|
+
return { stack, term: { k: 'return' } };
|
|
237
|
+
case opcodes_js_1.Op.returnvalue:
|
|
238
|
+
return { stack, term: { k: 'return', e: pop() } };
|
|
239
|
+
case opcodes_js_1.Op.jump:
|
|
240
|
+
return { stack, term: { k: 'jump' } };
|
|
241
|
+
case opcodes_js_1.Op.iftrue:
|
|
242
|
+
return { stack, term: { k: 'cond', c: pop() } };
|
|
243
|
+
case opcodes_js_1.Op.iffalse:
|
|
244
|
+
return { stack, term: { k: 'cond', c: (0, ast_js_1.not)(pop()) } };
|
|
245
|
+
case opcodes_js_1.Op.lookupswitch:
|
|
246
|
+
return { stack, term: { k: 'switch', e: pop() } };
|
|
247
|
+
case opcodes_js_1.Op.getsuper: {
|
|
248
|
+
const m = member(null, a[0], true);
|
|
249
|
+
stack.push(m.k === 'member' ? { ...m, obj: { k: 'super' } } : m);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case opcodes_js_1.Op.setsuper: {
|
|
253
|
+
const v = pop();
|
|
254
|
+
const m = member(null, a[0], true);
|
|
255
|
+
assign(m.k === 'member' ? { ...m, obj: { k: 'super' } } : m, v);
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
case opcodes_js_1.Op.dxns:
|
|
259
|
+
emit({ k: 'comment', text: `default xml namespace = ${JSON.stringify(abc.strings[a[0]] ?? '')}` });
|
|
260
|
+
break;
|
|
261
|
+
case opcodes_js_1.Op.dxnslate:
|
|
262
|
+
emit({ k: 'comment', text: 'default xml namespace = <runtime>' });
|
|
263
|
+
pop();
|
|
264
|
+
break;
|
|
265
|
+
case opcodes_js_1.Op.pushwith:
|
|
266
|
+
scope.push(pop());
|
|
267
|
+
emit({ k: 'comment', text: 'with(...) scope begins' });
|
|
268
|
+
break;
|
|
269
|
+
case opcodes_js_1.Op.pushscope:
|
|
270
|
+
scope.push(pop());
|
|
271
|
+
break;
|
|
272
|
+
case opcodes_js_1.Op.popscope:
|
|
273
|
+
scope.pop();
|
|
274
|
+
break;
|
|
275
|
+
case opcodes_js_1.Op.nextname:
|
|
276
|
+
case opcodes_js_1.Op.nextvalue: {
|
|
277
|
+
const idx = pop();
|
|
278
|
+
const obj = pop();
|
|
279
|
+
stack.push({ k: 'next', value: op === opcodes_js_1.Op.nextvalue, obj, idx });
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
case opcodes_js_1.Op.hasnext: {
|
|
283
|
+
const idx = pop();
|
|
284
|
+
const obj = pop();
|
|
285
|
+
stack.push({ k: 'hasnext', obj, idx });
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
case opcodes_js_1.Op.hasnext2:
|
|
289
|
+
stack.push({ k: 'hasnext2', objReg: a[0], idxReg: a[1] });
|
|
290
|
+
break;
|
|
291
|
+
case opcodes_js_1.Op.pushnull:
|
|
292
|
+
stack.push((0, ast_js_1.lit)(null));
|
|
293
|
+
break;
|
|
294
|
+
case opcodes_js_1.Op.pushundefined:
|
|
295
|
+
stack.push((0, ast_js_1.lit)(undefined));
|
|
296
|
+
break;
|
|
297
|
+
case opcodes_js_1.Op.pushtrue:
|
|
298
|
+
stack.push((0, ast_js_1.lit)(true));
|
|
299
|
+
break;
|
|
300
|
+
case opcodes_js_1.Op.pushfalse:
|
|
301
|
+
stack.push((0, ast_js_1.lit)(false));
|
|
302
|
+
break;
|
|
303
|
+
case opcodes_js_1.Op.pushnan:
|
|
304
|
+
stack.push((0, ast_js_1.lit)(NaN));
|
|
305
|
+
break;
|
|
306
|
+
case opcodes_js_1.Op.pushbyte:
|
|
307
|
+
case opcodes_js_1.Op.pushshort:
|
|
308
|
+
stack.push((0, ast_js_1.lit)(a[0]));
|
|
309
|
+
break;
|
|
310
|
+
case opcodes_js_1.Op.pushstring:
|
|
311
|
+
stack.push((0, ast_js_1.lit)(abc.strings[a[0]] ?? ''));
|
|
312
|
+
break;
|
|
313
|
+
case opcodes_js_1.Op.pushint:
|
|
314
|
+
stack.push((0, ast_js_1.lit)(abc.ints[a[0]] ?? 0));
|
|
315
|
+
break;
|
|
316
|
+
case opcodes_js_1.Op.pushuint:
|
|
317
|
+
stack.push((0, ast_js_1.lit)(abc.uints[a[0]] ?? 0));
|
|
318
|
+
break;
|
|
319
|
+
case opcodes_js_1.Op.pushdouble:
|
|
320
|
+
stack.push((0, ast_js_1.lit)(abc.doubles[a[0]] ?? NaN));
|
|
321
|
+
break;
|
|
322
|
+
case opcodes_js_1.Op.pushnamespace:
|
|
323
|
+
stack.push((0, ast_js_1.raw)((0, format_js_1.formatNamespace)(abc, a[0])));
|
|
324
|
+
break;
|
|
325
|
+
case opcodes_js_1.Op.pop: {
|
|
326
|
+
const v = pop();
|
|
327
|
+
// A duplicated value has already been consumed elsewhere (e.g. by a branch).
|
|
328
|
+
if ((0, ast_js_1.hasSideEffects)(v) && !duplicated.has(v))
|
|
329
|
+
emit({ k: 'expr', e: v });
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case opcodes_js_1.Op.dup: {
|
|
333
|
+
const v = pop();
|
|
334
|
+
duplicated.add(v);
|
|
335
|
+
stack.push(v, v);
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
case opcodes_js_1.Op.swap: {
|
|
339
|
+
const x = pop();
|
|
340
|
+
const y = pop();
|
|
341
|
+
stack.push(x, y);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
case opcodes_js_1.Op.li8:
|
|
345
|
+
case opcodes_js_1.Op.li16:
|
|
346
|
+
case opcodes_js_1.Op.li32:
|
|
347
|
+
case opcodes_js_1.Op.lf32:
|
|
348
|
+
case opcodes_js_1.Op.lf64:
|
|
349
|
+
case opcodes_js_1.Op.sxi1:
|
|
350
|
+
case opcodes_js_1.Op.sxi8:
|
|
351
|
+
case opcodes_js_1.Op.sxi16:
|
|
352
|
+
stack.push({ k: 'call', fn: (0, ast_js_1.raw)((0, opcodes_js_1.opInfo)(op).name), args: [pop()] });
|
|
353
|
+
break;
|
|
354
|
+
case opcodes_js_1.Op.si8:
|
|
355
|
+
case opcodes_js_1.Op.si16:
|
|
356
|
+
case opcodes_js_1.Op.si32:
|
|
357
|
+
case opcodes_js_1.Op.sf32:
|
|
358
|
+
case opcodes_js_1.Op.sf64: {
|
|
359
|
+
const addr = pop();
|
|
360
|
+
const v = pop();
|
|
361
|
+
emit({ k: 'expr', e: { k: 'call', fn: (0, ast_js_1.raw)((0, opcodes_js_1.opInfo)(op).name), args: [v, addr] } });
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case opcodes_js_1.Op.newfunction:
|
|
365
|
+
stack.push({ k: 'function', methodIndex: a[0] });
|
|
366
|
+
break;
|
|
367
|
+
case opcodes_js_1.Op.call: {
|
|
368
|
+
const args = popN(a[0]);
|
|
369
|
+
pop(); // receiver
|
|
370
|
+
const fn = pop();
|
|
371
|
+
stack.push({ k: 'call', fn, args });
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
case opcodes_js_1.Op.construct: {
|
|
375
|
+
const args = popN(a[0]);
|
|
376
|
+
stack.push({ k: 'new', ctor: pop(), args });
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
case opcodes_js_1.Op.callmethod: {
|
|
380
|
+
const args = popN(a[1]);
|
|
381
|
+
const obj = pop();
|
|
382
|
+
stack.push({ k: 'call', fn: { k: 'member', obj, name: `/*method#${a[0]}*/` }, args });
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case opcodes_js_1.Op.callstatic: {
|
|
386
|
+
const args = popN(a[1]);
|
|
387
|
+
pop();
|
|
388
|
+
stack.push({ k: 'call', fn: (0, ast_js_1.raw)(`/*static method ${a[0]}*/`), args });
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case opcodes_js_1.Op.callsuper:
|
|
392
|
+
case opcodes_js_1.Op.callsupervoid: {
|
|
393
|
+
const args = popN(a[1]);
|
|
394
|
+
const m = member(null, a[0], true);
|
|
395
|
+
const call = { k: 'call', fn: m.k === 'member' ? { ...m, obj: { k: 'super' } } : m, args };
|
|
396
|
+
if (op === opcodes_js_1.Op.callsupervoid)
|
|
397
|
+
emit({ k: 'expr', e: call });
|
|
398
|
+
else
|
|
399
|
+
stack.push(call);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
case opcodes_js_1.Op.callproperty:
|
|
403
|
+
case opcodes_js_1.Op.callproplex:
|
|
404
|
+
case opcodes_js_1.Op.callpropvoid: {
|
|
405
|
+
const args = popN(a[1]);
|
|
406
|
+
const call = { k: 'call', fn: access(member(null, a[0], true)), args };
|
|
407
|
+
if (op === opcodes_js_1.Op.callpropvoid)
|
|
408
|
+
emit({ k: 'expr', e: call });
|
|
409
|
+
else
|
|
410
|
+
stack.push(call);
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
case opcodes_js_1.Op.constructsuper: {
|
|
414
|
+
const args = popN(a[0]);
|
|
415
|
+
pop();
|
|
416
|
+
emit({ k: 'expr', e: { k: 'call', fn: { k: 'super' }, args } });
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
case opcodes_js_1.Op.constructprop: {
|
|
420
|
+
const args = popN(a[1]);
|
|
421
|
+
stack.push({ k: 'new', ctor: access(member(null, a[0], true)), args });
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
case opcodes_js_1.Op.applytype: {
|
|
425
|
+
const params = popN(a[0]);
|
|
426
|
+
stack.push({ k: 'applytype', base: pop(), params });
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
case opcodes_js_1.Op.newobject: {
|
|
430
|
+
const flat = popN(a[0] * 2);
|
|
431
|
+
const props = [];
|
|
432
|
+
for (let i = 0; i < flat.length; i += 2)
|
|
433
|
+
props.push([flat[i], flat[i + 1]]);
|
|
434
|
+
stack.push({ k: 'object', props });
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
case opcodes_js_1.Op.newarray:
|
|
438
|
+
stack.push({ k: 'array', items: popN(a[0]) });
|
|
439
|
+
break;
|
|
440
|
+
case opcodes_js_1.Op.newactivation:
|
|
441
|
+
stack.push({ k: 'activation' });
|
|
442
|
+
break;
|
|
443
|
+
case opcodes_js_1.Op.newclass:
|
|
444
|
+
pop();
|
|
445
|
+
stack.push({ k: 'name', name: abc.multinameName(abc.instances[a[0]]?.name ?? 0) });
|
|
446
|
+
break;
|
|
447
|
+
case opcodes_js_1.Op.getdescendants: {
|
|
448
|
+
const m = member(null, a[0], true);
|
|
449
|
+
stack.push(m.k === 'member' ? { k: 'descendants', obj: m.obj, name: m.name } : m);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case opcodes_js_1.Op.newcatch:
|
|
453
|
+
stack.push({ k: 'catchscope', index: a[0] });
|
|
454
|
+
break;
|
|
455
|
+
case opcodes_js_1.Op.findpropstrict:
|
|
456
|
+
case opcodes_js_1.Op.findproperty: {
|
|
457
|
+
const m = member({ k: 'global' }, a[0], false);
|
|
458
|
+
ctx.useName(a[0]);
|
|
459
|
+
stack.push(m.k === 'member' ? { k: 'findprop', name: m.name } : { k: 'global' });
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case opcodes_js_1.Op.finddef:
|
|
463
|
+
ctx.useName(a[0]);
|
|
464
|
+
stack.push({ k: 'findprop', name: abc.multinameName(a[0]) });
|
|
465
|
+
break;
|
|
466
|
+
case opcodes_js_1.Op.getlex:
|
|
467
|
+
ctx.useName(a[0]);
|
|
468
|
+
stack.push({ k: 'name', name: abc.multinameName(a[0]) });
|
|
469
|
+
break;
|
|
470
|
+
case opcodes_js_1.Op.setproperty:
|
|
471
|
+
case opcodes_js_1.Op.initproperty: {
|
|
472
|
+
const v = pop();
|
|
473
|
+
assign(access(member(null, a[0], true)), v);
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
case opcodes_js_1.Op.getproperty:
|
|
477
|
+
stack.push(access(member(null, a[0], true)));
|
|
478
|
+
break;
|
|
479
|
+
case opcodes_js_1.Op.deleteproperty:
|
|
480
|
+
stack.push({ k: 'unary', op: 'delete', e: access(member(null, a[0], true)) });
|
|
481
|
+
break;
|
|
482
|
+
case opcodes_js_1.Op.getlocal:
|
|
483
|
+
stack.push(getLocal(a[0]));
|
|
484
|
+
break;
|
|
485
|
+
case opcodes_js_1.Op.getlocal0:
|
|
486
|
+
case opcodes_js_1.Op.getlocal1:
|
|
487
|
+
case opcodes_js_1.Op.getlocal2:
|
|
488
|
+
case opcodes_js_1.Op.getlocal3:
|
|
489
|
+
stack.push(getLocal(op - opcodes_js_1.Op.getlocal0));
|
|
490
|
+
break;
|
|
491
|
+
case opcodes_js_1.Op.setlocal:
|
|
492
|
+
setLocal(a[0], pop());
|
|
493
|
+
break;
|
|
494
|
+
case opcodes_js_1.Op.setlocal0:
|
|
495
|
+
case opcodes_js_1.Op.setlocal1:
|
|
496
|
+
case opcodes_js_1.Op.setlocal2:
|
|
497
|
+
case opcodes_js_1.Op.setlocal3:
|
|
498
|
+
setLocal(op - opcodes_js_1.Op.setlocal0, pop());
|
|
499
|
+
break;
|
|
500
|
+
case opcodes_js_1.Op.getglobalscope:
|
|
501
|
+
stack.push({ k: 'global' });
|
|
502
|
+
break;
|
|
503
|
+
case opcodes_js_1.Op.getscopeobject:
|
|
504
|
+
stack.push(scope[a[0]] ?? { k: 'global' });
|
|
505
|
+
break;
|
|
506
|
+
case opcodes_js_1.Op.getouterscope:
|
|
507
|
+
stack.push({ k: 'global' });
|
|
508
|
+
break;
|
|
509
|
+
case opcodes_js_1.Op.getslot:
|
|
510
|
+
stack.push(resolveSlot(pop(), a[0]));
|
|
511
|
+
break;
|
|
512
|
+
case opcodes_js_1.Op.setslot: {
|
|
513
|
+
const v = pop();
|
|
514
|
+
const obj = pop();
|
|
515
|
+
const target = resolveSlot(obj, a[0]);
|
|
516
|
+
if (obj.k === 'catchscope' && v.k === 'name' && target.k === 'name' && v.name === target.name)
|
|
517
|
+
break;
|
|
518
|
+
assign(target, v);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
case opcodes_js_1.Op.getglobalslot:
|
|
522
|
+
stack.push(resolveSlot({ k: 'global' }, a[0]));
|
|
523
|
+
break;
|
|
524
|
+
case opcodes_js_1.Op.setglobalslot:
|
|
525
|
+
assign(resolveSlot({ k: 'global' }, a[0]), pop());
|
|
526
|
+
break;
|
|
527
|
+
case opcodes_js_1.Op.esc_xelem:
|
|
528
|
+
case opcodes_js_1.Op.esc_xattr:
|
|
529
|
+
case opcodes_js_1.Op.checkfilter:
|
|
530
|
+
break; // value passes through
|
|
531
|
+
case opcodes_js_1.Op.coerce:
|
|
532
|
+
stack.push({ k: 'coerce', type: ctx.typeName(a[0]), e: pop() });
|
|
533
|
+
break;
|
|
534
|
+
case opcodes_js_1.Op.astype:
|
|
535
|
+
stack.push({ k: 'binary', op: 'as', a: pop(), b: { k: 'name', name: ctx.typeName(a[0]) } });
|
|
536
|
+
break;
|
|
537
|
+
case opcodes_js_1.Op.istype:
|
|
538
|
+
stack.push({ k: 'binary', op: 'is', a: pop(), b: { k: 'name', name: ctx.typeName(a[0]) } });
|
|
539
|
+
break;
|
|
540
|
+
case opcodes_js_1.Op.negate:
|
|
541
|
+
case opcodes_js_1.Op.negate_i:
|
|
542
|
+
stack.push({ k: 'unary', op: '-', e: pop() });
|
|
543
|
+
break;
|
|
544
|
+
case opcodes_js_1.Op.increment:
|
|
545
|
+
case opcodes_js_1.Op.increment_i:
|
|
546
|
+
stack.push({ k: 'binary', op: '+', a: pop(), b: (0, ast_js_1.lit)(1) });
|
|
547
|
+
break;
|
|
548
|
+
case opcodes_js_1.Op.decrement:
|
|
549
|
+
case opcodes_js_1.Op.decrement_i:
|
|
550
|
+
stack.push({ k: 'binary', op: '-', a: pop(), b: (0, ast_js_1.lit)(1) });
|
|
551
|
+
break;
|
|
552
|
+
case opcodes_js_1.Op.inclocal:
|
|
553
|
+
case opcodes_js_1.Op.inclocal_i:
|
|
554
|
+
emit({ k: 'expr', e: { k: 'postfix', op: '++', e: getLocal(a[0]) } });
|
|
555
|
+
break;
|
|
556
|
+
case opcodes_js_1.Op.declocal:
|
|
557
|
+
case opcodes_js_1.Op.declocal_i:
|
|
558
|
+
emit({ k: 'expr', e: { k: 'postfix', op: '--', e: getLocal(a[0]) } });
|
|
559
|
+
break;
|
|
560
|
+
case opcodes_js_1.Op.typeof:
|
|
561
|
+
stack.push({ k: 'unary', op: 'typeof', e: pop() });
|
|
562
|
+
break;
|
|
563
|
+
case opcodes_js_1.Op.not:
|
|
564
|
+
stack.push((0, ast_js_1.not)(pop()));
|
|
565
|
+
break;
|
|
566
|
+
case opcodes_js_1.Op.bitnot:
|
|
567
|
+
stack.push({ k: 'unary', op: '~', e: pop() });
|
|
568
|
+
break;
|
|
569
|
+
default: {
|
|
570
|
+
const cond = CONDOPS[op];
|
|
571
|
+
if (cond) {
|
|
572
|
+
const b = pop();
|
|
573
|
+
const x = pop();
|
|
574
|
+
const c = { k: 'binary', op: cond[0], a: x, b };
|
|
575
|
+
return { stack, term: { k: 'cond', c: cond[1] ? { k: 'unary', op: '!', e: c } : c } };
|
|
576
|
+
}
|
|
577
|
+
throw new SimulationError(`Unsupported instruction ${(0, opcodes_js_1.opInfo)(op)?.name ?? op}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return { stack, term: { k: 'fall' } };
|
|
582
|
+
}
|
|
583
|
+
function stripCoerceLocal(e) {
|
|
584
|
+
while (e.k === 'coerce')
|
|
585
|
+
e = e.e;
|
|
586
|
+
return e;
|
|
587
|
+
}
|
|
588
|
+
function nsLabel(uri) {
|
|
589
|
+
const tail = uri.split(/[/:]/).filter(Boolean).pop() ?? 'ns';
|
|
590
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(tail) ? tail : 'ns';
|
|
591
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control-flow structuring: rebuilds if/else, loops, switch and try/catch
|
|
3
|
+
* from the CFG, using dominator / post-dominator information.
|
|
4
|
+
*/
|
|
5
|
+
import type { Stmt } from './ast.js';
|
|
6
|
+
import { type Cfg } from './cfg.js';
|
|
7
|
+
import { type MethodContext } from './simulate.js';
|
|
8
|
+
export declare class Structurer {
|
|
9
|
+
private readonly ctx;
|
|
10
|
+
private readonly cfg;
|
|
11
|
+
private visited;
|
|
12
|
+
private loops;
|
|
13
|
+
private labelCounter;
|
|
14
|
+
private tries;
|
|
15
|
+
/** Labels of loop statements that need one, keyed by LoopCtx. */
|
|
16
|
+
private loopStmts;
|
|
17
|
+
constructor(ctx: MethodContext, cfg: Cfg);
|
|
18
|
+
run(): Stmt[];
|
|
19
|
+
/**
|
|
20
|
+
* If block `id` only evaluates a condition on top of `stack` (no statements,
|
|
21
|
+
* stack unchanged afterwards), returns that condition.
|
|
22
|
+
*/
|
|
23
|
+
private blockIsEmptyCondition;
|
|
24
|
+
/** Collapses short-circuit conditions (`a && b`, `a || b`) starting at a conditional block. */
|
|
25
|
+
private collapse;
|
|
26
|
+
private mergeOf;
|
|
27
|
+
/** Follows blocks that only contain `label` / `jump` / `nop` to their destination. */
|
|
28
|
+
private resolveTrivial;
|
|
29
|
+
/** Statement for jumping to `target` from inside the current constructs, or null. */
|
|
30
|
+
private jumpStmt;
|
|
31
|
+
private loopLabel;
|
|
32
|
+
private seq;
|
|
33
|
+
private evalCond;
|
|
34
|
+
private evalPath;
|
|
35
|
+
private emitLoop;
|
|
36
|
+
/**
|
|
37
|
+
* Natural loop body plus dead-end blocks (return/throw paths) dominated by
|
|
38
|
+
* the header: those are syntactically inside the loop even though they
|
|
39
|
+
* never branch back.
|
|
40
|
+
*/
|
|
41
|
+
private loopRegion;
|
|
42
|
+
/** Header condition for blocks that only compute a condition from scratch (e.g. hasnext2). */
|
|
43
|
+
private pureHeaderCondition;
|
|
44
|
+
private switchFollow;
|
|
45
|
+
private reaches;
|
|
46
|
+
private emitSwitch;
|
|
47
|
+
private emitTry;
|
|
48
|
+
}
|