storm-lua-minify 0.3.0 → 0.9.1
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/README.md +119 -41
- package/dist/aggregateSpecialization.js +406 -0
- package/dist/ast2lua.js +156 -68
- package/dist/astWalk.js +162 -0
- package/dist/callGraph.js +372 -0
- package/dist/cli.js +53 -58
- package/dist/cliOptions.js +36 -0
- package/dist/cliProgress.js +87 -0
- package/dist/config.js +73 -0
- package/dist/constantFold.js +798 -0
- package/dist/controlFlow.js +266 -0
- package/dist/functionRewrites.js +580 -0
- package/dist/generatedAst.js +108 -0
- package/dist/generatedNode.js +23 -0
- package/dist/globalRename.js +17 -4
- package/dist/interproceduralAnalysis.js +842 -0
- package/dist/interproceduralConstants.js +120 -0
- package/dist/luaString.js +157 -0
- package/dist/minifier.js +1219 -49
- package/dist/optimizerAnalysis.js +43 -0
- package/dist/optimizerDiagnostics.js +65 -0
- package/dist/optimizerFacts.js +529 -0
- package/dist/optimizerPass.js +96 -0
- package/dist/optimizerTransaction.js +56 -0
- package/dist/optimizerValueDomain.js +200 -0
- package/dist/options.js +233 -0
- package/dist/progress.js +2 -0
- package/dist/removeUnused.js +145 -0
- package/dist/renamer.js +280 -54
- package/dist/resolver.js +35 -11
- package/dist/runtimeEnvironment.js +105 -0
- package/dist/sourceMetadata.js +314 -0
- package/dist/statementDataflow.js +259 -0
- package/dist/statementScheduler.js +598 -0
- package/dist/symbolLiveness.js +92 -0
- package/dist/tableEffects.js +356 -0
- package/dist/transform.js +10 -371
- package/dist/valueFlow.js +409 -0
- package/dist/wholeProgramExports.js +646 -0
- package/dist/wholeProgramFieldRenames.js +583 -0
- package/dist/wholeProgramFields.js +672 -0
- package/dist/wholeProgramObjects.js +783 -0
- package/package.json +11 -2
- package/dist/index.js +0 -27
package/dist/transform.js
CHANGED
|
@@ -1,55 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mergeLocalDeclarations = mergeLocalDeclarations;
|
|
4
3
|
exports.originalNameOf = originalNameOf;
|
|
5
4
|
exports.insertGlobalAliases = insertGlobalAliases;
|
|
6
5
|
const linker_1 = require("./linker");
|
|
7
|
-
function mergeLocalDeclarations(chunk, resolveResult, options) {
|
|
8
|
-
processBlock(chunk.body, resolveResult, options);
|
|
9
|
-
}
|
|
10
|
-
// 十分に非現実的な接頭辞を使い、実在の識別子との衝突を避ける
|
|
11
|
-
// (このパスの直後にresolveScopesが再実行され、最終的な短縮名は
|
|
12
|
-
// Renameパスが決めるため、この仮名自体が出力に残ることはない)。
|
|
13
6
|
const ALIAS_TEMP_NAME_PREFIX = "__mergeAlias";
|
|
14
|
-
// エイリアス化で`.name`を書き換えた参照ノードについて、Source Mapに載せる
|
|
15
|
-
// べき「元の名前」を記録する。ast2lua.tsのgenerateIdentifierはこれを参照し、
|
|
16
|
-
// 位置情報(loc)はそのままなのにSource Mapのnamesフィールドだけ書き換え後の
|
|
17
|
-
// 仮名(あるいは最終的な短縮名)になってしまう不具合を避ける。
|
|
18
7
|
const aliasedOriginalNames = new WeakMap();
|
|
19
8
|
function originalNameOf(identifier) {
|
|
20
9
|
return aliasedOriginalNames.get(identifier);
|
|
21
10
|
}
|
|
22
|
-
// 代入コスト(`local X=`相当、8バイト)を、参照1回あたりの節約バイト数
|
|
23
|
-
// (元の名前の長さ-1文字、エイリアス名を1文字と仮定)で上回るかどうかで判定する。
|
|
24
11
|
function isWorthAliasing(name, referenceCount) {
|
|
25
12
|
const savingsPerReference = name.length - 1;
|
|
26
13
|
const declarationCost = 8 + name.length;
|
|
27
14
|
return referenceCount * savingsPerReference > declarationCost;
|
|
28
15
|
}
|
|
16
|
+
/** Inserts aliases for profitable read-only external globals before statement scheduling. */
|
|
29
17
|
function insertGlobalAliases(chunk, resolveResult, options) {
|
|
30
18
|
const newLocals = [];
|
|
31
19
|
let aliasCounter = 0;
|
|
32
20
|
resolveResult.globals.forEach((binding) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
if (options.excludeNames.has(binding.name)) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
// require/dofileはPrintパスが名前文字列で再検出するため、
|
|
42
|
-
// エイリアス化して名前を変えると検出できなくなり壊れる。
|
|
43
|
-
if (linker_1.RESERVED_MODULE_FUNCTION_NAMES.has(binding.name)) {
|
|
21
|
+
if (binding.writes.length > 0 ||
|
|
22
|
+
options.excludeNames.has(binding.name) ||
|
|
23
|
+
linker_1.RESERVED_MODULE_FUNCTION_NAMES.has(binding.name) ||
|
|
24
|
+
!isWorthAliasing(binding.name, binding.references.length))
|
|
44
25
|
return;
|
|
45
|
-
}
|
|
46
|
-
if (!isWorthAliasing(binding.name, binding.references.length)) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
26
|
const tempName = ALIAS_TEMP_NAME_PREFIX + String(aliasCounter++);
|
|
50
|
-
binding.references.forEach((
|
|
51
|
-
aliasedOriginalNames.set(
|
|
52
|
-
|
|
27
|
+
binding.references.forEach((reference) => {
|
|
28
|
+
aliasedOriginalNames.set(reference, reference.name);
|
|
29
|
+
reference.name = tempName;
|
|
53
30
|
});
|
|
54
31
|
newLocals.push({
|
|
55
32
|
type: "LocalStatement",
|
|
@@ -57,345 +34,7 @@ function insertGlobalAliases(chunk, resolveResult, options) {
|
|
|
57
34
|
init: [{ type: "Identifier", name: binding.name }],
|
|
58
35
|
});
|
|
59
36
|
});
|
|
60
|
-
if (newLocals.length > 0)
|
|
37
|
+
if (newLocals.length > 0)
|
|
61
38
|
chunk.body.unshift(...newLocals);
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
// resolver.tsのresolveStatement/resolveExpressionの分岐を鏡写しにした
|
|
65
|
-
// 汎用ウォーカー。「参照として現れる識別子」と「ネストしたブロック本体」を
|
|
66
|
-
// どちらも一度の走査で収集できるようにし、resolver.tsの走査ロジックの
|
|
67
|
-
// 重複を避ける。
|
|
68
|
-
function walkStatement(statement, visitor) {
|
|
69
|
-
switch (statement.type) {
|
|
70
|
-
case "LocalStatement":
|
|
71
|
-
// 宣言される変数自体は参照ではないため辿らない
|
|
72
|
-
statement.init.forEach((expr) => {
|
|
73
|
-
walkExpression(expr, visitor);
|
|
74
|
-
});
|
|
75
|
-
return;
|
|
76
|
-
case "AssignmentStatement":
|
|
77
|
-
statement.variables.forEach((v) => {
|
|
78
|
-
if (v.type === "Identifier") {
|
|
79
|
-
visitor.onIdentifierReference?.(v);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
walkExpression(v, visitor);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
statement.init.forEach((expr) => {
|
|
86
|
-
walkExpression(expr, visitor);
|
|
87
|
-
});
|
|
88
|
-
return;
|
|
89
|
-
case "CallStatement":
|
|
90
|
-
walkExpression(statement.expression, visitor);
|
|
91
|
-
return;
|
|
92
|
-
case "DoStatement":
|
|
93
|
-
visitor.onBlock?.(statement.body);
|
|
94
|
-
return;
|
|
95
|
-
case "WhileStatement":
|
|
96
|
-
walkExpression(statement.condition, visitor);
|
|
97
|
-
visitor.onBlock?.(statement.body);
|
|
98
|
-
return;
|
|
99
|
-
case "RepeatStatement":
|
|
100
|
-
visitor.onBlock?.(statement.body);
|
|
101
|
-
walkExpression(statement.condition, visitor);
|
|
102
|
-
return;
|
|
103
|
-
case "IfStatement":
|
|
104
|
-
statement.clauses.forEach((clause) => {
|
|
105
|
-
if (clause.type !== "ElseClause") {
|
|
106
|
-
walkExpression(clause.condition, visitor);
|
|
107
|
-
}
|
|
108
|
-
visitor.onBlock?.(clause.body);
|
|
109
|
-
});
|
|
110
|
-
return;
|
|
111
|
-
case "ForNumericStatement":
|
|
112
|
-
walkExpression(statement.start, visitor);
|
|
113
|
-
walkExpression(statement.end, visitor);
|
|
114
|
-
if (statement.step) {
|
|
115
|
-
walkExpression(statement.step, visitor);
|
|
116
|
-
}
|
|
117
|
-
visitor.onBlock?.(statement.body);
|
|
118
|
-
return;
|
|
119
|
-
case "ForGenericStatement":
|
|
120
|
-
statement.iterators.forEach((iterator) => {
|
|
121
|
-
walkExpression(iterator, visitor);
|
|
122
|
-
});
|
|
123
|
-
visitor.onBlock?.(statement.body);
|
|
124
|
-
return;
|
|
125
|
-
case "FunctionDeclaration":
|
|
126
|
-
if (statement.identifier) {
|
|
127
|
-
if (statement.identifier.type === "Identifier") {
|
|
128
|
-
if (!statement.isLocal) {
|
|
129
|
-
// 非local: 既存のローカル/グローバルへの代入(参照扱い)
|
|
130
|
-
visitor.onIdentifierReference?.(statement.identifier);
|
|
131
|
-
}
|
|
132
|
-
// isLocalの場合は宣言自体であり参照ではないため辿らない
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
walkExpression(statement.identifier, visitor);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
visitor.onBlock?.(statement.body);
|
|
139
|
-
return;
|
|
140
|
-
case "ReturnStatement":
|
|
141
|
-
statement.arguments.forEach((argument) => {
|
|
142
|
-
walkExpression(argument, visitor);
|
|
143
|
-
});
|
|
144
|
-
return;
|
|
145
|
-
case "BreakStatement":
|
|
146
|
-
case "LabelStatement":
|
|
147
|
-
case "GotoStatement":
|
|
148
|
-
return;
|
|
149
|
-
default: {
|
|
150
|
-
const exhaustive = statement;
|
|
151
|
-
throw new TypeError("Unknown statement type: `" + JSON.stringify(exhaustive) + "`");
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function walkExpression(expr, visitor) {
|
|
156
|
-
switch (expr.type) {
|
|
157
|
-
case "Identifier":
|
|
158
|
-
visitor.onIdentifierReference?.(expr);
|
|
159
|
-
return;
|
|
160
|
-
case "StringLiteral":
|
|
161
|
-
case "NumericLiteral":
|
|
162
|
-
case "BooleanLiteral":
|
|
163
|
-
case "NilLiteral":
|
|
164
|
-
case "VarargLiteral":
|
|
165
|
-
return;
|
|
166
|
-
case "LogicalExpression":
|
|
167
|
-
case "BinaryExpression":
|
|
168
|
-
walkExpression(expr.left, visitor);
|
|
169
|
-
walkExpression(expr.right, visitor);
|
|
170
|
-
return;
|
|
171
|
-
case "UnaryExpression":
|
|
172
|
-
walkExpression(expr.argument, visitor);
|
|
173
|
-
return;
|
|
174
|
-
case "CallExpression":
|
|
175
|
-
walkExpression(expr.base, visitor);
|
|
176
|
-
expr.arguments.forEach((argument) => {
|
|
177
|
-
walkExpression(argument, visitor);
|
|
178
|
-
});
|
|
179
|
-
return;
|
|
180
|
-
case "TableCallExpression":
|
|
181
|
-
walkExpression(expr.base, visitor);
|
|
182
|
-
walkExpression(expr.arguments, visitor);
|
|
183
|
-
return;
|
|
184
|
-
case "StringCallExpression":
|
|
185
|
-
walkExpression(expr.base, visitor);
|
|
186
|
-
walkExpression(expr.argument, visitor);
|
|
187
|
-
return;
|
|
188
|
-
case "IndexExpression":
|
|
189
|
-
walkExpression(expr.base, visitor);
|
|
190
|
-
walkExpression(expr.index, visitor);
|
|
191
|
-
return;
|
|
192
|
-
case "MemberExpression":
|
|
193
|
-
walkExpression(expr.base, visitor);
|
|
194
|
-
// フィールド名(`.identifier`)は変数参照ではないため辿らない
|
|
195
|
-
return;
|
|
196
|
-
case "FunctionDeclaration":
|
|
197
|
-
visitor.onBlock?.(expr.body);
|
|
198
|
-
return;
|
|
199
|
-
case "TableConstructorExpression":
|
|
200
|
-
expr.fields.forEach((field) => {
|
|
201
|
-
if (field.type === "TableKey") {
|
|
202
|
-
walkExpression(field.key, visitor);
|
|
203
|
-
walkExpression(field.value, visitor);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
walkExpression(field.value, visitor);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
return;
|
|
210
|
-
default: {
|
|
211
|
-
const exhaustive = expr;
|
|
212
|
-
throw new TypeError("Unknown expression type: `" + JSON.stringify(exhaustive) + "`");
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
function processBlock(body, resolveResult, options) {
|
|
217
|
-
// 先に子ブロック(ネストしたスコープ)を処理する。子ブロックは別配列なので
|
|
218
|
-
// このレベルでのまとめ上げには影響しない。
|
|
219
|
-
body.forEach((statement) => {
|
|
220
|
-
walkStatement(statement, {
|
|
221
|
-
onBlock: (nested) => {
|
|
222
|
-
processBlock(nested, resolveResult, options);
|
|
223
|
-
},
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
mergeRunsInPlace(body, resolveResult, options);
|
|
227
|
-
}
|
|
228
|
-
// あるexpr(候補文のinit式)の中に現れる識別子参照をすべて収集する。
|
|
229
|
-
// ネストした関数リテラルの本体も再帰的に辿る(ハザード1: クロージャの
|
|
230
|
-
// 字句スコープは実行タイミングに関わらず即座に確定するため)。
|
|
231
|
-
function collectIdentifierReferences(expr) {
|
|
232
|
-
const found = [];
|
|
233
|
-
const visitor = {
|
|
234
|
-
onIdentifierReference: (id) => found.push(id),
|
|
235
|
-
onBlock: (body) => {
|
|
236
|
-
body.forEach((s) => {
|
|
237
|
-
walkStatement(s, visitor);
|
|
238
|
-
});
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
walkExpression(expr, visitor);
|
|
242
|
-
return found;
|
|
243
|
-
}
|
|
244
|
-
// ============================================================
|
|
245
|
-
// ハザード判定
|
|
246
|
-
// ============================================================
|
|
247
|
-
function isExpandable(expr) {
|
|
248
|
-
return (expr.type === "CallExpression" ||
|
|
249
|
-
expr.type === "TableCallExpression" ||
|
|
250
|
-
expr.type === "StringCallExpression" ||
|
|
251
|
-
expr.type === "VarargLiteral");
|
|
252
|
-
}
|
|
253
|
-
// ハザード2(複数値展開): statementをグループの非末尾要素として
|
|
254
|
-
// 後ろに文を連結してよいかどうかを判定する。
|
|
255
|
-
// - 変数と初期化式の個数が一致: 常に安全(パディング不要)
|
|
256
|
-
// - 変数が多い(不足): 最後の初期化式が展開可能でなければnilパディングで安全。
|
|
257
|
-
// 展開可能な式に依存している場合は非末尾にできない。
|
|
258
|
-
// - 初期化式が多い(過剰): 常に非末尾として不安全
|
|
259
|
-
// (余剰の値が後続文の変数へずれ込むため、パディングでは救えない)
|
|
260
|
-
function classifyNonTerminal(statement) {
|
|
261
|
-
const varCount = statement.variables.length;
|
|
262
|
-
const initCount = statement.init.length;
|
|
263
|
-
if (varCount === initCount) {
|
|
264
|
-
return { safe: true, needsPadding: false };
|
|
265
|
-
}
|
|
266
|
-
if (varCount > initCount) {
|
|
267
|
-
const lastInit = initCount > 0 ? statement.init[initCount - 1] : undefined;
|
|
268
|
-
if (!lastInit || !isExpandable(lastInit)) {
|
|
269
|
-
return { safe: true, needsPadding: true };
|
|
270
|
-
}
|
|
271
|
-
return { safe: false };
|
|
272
|
-
}
|
|
273
|
-
return { safe: false };
|
|
274
|
-
}
|
|
275
|
-
// ハザード1(参照順序): candidateのinit式が、groupSoFarで宣言済みの
|
|
276
|
-
// 変数を参照していないかを判定する。参照していれば統合するとLuaの
|
|
277
|
-
// スコープ規則上意味が変わってしまうため、そこでグループを閉じる必要がある。
|
|
278
|
-
function initReferencesDeclaredInGroup(candidate, groupSoFar, resolveResult) {
|
|
279
|
-
const declaredInGroup = new Set();
|
|
280
|
-
groupSoFar.forEach((statement) => {
|
|
281
|
-
statement.variables.forEach((v) => {
|
|
282
|
-
const symbol = resolveResult.symbolOf(v);
|
|
283
|
-
if (symbol) {
|
|
284
|
-
declaredInGroup.add(symbol);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
return candidate.init.some((expr) => collectIdentifierReferences(expr).some((id) => {
|
|
289
|
-
const symbol = resolveResult.symbolOf(id);
|
|
290
|
-
return symbol !== undefined && declaredInGroup.has(symbol);
|
|
291
|
-
}));
|
|
292
|
-
}
|
|
293
|
-
// ハザード4: SLモードでtrySpliceRequireStatement(#29)の対象になり得る
|
|
294
|
-
// `local x = require(<静的文字列>)`形式の文かどうかを判定する。
|
|
295
|
-
function isSpliceEligibleRequire(statement) {
|
|
296
|
-
if (statement.variables.length !== 1 || statement.init.length !== 1) {
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
return isStaticRequireCall(statement.init[0]);
|
|
300
|
-
}
|
|
301
|
-
function isStaticRequireCall(expr) {
|
|
302
|
-
if (expr.type === "CallExpression") {
|
|
303
|
-
return (expr.base.type === "Identifier" &&
|
|
304
|
-
expr.base.name === "require" &&
|
|
305
|
-
expr.arguments.length > 0 &&
|
|
306
|
-
(0, linker_1.staticStringArgument)(expr.arguments[0]) !== undefined);
|
|
307
|
-
}
|
|
308
|
-
if (expr.type === "StringCallExpression") {
|
|
309
|
-
return (expr.base.type === "Identifier" &&
|
|
310
|
-
expr.base.name === "require" &&
|
|
311
|
-
(0, linker_1.staticStringArgument)(expr.argument) !== undefined);
|
|
312
|
-
}
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
315
|
-
// ============================================================
|
|
316
|
-
// グループ化とマージ
|
|
317
|
-
// ============================================================
|
|
318
|
-
function nilLiteral() {
|
|
319
|
-
return { type: "NilLiteral", value: null, raw: "nil" };
|
|
320
|
-
}
|
|
321
|
-
function combineGroup(group) {
|
|
322
|
-
const variables = [];
|
|
323
|
-
const init = [];
|
|
324
|
-
group.forEach((statement, index) => {
|
|
325
|
-
variables.push(...statement.variables);
|
|
326
|
-
const isLast = index === group.length - 1;
|
|
327
|
-
init.push(...statement.init);
|
|
328
|
-
if (!isLast) {
|
|
329
|
-
const safety = classifyNonTerminal(statement);
|
|
330
|
-
if (safety.safe && safety.needsPadding) {
|
|
331
|
-
const padCount = statement.variables.length - statement.init.length;
|
|
332
|
-
for (let i = 0; i < padCount; i++) {
|
|
333
|
-
init.push(nilLiteral());
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
// 末尾の合成nilは、Luaが変数超過分を自動的にnilで埋めるため省略できる
|
|
339
|
-
// (元から書かれていた明示的なnilを末尾で省略しても意味は変わらない)。
|
|
340
|
-
while (init.length > 0 && init[init.length - 1].type === "NilLiteral") {
|
|
341
|
-
init.pop();
|
|
342
|
-
}
|
|
343
|
-
return {
|
|
344
|
-
type: "LocalStatement",
|
|
345
|
-
variables,
|
|
346
|
-
init,
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
function mergeRun(run, resolveResult, options) {
|
|
350
|
-
const output = [];
|
|
351
|
-
let groupStart = 0;
|
|
352
|
-
const flushGroup = (endExclusive) => {
|
|
353
|
-
if (endExclusive <= groupStart) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
const group = run.slice(groupStart, endExclusive);
|
|
357
|
-
output.push(group.length >= 2 ? combineGroup(group) : group[0]);
|
|
358
|
-
};
|
|
359
|
-
for (let k = 0; k < run.length; k++) {
|
|
360
|
-
const statement = run[k];
|
|
361
|
-
if (options.preserveRequireSplice && isSpliceEligibleRequire(statement)) {
|
|
362
|
-
flushGroup(k);
|
|
363
|
-
output.push(statement);
|
|
364
|
-
groupStart = k + 1;
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
if (k === groupStart) {
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
const previous = run[k - 1];
|
|
371
|
-
const previousSafety = classifyNonTerminal(previous);
|
|
372
|
-
const referencesGroupLocal = initReferencesDeclaredInGroup(statement, run.slice(groupStart, k), resolveResult);
|
|
373
|
-
if (!previousSafety.safe || referencesGroupLocal) {
|
|
374
|
-
flushGroup(k);
|
|
375
|
-
groupStart = k;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
flushGroup(run.length);
|
|
379
|
-
return output;
|
|
380
|
-
}
|
|
381
|
-
function mergeRunsInPlace(body, resolveResult, options) {
|
|
382
|
-
const result = [];
|
|
383
|
-
let i = 0;
|
|
384
|
-
while (i < body.length) {
|
|
385
|
-
const statement = body[i];
|
|
386
|
-
if (statement.type !== "LocalStatement") {
|
|
387
|
-
result.push(statement);
|
|
388
|
-
i++;
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
let j = i;
|
|
392
|
-
const run = [];
|
|
393
|
-
while (j < body.length && body[j].type === "LocalStatement") {
|
|
394
|
-
run.push(body[j]);
|
|
395
|
-
j++;
|
|
396
|
-
}
|
|
397
|
-
result.push(...mergeRun(run, resolveResult, options));
|
|
398
|
-
i = j;
|
|
399
|
-
}
|
|
400
|
-
body.splice(0, body.length, ...result);
|
|
39
|
+
return newLocals.length > 0;
|
|
401
40
|
}
|