storm-lua-minify 0.3.0 → 0.9.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/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/interproceduralAnalysis.js +842 -0
- package/dist/interproceduralConstants.js +120 -0
- package/dist/luaString.js +157 -0
- package/dist/minifier.js +1178 -44
- 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 +180 -0
- package/dist/options.js +233 -0
- package/dist/progress.js +2 -0
- package/dist/removeUnused.js +145 -0
- package/dist/renamer.js +223 -54
- package/dist/resolver.js +28 -11
- package/dist/runtimeEnvironment.js +105 -0
- package/dist/sourceMetadata.js +314 -0
- package/dist/statementDataflow.js +259 -0
- package/dist/statementScheduler.js +595 -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/ast2lua.js
CHANGED
|
@@ -8,6 +8,7 @@ const source_map_1 = require("source-map");
|
|
|
8
8
|
const linker_1 = require("./linker");
|
|
9
9
|
const keywordLocator_1 = require("./keywordLocator");
|
|
10
10
|
const transform_1 = require("./transform");
|
|
11
|
+
const sourceMetadata_1 = require("./sourceMetadata");
|
|
11
12
|
const PRECEDENCE = {
|
|
12
13
|
or: 1,
|
|
13
14
|
and: 2,
|
|
@@ -137,6 +138,10 @@ function isKeyword(id) {
|
|
|
137
138
|
}
|
|
138
139
|
return false;
|
|
139
140
|
}
|
|
141
|
+
// 末尾が16進整数リテラル(0x/0Xの後に16進数字が1つ以上続く)かどうか。
|
|
142
|
+
// a〜fは識別子の文字集合とも重なるため、isNeedSeparatorのalpha分岐だけでは
|
|
143
|
+
// 区別できない(#53)。
|
|
144
|
+
const HEX_INT_LITERAL_TAIL = /0[xX][0-9a-fA-F]+$/;
|
|
140
145
|
function isNeedSeparator(a, b) {
|
|
141
146
|
const lastCharA = a.slice(-1);
|
|
142
147
|
const firstCharB = b.charAt(0);
|
|
@@ -152,11 +157,15 @@ function isNeedSeparator(a, b) {
|
|
|
152
157
|
// e.g. `local a` + `local b`
|
|
153
158
|
return true;
|
|
154
159
|
}
|
|
155
|
-
|
|
156
|
-
// e.g. `
|
|
157
|
-
//
|
|
158
|
-
|
|
160
|
+
if (firstCharB == "." && HEX_INT_LITERAL_TAIL.test(a)) {
|
|
161
|
+
// e.g. `0xff` + `..` : 16進の浮動小数点(`0xff.8`)も読めてしまうため、
|
|
162
|
+
// 続く`.`をそのまま出すと`0xff.`まで数値として読まれ、10進では起きない
|
|
163
|
+
// 区切り落ち(malformed number)になる(#53)。
|
|
164
|
+
return true;
|
|
159
165
|
}
|
|
166
|
+
// e.g. `not` + `(2>3 or 3<2)`
|
|
167
|
+
// e.g. `x` + `^`
|
|
168
|
+
return false;
|
|
160
169
|
}
|
|
161
170
|
if (regexDigits.test(lastCharA)) {
|
|
162
171
|
if (firstCharB == "(" ||
|
|
@@ -184,7 +193,18 @@ function isNeedSeparator(a, b) {
|
|
|
184
193
|
}
|
|
185
194
|
return false;
|
|
186
195
|
}
|
|
187
|
-
|
|
196
|
+
const requiredWhitespaceByNode = new WeakMap();
|
|
197
|
+
function whitespaceOf(...values) {
|
|
198
|
+
for (const value of values) {
|
|
199
|
+
if (value instanceof source_map_1.SourceNode) {
|
|
200
|
+
const whitespace = requiredWhitespaceByNode.get(value);
|
|
201
|
+
if (whitespace)
|
|
202
|
+
return whitespace;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return "\n";
|
|
206
|
+
}
|
|
207
|
+
function addWithSeparator(val, adding, separator = whitespaceOf(val)) {
|
|
188
208
|
if (isNeedSeparator(val.toString(), wrapArray(adding)
|
|
189
209
|
.map((p) => p.toString())
|
|
190
210
|
.join())) {
|
|
@@ -193,7 +213,7 @@ function addWithSeparator(val, adding, separator = " ") {
|
|
|
193
213
|
val.add(adding);
|
|
194
214
|
return val;
|
|
195
215
|
}
|
|
196
|
-
function prependWithSeparator(val, prepending, separator =
|
|
216
|
+
function prependWithSeparator(val, prepending, separator = whitespaceOf(val)) {
|
|
197
217
|
if (isNeedSeparator(wrapArray(prepending)
|
|
198
218
|
.map((p) => p.toString())
|
|
199
219
|
.join(), val.toString())) {
|
|
@@ -202,7 +222,7 @@ function prependWithSeparator(val, prepending, separator = " ") {
|
|
|
202
222
|
val.prepend(prepending);
|
|
203
223
|
return val;
|
|
204
224
|
}
|
|
205
|
-
function insertSeparator(a, b, separator =
|
|
225
|
+
function insertSeparator(a, b, separator = whitespaceOf(a, b)) {
|
|
206
226
|
return isNeedSeparator(a.toString(), b.toString()) ? separator : undefined;
|
|
207
227
|
}
|
|
208
228
|
class MinifyFile {
|
|
@@ -211,28 +231,29 @@ class MinifyFile {
|
|
|
211
231
|
ast;
|
|
212
232
|
minifier;
|
|
213
233
|
mode;
|
|
234
|
+
requiredWhitespace;
|
|
214
235
|
constructor(fileName, moduleName, ast, minifier, mode) {
|
|
215
236
|
this.fileName = fileName;
|
|
216
237
|
this.moduleName = moduleName;
|
|
217
238
|
this.ast = ast;
|
|
218
239
|
this.minifier = minifier;
|
|
219
240
|
this.mode = mode;
|
|
241
|
+
this.requiredWhitespace = mode.requiredWhitespace ?? "\n";
|
|
220
242
|
}
|
|
221
|
-
|
|
243
|
+
/** 合成文同士の境界も、通常の文リストと同じ区切り判定で出力する。 */
|
|
244
|
+
printGeneratedStatements(statements) {
|
|
245
|
+
return this.formatStatementList(statements);
|
|
246
|
+
}
|
|
247
|
+
parse() {
|
|
222
248
|
const body = this.formatStatementList(this.ast.body);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return body;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
return body;
|
|
235
|
-
}
|
|
249
|
+
this.minifier
|
|
250
|
+
.getSourceMetadata(this.moduleName)
|
|
251
|
+
.afterModuleComments()
|
|
252
|
+
.filter(sourceMetadata_1.isPreservedComment)
|
|
253
|
+
.forEach((comment) => {
|
|
254
|
+
body.add(["\n", this.sourceNodeHelper(comment, comment.raw)]);
|
|
255
|
+
});
|
|
256
|
+
return body;
|
|
236
257
|
}
|
|
237
258
|
/**
|
|
238
259
|
* モジュール本体の最後の文が「単一の式を返すreturn文」であるときに限り、
|
|
@@ -240,7 +261,7 @@ class MinifyFile {
|
|
|
240
261
|
* 展開したい呼び出し側(#29のレビュー対応)が利用する。
|
|
241
262
|
* 該当しない場合はundefinedを返し、呼び出し側はIIFE方式へフォールバックする。
|
|
242
263
|
*/
|
|
243
|
-
parseAsStatementsAndFinalExpression(
|
|
264
|
+
parseAsStatementsAndFinalExpression() {
|
|
244
265
|
const body = this.ast.body;
|
|
245
266
|
const last = body[body.length - 1];
|
|
246
267
|
if (!last ||
|
|
@@ -249,18 +270,20 @@ class MinifyFile {
|
|
|
249
270
|
return undefined;
|
|
250
271
|
}
|
|
251
272
|
const statements = this.formatStatementList(body.slice(0, -1));
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
273
|
+
const metadata = this.minifier.getSourceMetadata(this.moduleName);
|
|
274
|
+
[
|
|
275
|
+
...metadata.beforeOf(last),
|
|
276
|
+
...metadata.trailingOf(last),
|
|
277
|
+
...metadata.afterModuleComments(),
|
|
278
|
+
]
|
|
279
|
+
.filter(sourceMetadata_1.isPreservedComment)
|
|
280
|
+
.forEach((comment) => {
|
|
281
|
+
statements.add([
|
|
282
|
+
"\n",
|
|
283
|
+
this.sourceNodeHelper(comment, comment.raw),
|
|
284
|
+
"\n",
|
|
285
|
+
]);
|
|
286
|
+
});
|
|
264
287
|
const finalExpression = this.formatExpression(last.arguments[0]);
|
|
265
288
|
return { statements, finalExpression };
|
|
266
289
|
}
|
|
@@ -269,7 +292,9 @@ class MinifyFile {
|
|
|
269
292
|
const column = node?.loc?.start.column;
|
|
270
293
|
// this.fileNameは常にこのMinifyFileインスタンスが担当するモジュール自身の
|
|
271
294
|
// ファイル名(Linkパスで解決済み)なので、ここで出力するノードの由来ファイルとして正しい。
|
|
272
|
-
|
|
295
|
+
const sourceNode = new source_map_1.SourceNode(line == undefined ? null : line, column == undefined ? null : column, this.fileName, chuncks, name);
|
|
296
|
+
requiredWhitespaceByNode.set(sourceNode, this.requiredWhitespace);
|
|
297
|
+
return sourceNode;
|
|
273
298
|
}
|
|
274
299
|
_keywordLocator;
|
|
275
300
|
/**
|
|
@@ -296,6 +321,9 @@ class MinifyFile {
|
|
|
296
321
|
* (入れ子の`if...then`等)を誤って拾うことはない。
|
|
297
322
|
*/
|
|
298
323
|
keywordAfter(anchor, value) {
|
|
324
|
+
if (!anchor.loc?.end) {
|
|
325
|
+
return this.sourceNodeHelper(undefined, value);
|
|
326
|
+
}
|
|
299
327
|
return this.keywordFrom(anchor.loc?.end, value);
|
|
300
328
|
}
|
|
301
329
|
keywordFrom(from, value) {
|
|
@@ -314,8 +342,29 @@ class MinifyFile {
|
|
|
314
342
|
}
|
|
315
343
|
formatStatementList(body) {
|
|
316
344
|
const result = this.sourceNodeHelper(undefined, []);
|
|
345
|
+
const metadata = this.minifier.getSourceMetadata(this.moduleName);
|
|
317
346
|
wrapArray(body).forEach((statement) => {
|
|
318
|
-
|
|
347
|
+
const statementNode = this.sourceNodeHelper(undefined, []);
|
|
348
|
+
const sourceStatement = statement.type === "ModuleSplice" ? undefined : statement;
|
|
349
|
+
(sourceStatement ? metadata.beforeOf(sourceStatement) : [])
|
|
350
|
+
.filter(sourceMetadata_1.isPreservedComment)
|
|
351
|
+
.forEach((comment) => {
|
|
352
|
+
statementNode.add([
|
|
353
|
+
this.sourceNodeHelper(comment, comment.raw),
|
|
354
|
+
"\n",
|
|
355
|
+
]);
|
|
356
|
+
});
|
|
357
|
+
statementNode.add(this.formatStatement(statement));
|
|
358
|
+
(sourceStatement ? metadata.trailingOf(sourceStatement) : [])
|
|
359
|
+
.filter(sourceMetadata_1.isPreservedComment)
|
|
360
|
+
.forEach((comment) => {
|
|
361
|
+
statementNode.add([
|
|
362
|
+
this.requiredWhitespace,
|
|
363
|
+
this.sourceNodeHelper(comment, comment.raw),
|
|
364
|
+
"\n",
|
|
365
|
+
]);
|
|
366
|
+
});
|
|
367
|
+
addWithSeparator(result, statementNode, "\n");
|
|
319
368
|
});
|
|
320
369
|
return result;
|
|
321
370
|
}
|
|
@@ -330,7 +379,7 @@ class MinifyFile {
|
|
|
330
379
|
* (呼び出し側は従来のIIFE方式にフォールバックする)。
|
|
331
380
|
*/
|
|
332
381
|
trySpliceRequireStatement(statement) {
|
|
333
|
-
if (this.mode.
|
|
382
|
+
if (this.mode.requireWrapper) {
|
|
334
383
|
return undefined;
|
|
335
384
|
}
|
|
336
385
|
if (statement.variables.length !== 1 || statement.init.length !== 1) {
|
|
@@ -351,7 +400,10 @@ class MinifyFile {
|
|
|
351
400
|
: this.formatExpression(target);
|
|
352
401
|
const result = this.sourceNodeHelper(statement, []);
|
|
353
402
|
addWithSeparator(result, spliced.statements);
|
|
354
|
-
|
|
403
|
+
if (isLocal) {
|
|
404
|
+
addWithSeparator(result, "local");
|
|
405
|
+
}
|
|
406
|
+
addWithSeparator(result, targetNode);
|
|
355
407
|
addWithSeparator(result, "=");
|
|
356
408
|
addWithSeparator(result, spliced.finalExpression);
|
|
357
409
|
return result;
|
|
@@ -362,16 +414,26 @@ class MinifyFile {
|
|
|
362
414
|
* 近い、LifeBoat Modeでの一般的な使い方に合わせるための対応。#29のレビュー対応)。
|
|
363
415
|
*/
|
|
364
416
|
tryInlineBareRequireStatement(expr) {
|
|
365
|
-
if (this.mode.
|
|
417
|
+
if (this.mode.requireWrapper) {
|
|
366
418
|
return undefined;
|
|
367
419
|
}
|
|
368
420
|
const moduleRef = this.matchModuleCallExpression(expr);
|
|
369
421
|
if (!moduleRef || moduleRef.kind !== "require") {
|
|
370
422
|
return undefined;
|
|
371
423
|
}
|
|
424
|
+
// 戻り値を使わないrequireでは、依存モジュール末尾のreturn式も不要になる。
|
|
425
|
+
// return文ごと展開すると呼び出し元を途中でreturnし、後続文がある場合は
|
|
426
|
+
// 構文上も不正になるため、分離可能なら副作用を持つ先行文だけを出力する。
|
|
427
|
+
const spliced = this.minifier.splitModuleForStatementSplice(moduleRef.moduleName);
|
|
428
|
+
if (spliced) {
|
|
429
|
+
return spliced.statements;
|
|
430
|
+
}
|
|
372
431
|
return this.minifier.printModuleInline(moduleRef.moduleName);
|
|
373
432
|
}
|
|
374
433
|
formatStatement(statement) {
|
|
434
|
+
if (statement.type === "ModuleSplice") {
|
|
435
|
+
return this.minifier.printModuleInline(statement.moduleName);
|
|
436
|
+
}
|
|
375
437
|
if (statement.type == "AssignmentStatement" ||
|
|
376
438
|
statement.type == "LocalStatement") {
|
|
377
439
|
const spliced = this.trySpliceRequireStatement(statement);
|
|
@@ -396,10 +458,8 @@ class MinifyFile {
|
|
|
396
458
|
const variables = statement.variables
|
|
397
459
|
.map((variable) => [this.formatExpression(variable), ","])
|
|
398
460
|
.flat();
|
|
399
|
-
const result = this.sourceNodeHelper(statement,
|
|
400
|
-
|
|
401
|
-
this.sourceNodeHelper(undefined, variables.slice(0, -1)),
|
|
402
|
-
]);
|
|
461
|
+
const result = this.sourceNodeHelper(statement, "local");
|
|
462
|
+
addWithSeparator(result, this.sourceNodeHelper(undefined, variables.slice(0, -1)));
|
|
403
463
|
if (statement.init.length) {
|
|
404
464
|
const inits = statement.init
|
|
405
465
|
.map((init) => [this.formatExpression(init), ","])
|
|
@@ -480,7 +540,10 @@ class MinifyFile {
|
|
|
480
540
|
return result;
|
|
481
541
|
}
|
|
482
542
|
else if (statement.type == "FunctionDeclaration") {
|
|
483
|
-
const result = this.sourceNodeHelper(statement,
|
|
543
|
+
const result = this.sourceNodeHelper(statement, statement.isLocal ? "local" : "function");
|
|
544
|
+
if (statement.isLocal) {
|
|
545
|
+
addWithSeparator(result, "function");
|
|
546
|
+
}
|
|
484
547
|
if (statement.identifier) {
|
|
485
548
|
addWithSeparator(result, this.formatExpression(statement.identifier));
|
|
486
549
|
}
|
|
@@ -547,10 +610,9 @@ class MinifyFile {
|
|
|
547
610
|
}
|
|
548
611
|
else if (statement.type == "GotoStatement") {
|
|
549
612
|
// The identifier names in a `GotoStatement` can safely be renamed
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
]);
|
|
613
|
+
const result = this.sourceNodeHelper(statement, "goto");
|
|
614
|
+
addWithSeparator(result, this.generateIdentifier(statement.label));
|
|
615
|
+
return result;
|
|
554
616
|
}
|
|
555
617
|
else {
|
|
556
618
|
throw TypeError("Unknown statement type: `" + JSON.stringify(statement) + "`");
|
|
@@ -563,8 +625,13 @@ class MinifyFile {
|
|
|
563
625
|
if (expression.type == "Identifier") {
|
|
564
626
|
return this.generateIdentifier(expression);
|
|
565
627
|
}
|
|
566
|
-
else if (expression.type == "StringLiteral"
|
|
567
|
-
|
|
628
|
+
else if (expression.type == "StringLiteral") {
|
|
629
|
+
const fieldRename = this.minifier.getFieldRename(expression);
|
|
630
|
+
return fieldRename
|
|
631
|
+
? this.sourceNodeHelper(expression, JSON.stringify(fieldRename.name), fieldRename.originalName)
|
|
632
|
+
: this.sourceNodeHelper(expression, expression.raw);
|
|
633
|
+
}
|
|
634
|
+
else if (expression.type == "NumericLiteral" ||
|
|
568
635
|
expression.type == "BooleanLiteral" ||
|
|
569
636
|
expression.type == "NilLiteral" ||
|
|
570
637
|
expression.type == "VarargLiteral") {
|
|
@@ -593,7 +660,11 @@ class MinifyFile {
|
|
|
593
660
|
if (operator == "^" || operator == "..") {
|
|
594
661
|
associativity = "right";
|
|
595
662
|
}
|
|
596
|
-
else if
|
|
663
|
+
// 上のif(結合則の決定)とは独立に判定する必要がある。else ifだと`^`と`..`が
|
|
664
|
+
// ここに来ず、優先順位の高い演算子の中に現れても丸括弧が付かなくなる
|
|
665
|
+
// (#52。例: `0.5 % (2 .. 16)`が`0.5%2 ..16`になり、`%`の方が優先順位が
|
|
666
|
+
// 高いため意味の違うコードとして読み直されてしまっていた)。
|
|
667
|
+
if (currentPrecedence < options.precedence ||
|
|
597
668
|
(currentPrecedence == options.precedence &&
|
|
598
669
|
associativity != options.direction &&
|
|
599
670
|
options.parent != "+" &&
|
|
@@ -686,12 +757,15 @@ class MinifyFile {
|
|
|
686
757
|
]);
|
|
687
758
|
}
|
|
688
759
|
else if (expression.type == "MemberExpression") {
|
|
760
|
+
const fieldRename = this.minifier.getFieldRename(expression.identifier);
|
|
689
761
|
return this.sourceNodeHelper(expression, [
|
|
690
762
|
this.formatBase(expression.base),
|
|
691
763
|
expression.indexer,
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
764
|
+
fieldRename
|
|
765
|
+
? this.sourceNodeHelper(expression.identifier, fieldRename.name, fieldRename.originalName)
|
|
766
|
+
: this.formatExpression(expression.identifier, {
|
|
767
|
+
preserveIdentifiers: true,
|
|
768
|
+
}),
|
|
695
769
|
]);
|
|
696
770
|
}
|
|
697
771
|
else if (expression.type == "FunctionDeclaration") {
|
|
@@ -742,8 +816,13 @@ class MinifyFile {
|
|
|
742
816
|
else {
|
|
743
817
|
// at this point, `field.type == 'TableKeyString'`
|
|
744
818
|
// TODO: keep track of nested scopes (#18)
|
|
819
|
+
const fieldRename = this.minifier.getFieldRename(field.key);
|
|
745
820
|
return this.sourceNodeHelper(field, [
|
|
746
|
-
|
|
821
|
+
fieldRename
|
|
822
|
+
? this.sourceNodeHelper(field.key, fieldRename.name, fieldRename.originalName)
|
|
823
|
+
: this.formatExpression(field.key, {
|
|
824
|
+
preserveIdentifiers: true,
|
|
825
|
+
}),
|
|
747
826
|
"=",
|
|
748
827
|
this.formatExpression(field.value),
|
|
749
828
|
comma,
|
|
@@ -759,16 +838,25 @@ class MinifyFile {
|
|
|
759
838
|
throw TypeError("Unknown expression type: `" + JSON.stringify(expression) + "`");
|
|
760
839
|
}
|
|
761
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* インデックス・メンバー参照・呼び出しの基底を出力する。
|
|
843
|
+
*
|
|
844
|
+
* Luaの文法で基底にそのまま置けるのは、変数・インデックス式・関数呼び出しだけで
|
|
845
|
+
* ある。それ以外の式は丸括弧で包まないと基底に置けない(`3 .x` も `"a":upper()`
|
|
846
|
+
* も構文エラーになる)。そのため、包む必要のある型を数え上げるのではなく、
|
|
847
|
+
* そのまま置ける形かどうかだけを見る。式の種類が増えても、元のソースには現れない
|
|
848
|
+
* 式を基底へ置く変換(定数畳み込みなど)が入っても、この判定だけで正しい括弧が付く。
|
|
849
|
+
*/
|
|
762
850
|
formatBase(base) {
|
|
763
851
|
const type = base.type;
|
|
764
|
-
const
|
|
765
|
-
type == "
|
|
766
|
-
type == "
|
|
767
|
-
type == "
|
|
768
|
-
type == "
|
|
769
|
-
type == "
|
|
852
|
+
const canStandAsBase = type == "Identifier" ||
|
|
853
|
+
type == "MemberExpression" ||
|
|
854
|
+
type == "IndexExpression" ||
|
|
855
|
+
type == "CallExpression" ||
|
|
856
|
+
type == "StringCallExpression" ||
|
|
857
|
+
type == "TableCallExpression";
|
|
770
858
|
const result = this.sourceNodeHelper(base, this.formatExpression(base));
|
|
771
|
-
if (
|
|
859
|
+
if (!canStandAsBase) {
|
|
772
860
|
prependWithSeparator(result, "(");
|
|
773
861
|
addWithSeparator(result, ")");
|
|
774
862
|
}
|
|
@@ -805,16 +893,16 @@ class MinifyFile {
|
|
|
805
893
|
// dofileは呼び出しごとに毎回展開しなおす(キャッシュしない)
|
|
806
894
|
return this.minifier.printModuleInline(ref.moduleName);
|
|
807
895
|
}
|
|
808
|
-
if (!this.mode.
|
|
896
|
+
if (!this.mode.requireWrapper) {
|
|
809
897
|
// SLモード(無オプション): requireもキャッシュせずその場展開する
|
|
810
898
|
// (挙動互換性のため、ホイストした共有ローカルへの参照にはしない)。
|
|
811
899
|
// 式の位置に置けるようにIIFEで包む。
|
|
812
900
|
const body = this.minifier.printModuleInline(ref.moduleName);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
901
|
+
const result = this.sourceNodeHelper(expression, "(function()");
|
|
902
|
+
addWithSeparator(result, body);
|
|
903
|
+
addWithSeparator(result, "end");
|
|
904
|
+
result.add(")()");
|
|
905
|
+
return result;
|
|
818
906
|
}
|
|
819
907
|
// -mモードのrequireはそのまま呼び出しとして出力し、実行時のrequire関数に委ねる
|
|
820
908
|
return undefined;
|
package/dist/astWalk.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.walkStatement = walkStatement;
|
|
4
|
+
exports.walkExpression = walkExpression;
|
|
5
|
+
exports.walkBlockDeep = walkBlockDeep;
|
|
6
|
+
function walkStatement(statement, visitor) {
|
|
7
|
+
visitor.onStatement?.(statement);
|
|
8
|
+
switch (statement.type) {
|
|
9
|
+
case "LocalStatement":
|
|
10
|
+
statement.init.forEach((expr) => {
|
|
11
|
+
walkExpression(expr, visitor);
|
|
12
|
+
});
|
|
13
|
+
return;
|
|
14
|
+
case "AssignmentStatement":
|
|
15
|
+
statement.variables.forEach((variable) => {
|
|
16
|
+
if (variable.type === "Identifier") {
|
|
17
|
+
visitor.onIdentifierReference?.(variable);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
walkExpression(variable, visitor);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
statement.init.forEach((expr) => {
|
|
24
|
+
walkExpression(expr, visitor);
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
case "CallStatement":
|
|
28
|
+
walkExpression(statement.expression, visitor);
|
|
29
|
+
return;
|
|
30
|
+
case "DoStatement":
|
|
31
|
+
visitor.onBlock?.(statement.body);
|
|
32
|
+
return;
|
|
33
|
+
case "WhileStatement":
|
|
34
|
+
walkExpression(statement.condition, visitor);
|
|
35
|
+
visitor.onBlock?.(statement.body);
|
|
36
|
+
return;
|
|
37
|
+
case "RepeatStatement":
|
|
38
|
+
visitor.onBlock?.(statement.body);
|
|
39
|
+
walkExpression(statement.condition, visitor);
|
|
40
|
+
return;
|
|
41
|
+
case "IfStatement":
|
|
42
|
+
statement.clauses.forEach((clause) => {
|
|
43
|
+
if (clause.type !== "ElseClause") {
|
|
44
|
+
walkExpression(clause.condition, visitor);
|
|
45
|
+
}
|
|
46
|
+
visitor.onBlock?.(clause.body);
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
case "ForNumericStatement":
|
|
50
|
+
walkExpression(statement.start, visitor);
|
|
51
|
+
walkExpression(statement.end, visitor);
|
|
52
|
+
if (statement.step)
|
|
53
|
+
walkExpression(statement.step, visitor);
|
|
54
|
+
visitor.onBlock?.(statement.body);
|
|
55
|
+
return;
|
|
56
|
+
case "ForGenericStatement":
|
|
57
|
+
statement.iterators.forEach((iterator) => {
|
|
58
|
+
walkExpression(iterator, visitor);
|
|
59
|
+
});
|
|
60
|
+
visitor.onBlock?.(statement.body);
|
|
61
|
+
return;
|
|
62
|
+
case "FunctionDeclaration":
|
|
63
|
+
if (statement.identifier) {
|
|
64
|
+
if (statement.identifier.type === "Identifier") {
|
|
65
|
+
if (!statement.isLocal) {
|
|
66
|
+
visitor.onIdentifierReference?.(statement.identifier);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
walkExpression(statement.identifier, visitor);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
visitor.onFunction?.(statement);
|
|
74
|
+
visitor.onBlock?.(statement.body);
|
|
75
|
+
return;
|
|
76
|
+
case "ReturnStatement":
|
|
77
|
+
statement.arguments.forEach((argument) => {
|
|
78
|
+
walkExpression(argument, visitor);
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
case "BreakStatement":
|
|
82
|
+
case "LabelStatement":
|
|
83
|
+
case "GotoStatement":
|
|
84
|
+
return;
|
|
85
|
+
default: {
|
|
86
|
+
const exhaustive = statement;
|
|
87
|
+
throw new TypeError("Unknown statement type: `" + JSON.stringify(exhaustive) + "`");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function walkExpression(expression, visitor) {
|
|
92
|
+
visitor.onExpression?.(expression);
|
|
93
|
+
switch (expression.type) {
|
|
94
|
+
case "Identifier":
|
|
95
|
+
visitor.onIdentifierReference?.(expression);
|
|
96
|
+
return;
|
|
97
|
+
case "StringLiteral":
|
|
98
|
+
case "NumericLiteral":
|
|
99
|
+
case "BooleanLiteral":
|
|
100
|
+
case "NilLiteral":
|
|
101
|
+
case "VarargLiteral":
|
|
102
|
+
return;
|
|
103
|
+
case "LogicalExpression":
|
|
104
|
+
case "BinaryExpression":
|
|
105
|
+
walkExpression(expression.left, visitor);
|
|
106
|
+
walkExpression(expression.right, visitor);
|
|
107
|
+
return;
|
|
108
|
+
case "UnaryExpression":
|
|
109
|
+
walkExpression(expression.argument, visitor);
|
|
110
|
+
return;
|
|
111
|
+
case "CallExpression":
|
|
112
|
+
walkExpression(expression.base, visitor);
|
|
113
|
+
expression.arguments.forEach((argument) => {
|
|
114
|
+
walkExpression(argument, visitor);
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
case "TableCallExpression":
|
|
118
|
+
walkExpression(expression.base, visitor);
|
|
119
|
+
walkExpression(expression.arguments, visitor);
|
|
120
|
+
return;
|
|
121
|
+
case "StringCallExpression":
|
|
122
|
+
walkExpression(expression.base, visitor);
|
|
123
|
+
walkExpression(expression.argument, visitor);
|
|
124
|
+
return;
|
|
125
|
+
case "IndexExpression":
|
|
126
|
+
walkExpression(expression.base, visitor);
|
|
127
|
+
walkExpression(expression.index, visitor);
|
|
128
|
+
return;
|
|
129
|
+
case "MemberExpression":
|
|
130
|
+
walkExpression(expression.base, visitor);
|
|
131
|
+
return;
|
|
132
|
+
case "FunctionDeclaration":
|
|
133
|
+
visitor.onFunction?.(expression);
|
|
134
|
+
visitor.onBlock?.(expression.body);
|
|
135
|
+
return;
|
|
136
|
+
case "TableConstructorExpression":
|
|
137
|
+
expression.fields.forEach((field) => {
|
|
138
|
+
if (field.type === "TableKey") {
|
|
139
|
+
walkExpression(field.key, visitor);
|
|
140
|
+
}
|
|
141
|
+
walkExpression(field.value, visitor);
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
default: {
|
|
145
|
+
const exhaustive = expression;
|
|
146
|
+
throw new TypeError("Unknown expression type: `" + JSON.stringify(exhaustive) + "`");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Walk a complete execution tree while preserving the shared node classification above. */
|
|
151
|
+
function walkBlockDeep(body, visitor) {
|
|
152
|
+
const deepVisitor = {
|
|
153
|
+
...visitor,
|
|
154
|
+
onBlock: (nested) => {
|
|
155
|
+
visitor.onBlock?.(nested);
|
|
156
|
+
walkBlockDeep(nested, visitor);
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
body.forEach((statement) => {
|
|
160
|
+
walkStatement(statement, deepVisitor);
|
|
161
|
+
});
|
|
162
|
+
}
|