terser 5.7.2 → 5.11.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/CHANGELOG.md +36 -0
- package/README.md +21 -2
- package/dist/bundle.min.js +705 -213
- package/lib/ast.js +6 -4
- package/lib/compress/drop-side-effect-free.js +9 -12
- package/lib/compress/evaluate.js +15 -12
- package/lib/compress/index.js +368 -66
- package/lib/compress/inference.js +27 -13
- package/lib/compress/native-objects.js +1 -0
- package/lib/compress/tighten-body.js +29 -7
- package/lib/minify.js +3 -1
- package/lib/mozilla-ast.js +48 -6
- package/lib/output.js +81 -26
- package/lib/parse.js +24 -8
- package/lib/propmangle.js +57 -34
- package/lib/scope.js +42 -21
- package/lib/transform.js +2 -2
- package/package.json +9 -9
- package/tools/terser.d.ts +36 -0
package/dist/bundle.min.js
CHANGED
@@ -1214,7 +1214,7 @@ function parse($TEXT, options) {
|
|
1214
1214
|
// Example: /* I count */ ( /* I don't */ foo() )
|
1215
1215
|
// Useful because comments_before property of call with parens outside
|
1216
1216
|
// contains both comments inside and outside these parens. Used to find the
|
1217
|
-
|
1217
|
+
|
1218
1218
|
const outer_comments_before_counts = new WeakMap();
|
1219
1219
|
|
1220
1220
|
options = defaults(options, {
|
@@ -1387,7 +1387,7 @@ function parse($TEXT, options) {
|
|
1387
1387
|
}
|
1388
1388
|
if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
|
1389
1389
|
next();
|
1390
|
-
var node =
|
1390
|
+
var node = import_statement();
|
1391
1391
|
semicolon();
|
1392
1392
|
return node;
|
1393
1393
|
}
|
@@ -1539,7 +1539,7 @@ function parse($TEXT, options) {
|
|
1539
1539
|
case "export":
|
1540
1540
|
if (!is_token(peek(), "punc", "(")) {
|
1541
1541
|
next();
|
1542
|
-
var node =
|
1542
|
+
var node = export_statement();
|
1543
1543
|
if (is("punc", ";")) semicolon();
|
1544
1544
|
return node;
|
1545
1545
|
}
|
@@ -2716,7 +2716,7 @@ function parse($TEXT, options) {
|
|
2716
2716
|
};
|
2717
2717
|
|
2718
2718
|
const is_not_method_start = () =>
|
2719
|
-
!is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=");
|
2719
|
+
!is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("punc", ";") && !is("operator", "=");
|
2720
2720
|
|
2721
2721
|
var is_async = false;
|
2722
2722
|
var is_static = false;
|
@@ -2831,7 +2831,15 @@ function parse($TEXT, options) {
|
|
2831
2831
|
}
|
2832
2832
|
}
|
2833
2833
|
|
2834
|
-
function
|
2834
|
+
function maybe_import_assertion() {
|
2835
|
+
if (is("name", "assert") && !has_newline_before(S.token)) {
|
2836
|
+
next();
|
2837
|
+
return object_or_destructuring_();
|
2838
|
+
}
|
2839
|
+
return null;
|
2840
|
+
}
|
2841
|
+
|
2842
|
+
function import_statement() {
|
2835
2843
|
var start = prev();
|
2836
2844
|
|
2837
2845
|
var imported_name;
|
@@ -2854,16 +2862,20 @@ function parse($TEXT, options) {
|
|
2854
2862
|
unexpected();
|
2855
2863
|
}
|
2856
2864
|
next();
|
2865
|
+
|
2866
|
+
const assert_clause = maybe_import_assertion();
|
2867
|
+
|
2857
2868
|
return new AST_Import({
|
2858
|
-
start
|
2859
|
-
imported_name
|
2860
|
-
imported_names
|
2869
|
+
start,
|
2870
|
+
imported_name,
|
2871
|
+
imported_names,
|
2861
2872
|
module_name: new AST_String({
|
2862
2873
|
start: mod_str,
|
2863
2874
|
value: mod_str.value,
|
2864
2875
|
quote: mod_str.quote,
|
2865
2876
|
end: mod_str,
|
2866
2877
|
}),
|
2878
|
+
assert_clause,
|
2867
2879
|
end: S.token,
|
2868
2880
|
});
|
2869
2881
|
}
|
@@ -2971,7 +2983,7 @@ function parse($TEXT, options) {
|
|
2971
2983
|
return names;
|
2972
2984
|
}
|
2973
2985
|
|
2974
|
-
function
|
2986
|
+
function export_statement() {
|
2975
2987
|
var start = S.token;
|
2976
2988
|
var is_default;
|
2977
2989
|
var exported_names;
|
@@ -2989,6 +3001,8 @@ function parse($TEXT, options) {
|
|
2989
3001
|
}
|
2990
3002
|
next();
|
2991
3003
|
|
3004
|
+
const assert_clause = maybe_import_assertion();
|
3005
|
+
|
2992
3006
|
return new AST_Export({
|
2993
3007
|
start: start,
|
2994
3008
|
is_default: is_default,
|
@@ -3000,6 +3014,7 @@ function parse($TEXT, options) {
|
|
3000
3014
|
end: mod_str,
|
3001
3015
|
}),
|
3002
3016
|
end: prev(),
|
3017
|
+
assert_clause
|
3003
3018
|
});
|
3004
3019
|
} else {
|
3005
3020
|
return new AST_Export({
|
@@ -3045,6 +3060,7 @@ function parse($TEXT, options) {
|
|
3045
3060
|
exported_value: exported_value,
|
3046
3061
|
exported_definition: exported_definition,
|
3047
3062
|
end: prev(),
|
3063
|
+
assert_clause: null
|
3048
3064
|
});
|
3049
3065
|
}
|
3050
3066
|
|
@@ -4378,12 +4394,13 @@ var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
|
|
4378
4394
|
},
|
4379
4395
|
});
|
4380
4396
|
|
4381
|
-
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
4397
|
+
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name assert_clause", {
|
4382
4398
|
$documentation: "An `import` statement",
|
4383
4399
|
$propdoc: {
|
4384
4400
|
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
|
4385
4401
|
imported_names: "[AST_NameMapping*] The names of non-default imported variables",
|
4386
4402
|
module_name: "[AST_String] String literal describing where this module came from",
|
4403
|
+
assert_clause: "[AST_Object?] The import assertion"
|
4387
4404
|
},
|
4388
4405
|
_walk: function(visitor) {
|
4389
4406
|
return visitor._visit(this, function() {
|
@@ -4412,14 +4429,15 @@ var AST_ImportMeta = DEFNODE("ImportMeta", null, {
|
|
4412
4429
|
$documentation: "A reference to import.meta",
|
4413
4430
|
});
|
4414
4431
|
|
4415
|
-
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
|
4432
|
+
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name assert_clause", {
|
4416
4433
|
$documentation: "An `export` statement",
|
4417
4434
|
$propdoc: {
|
4418
4435
|
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
4419
4436
|
exported_value: "[AST_Node?] An exported value",
|
4420
4437
|
exported_names: "[AST_NameMapping*?] List of exported names",
|
4421
4438
|
module_name: "[AST_String?] Name of the file to load exports from",
|
4422
|
-
is_default: "[Boolean] Whether this is the default exported value of this module"
|
4439
|
+
is_default: "[Boolean] Whether this is the default exported value of this module",
|
4440
|
+
assert_clause: "[AST_Object?] The import assertion"
|
4423
4441
|
},
|
4424
4442
|
_walk: function (visitor) {
|
4425
4443
|
return visitor._visit(this, function () {
|
@@ -4819,7 +4837,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", {
|
|
4819
4837
|
}
|
4820
4838
|
}, AST_ObjectProperty);
|
4821
4839
|
|
4822
|
-
var AST_ClassPrivateProperty = DEFNODE("
|
4840
|
+
var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", {
|
4823
4841
|
$documentation: "A class property for a private property",
|
4824
4842
|
}, AST_ClassProperty);
|
4825
4843
|
|
@@ -5521,7 +5539,7 @@ def_transform(AST_Sequence, function(self, tw) {
|
|
5521
5539
|
: [new AST_Number({ value: 0 })];
|
5522
5540
|
});
|
5523
5541
|
|
5524
|
-
def_transform(
|
5542
|
+
def_transform(AST_PropAccess, function(self, tw) {
|
5525
5543
|
self.expression = self.expression.transform(tw);
|
5526
5544
|
});
|
5527
5545
|
|
@@ -5672,6 +5690,24 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
5672
5690
|
return body;
|
5673
5691
|
};
|
5674
5692
|
|
5693
|
+
const assert_clause_from_moz = (assertions) => {
|
5694
|
+
if (assertions && assertions.length > 0) {
|
5695
|
+
return new AST_Object({
|
5696
|
+
start: my_start_token(assertions),
|
5697
|
+
end: my_end_token(assertions),
|
5698
|
+
properties: assertions.map((assertion_kv) =>
|
5699
|
+
new AST_ObjectKeyVal({
|
5700
|
+
start: my_start_token(assertion_kv),
|
5701
|
+
end: my_end_token(assertion_kv),
|
5702
|
+
key: assertion_kv.key.name || assertion_kv.key.value,
|
5703
|
+
value: from_moz(assertion_kv.value)
|
5704
|
+
})
|
5705
|
+
)
|
5706
|
+
});
|
5707
|
+
}
|
5708
|
+
return null;
|
5709
|
+
};
|
5710
|
+
|
5675
5711
|
var MOZ_TO_ME = {
|
5676
5712
|
Program: function(M) {
|
5677
5713
|
return new AST_Toplevel({
|
@@ -5992,7 +6028,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
5992
6028
|
end : my_end_token(M),
|
5993
6029
|
imported_name: imported_name,
|
5994
6030
|
imported_names : imported_names,
|
5995
|
-
module_name : from_moz(M.source)
|
6031
|
+
module_name : from_moz(M.source),
|
6032
|
+
assert_clause: assert_clause_from_moz(M.assertions)
|
5996
6033
|
});
|
5997
6034
|
},
|
5998
6035
|
ExportAllDeclaration: function(M) {
|
@@ -6005,7 +6042,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
6005
6042
|
foreign_name: new AST_SymbolExportForeign({ name: "*" })
|
6006
6043
|
})
|
6007
6044
|
],
|
6008
|
-
module_name: from_moz(M.source)
|
6045
|
+
module_name: from_moz(M.source),
|
6046
|
+
assert_clause: assert_clause_from_moz(M.assertions)
|
6009
6047
|
});
|
6010
6048
|
},
|
6011
6049
|
ExportNamedDeclaration: function(M) {
|
@@ -6019,7 +6057,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
6019
6057
|
name: from_moz(specifier.local)
|
6020
6058
|
});
|
6021
6059
|
}) : null,
|
6022
|
-
module_name: from_moz(M.source)
|
6060
|
+
module_name: from_moz(M.source),
|
6061
|
+
assert_clause: assert_clause_from_moz(M.assertions)
|
6023
6062
|
});
|
6024
6063
|
},
|
6025
6064
|
ExportDefaultDeclaration: function(M) {
|
@@ -6311,12 +6350,30 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
6311
6350
|
};
|
6312
6351
|
});
|
6313
6352
|
|
6353
|
+
const assert_clause_to_moz = assert_clause => {
|
6354
|
+
const assertions = [];
|
6355
|
+
if (assert_clause) {
|
6356
|
+
for (const { key, value } of assert_clause.properties) {
|
6357
|
+
const key_moz = is_basic_identifier_string(key)
|
6358
|
+
? { type: "Identifier", name: key }
|
6359
|
+
: { type: "Literal", value: key, raw: JSON.stringify(key) };
|
6360
|
+
assertions.push({
|
6361
|
+
type: "ImportAttribute",
|
6362
|
+
key: key_moz,
|
6363
|
+
value: to_moz(value)
|
6364
|
+
});
|
6365
|
+
}
|
6366
|
+
}
|
6367
|
+
return assertions;
|
6368
|
+
};
|
6369
|
+
|
6314
6370
|
def_to_moz(AST_Export, function To_Moz_ExportDeclaration(M) {
|
6315
6371
|
if (M.exported_names) {
|
6316
6372
|
if (M.exported_names[0].name.name === "*") {
|
6317
6373
|
return {
|
6318
6374
|
type: "ExportAllDeclaration",
|
6319
|
-
source: to_moz(M.module_name)
|
6375
|
+
source: to_moz(M.module_name),
|
6376
|
+
assertions: assert_clause_to_moz(M.assert_clause)
|
6320
6377
|
};
|
6321
6378
|
}
|
6322
6379
|
return {
|
@@ -6329,7 +6386,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
6329
6386
|
};
|
6330
6387
|
}),
|
6331
6388
|
declaration: to_moz(M.exported_definition),
|
6332
|
-
source: to_moz(M.module_name)
|
6389
|
+
source: to_moz(M.module_name),
|
6390
|
+
assertions: assert_clause_to_moz(M.assert_clause)
|
6333
6391
|
};
|
6334
6392
|
}
|
6335
6393
|
return {
|
@@ -6363,7 +6421,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
6363
6421
|
return {
|
6364
6422
|
type: "ImportDeclaration",
|
6365
6423
|
specifiers: specifiers,
|
6366
|
-
source: to_moz(M.module_name)
|
6424
|
+
source: to_moz(M.module_name),
|
6425
|
+
assertions: assert_clause_to_moz(M.assert_clause)
|
6367
6426
|
};
|
6368
6427
|
});
|
6369
6428
|
|
@@ -6920,6 +6979,48 @@ function is_some_comments(comment) {
|
|
6920
6979
|
);
|
6921
6980
|
}
|
6922
6981
|
|
6982
|
+
class Rope {
|
6983
|
+
constructor() {
|
6984
|
+
this.committed = "";
|
6985
|
+
this.current = "";
|
6986
|
+
}
|
6987
|
+
|
6988
|
+
append(str) {
|
6989
|
+
this.current += str;
|
6990
|
+
}
|
6991
|
+
|
6992
|
+
insertAt(char, index) {
|
6993
|
+
const { committed, current } = this;
|
6994
|
+
if (index < committed.length) {
|
6995
|
+
this.committed = committed.slice(0, index) + char + committed.slice(index);
|
6996
|
+
} else if (index === committed.length) {
|
6997
|
+
this.committed += char;
|
6998
|
+
} else {
|
6999
|
+
index -= committed.length;
|
7000
|
+
this.committed += current.slice(0, index) + char;
|
7001
|
+
this.current = current.slice(index);
|
7002
|
+
}
|
7003
|
+
}
|
7004
|
+
|
7005
|
+
charAt(index) {
|
7006
|
+
const { committed } = this;
|
7007
|
+
if (index < committed.length) return committed[index];
|
7008
|
+
return this.current[index - committed.length];
|
7009
|
+
}
|
7010
|
+
|
7011
|
+
curLength() {
|
7012
|
+
return this.current.length;
|
7013
|
+
}
|
7014
|
+
|
7015
|
+
length() {
|
7016
|
+
return this.committed.length + this.current.length;
|
7017
|
+
}
|
7018
|
+
|
7019
|
+
toString() {
|
7020
|
+
return this.committed + this.current;
|
7021
|
+
}
|
7022
|
+
}
|
7023
|
+
|
6923
7024
|
function OutputStream(options) {
|
6924
7025
|
|
6925
7026
|
var readonly = !options;
|
@@ -6984,11 +7085,11 @@ function OutputStream(options) {
|
|
6984
7085
|
var current_col = 0;
|
6985
7086
|
var current_line = 1;
|
6986
7087
|
var current_pos = 0;
|
6987
|
-
var OUTPUT =
|
7088
|
+
var OUTPUT = new Rope();
|
6988
7089
|
let printed_comments = new Set();
|
6989
7090
|
|
6990
|
-
var to_utf8 = options.ascii_only ? function(str, identifier) {
|
6991
|
-
if (options.ecma >= 2015 && !options.safari10) {
|
7091
|
+
var to_utf8 = options.ascii_only ? function(str, identifier = false, regexp = false) {
|
7092
|
+
if (options.ecma >= 2015 && !options.safari10 && !regexp) {
|
6992
7093
|
str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
|
6993
7094
|
var code = get_full_char_code(ch, 0).toString(16);
|
6994
7095
|
return "\\u{" + code + "}";
|
@@ -7093,9 +7194,11 @@ function OutputStream(options) {
|
|
7093
7194
|
var do_add_mapping = mappings ? function() {
|
7094
7195
|
mappings.forEach(function(mapping) {
|
7095
7196
|
try {
|
7096
|
-
let
|
7097
|
-
if (name
|
7098
|
-
name =
|
7197
|
+
let { name, token } = mapping;
|
7198
|
+
if (token.type == "name" || token.type === "privatename") {
|
7199
|
+
name = token.value;
|
7200
|
+
} else if (name instanceof AST_Symbol) {
|
7201
|
+
name = token.type === "string" ? token.value : name.name;
|
7099
7202
|
}
|
7100
7203
|
options.source_map.add(
|
7101
7204
|
mapping.token.file,
|
@@ -7113,19 +7216,18 @@ function OutputStream(options) {
|
|
7113
7216
|
var ensure_line_len = options.max_line_len ? function() {
|
7114
7217
|
if (current_col > options.max_line_len) {
|
7115
7218
|
if (might_add_newline) {
|
7116
|
-
|
7117
|
-
|
7219
|
+
OUTPUT.insertAt("\n", might_add_newline);
|
7220
|
+
const curLength = OUTPUT.curLength();
|
7118
7221
|
if (mappings) {
|
7119
|
-
var delta =
|
7222
|
+
var delta = curLength - current_col;
|
7120
7223
|
mappings.forEach(function(mapping) {
|
7121
7224
|
mapping.line++;
|
7122
7225
|
mapping.col += delta;
|
7123
7226
|
});
|
7124
7227
|
}
|
7125
|
-
OUTPUT = left + "\n" + right;
|
7126
7228
|
current_line++;
|
7127
7229
|
current_pos++;
|
7128
|
-
current_col =
|
7230
|
+
current_col = curLength;
|
7129
7231
|
}
|
7130
7232
|
}
|
7131
7233
|
if (might_add_newline) {
|
@@ -7159,13 +7261,13 @@ function OutputStream(options) {
|
|
7159
7261
|
|
7160
7262
|
if (prev === ":" && ch === "}" || (!ch || !";}".includes(ch)) && prev !== ";") {
|
7161
7263
|
if (options.semicolons || requireSemicolonChars.has(ch)) {
|
7162
|
-
OUTPUT
|
7264
|
+
OUTPUT.append(";");
|
7163
7265
|
current_col++;
|
7164
7266
|
current_pos++;
|
7165
7267
|
} else {
|
7166
7268
|
ensure_line_len();
|
7167
7269
|
if (current_col > 0) {
|
7168
|
-
OUTPUT
|
7270
|
+
OUTPUT.append("\n");
|
7169
7271
|
current_pos++;
|
7170
7272
|
current_line++;
|
7171
7273
|
current_col = 0;
|
@@ -7189,7 +7291,7 @@ function OutputStream(options) {
|
|
7189
7291
|
|| (ch == "/" && ch == prev)
|
7190
7292
|
|| ((ch == "+" || ch == "-") && ch == last)
|
7191
7293
|
) {
|
7192
|
-
OUTPUT
|
7294
|
+
OUTPUT.append(" ");
|
7193
7295
|
current_col++;
|
7194
7296
|
current_pos++;
|
7195
7297
|
}
|
@@ -7207,7 +7309,7 @@ function OutputStream(options) {
|
|
7207
7309
|
if (!might_add_newline) do_add_mapping();
|
7208
7310
|
}
|
7209
7311
|
|
7210
|
-
OUTPUT
|
7312
|
+
OUTPUT.append(str);
|
7211
7313
|
has_parens = str[str.length - 1] == "(";
|
7212
7314
|
current_pos += str.length;
|
7213
7315
|
var a = str.split(/\r?\n/), n = a.length - 1;
|
@@ -7247,15 +7349,15 @@ function OutputStream(options) {
|
|
7247
7349
|
|
7248
7350
|
var newline = options.beautify ? function() {
|
7249
7351
|
if (newline_insert < 0) return print("\n");
|
7250
|
-
if (OUTPUT
|
7251
|
-
OUTPUT
|
7352
|
+
if (OUTPUT.charAt(newline_insert) != "\n") {
|
7353
|
+
OUTPUT.insertAt("\n", newline_insert);
|
7252
7354
|
current_pos++;
|
7253
7355
|
current_line++;
|
7254
7356
|
}
|
7255
7357
|
newline_insert++;
|
7256
7358
|
} : options.max_line_len ? function() {
|
7257
7359
|
ensure_line_len();
|
7258
|
-
might_add_newline = OUTPUT.length;
|
7360
|
+
might_add_newline = OUTPUT.length();
|
7259
7361
|
} : noop;
|
7260
7362
|
|
7261
7363
|
var semicolon = options.beautify ? function() {
|
@@ -7321,13 +7423,14 @@ function OutputStream(options) {
|
|
7321
7423
|
if (might_add_newline) {
|
7322
7424
|
ensure_line_len();
|
7323
7425
|
}
|
7324
|
-
return OUTPUT;
|
7426
|
+
return OUTPUT.toString();
|
7325
7427
|
}
|
7326
7428
|
|
7327
7429
|
function has_nlb() {
|
7328
|
-
|
7430
|
+
const output = OUTPUT.toString();
|
7431
|
+
let n = output.length - 1;
|
7329
7432
|
while (n >= 0) {
|
7330
|
-
const code =
|
7433
|
+
const code = output.charCodeAt(n);
|
7331
7434
|
if (code === CODE_LINE_BREAK) {
|
7332
7435
|
return true;
|
7333
7436
|
}
|
@@ -7464,7 +7567,7 @@ function OutputStream(options) {
|
|
7464
7567
|
!/comment[134]/.test(c.type)
|
7465
7568
|
))) return;
|
7466
7569
|
printed_comments.add(comments);
|
7467
|
-
var insert = OUTPUT.length;
|
7570
|
+
var insert = OUTPUT.length();
|
7468
7571
|
comments.filter(comment_filter, node).forEach(function(c, i) {
|
7469
7572
|
if (printed_comments.has(c)) return;
|
7470
7573
|
printed_comments.add(c);
|
@@ -7493,7 +7596,7 @@ function OutputStream(options) {
|
|
7493
7596
|
need_space = true;
|
7494
7597
|
}
|
7495
7598
|
});
|
7496
|
-
if (OUTPUT.length > insert) newline_insert = insert;
|
7599
|
+
if (OUTPUT.length() > insert) newline_insert = insert;
|
7497
7600
|
}
|
7498
7601
|
|
7499
7602
|
var stack = [];
|
@@ -7523,7 +7626,7 @@ function OutputStream(options) {
|
|
7523
7626
|
var encoded = encode_string(str, quote);
|
7524
7627
|
if (escape_directive === true && !encoded.includes("\\")) {
|
7525
7628
|
// Insert semicolons to break directive prologue
|
7526
|
-
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
|
7629
|
+
if (!EXPECT_DIRECTIVE.test(OUTPUT.toString())) {
|
7527
7630
|
force_semicolon();
|
7528
7631
|
}
|
7529
7632
|
force_semicolon();
|
@@ -8379,6 +8482,10 @@ function OutputStream(options) {
|
|
8379
8482
|
output.space();
|
8380
8483
|
}
|
8381
8484
|
self.module_name.print(output);
|
8485
|
+
if (self.assert_clause) {
|
8486
|
+
output.print("assert");
|
8487
|
+
self.assert_clause.print(output);
|
8488
|
+
}
|
8382
8489
|
output.semicolon();
|
8383
8490
|
});
|
8384
8491
|
DEFPRINT(AST_ImportMeta, function(self, output) {
|
@@ -8444,6 +8551,10 @@ function OutputStream(options) {
|
|
8444
8551
|
output.space();
|
8445
8552
|
self.module_name.print(output);
|
8446
8553
|
}
|
8554
|
+
if (self.assert_clause) {
|
8555
|
+
output.print("assert");
|
8556
|
+
self.assert_clause.print(output);
|
8557
|
+
}
|
8447
8558
|
if (self.exported_value
|
8448
8559
|
&& !(self.exported_value instanceof AST_Defun ||
|
8449
8560
|
self.exported_value instanceof AST_Function ||
|
@@ -8568,6 +8679,7 @@ function OutputStream(options) {
|
|
8568
8679
|
|
8569
8680
|
if (self.optional) output.print("?");
|
8570
8681
|
output.print(".#");
|
8682
|
+
output.add_mapping(self.end);
|
8571
8683
|
output.print_name(prop);
|
8572
8684
|
});
|
8573
8685
|
DEFPRINT(AST_Sub, function(self, output) {
|
@@ -8904,7 +9016,7 @@ function OutputStream(options) {
|
|
8904
9016
|
flags = flags ? sort_regexp_flags(flags) : "";
|
8905
9017
|
source = source.replace(r_slash_script, slash_script_replace);
|
8906
9018
|
|
8907
|
-
output.print(output.to_utf8(`/${source}/${flags}
|
9019
|
+
output.print(output.to_utf8(`/${source}/${flags}`, false, true));
|
8908
9020
|
|
8909
9021
|
const parent = output.parent();
|
8910
9022
|
if (
|
@@ -9021,8 +9133,10 @@ function OutputStream(options) {
|
|
9021
9133
|
DEFMAP([
|
9022
9134
|
AST_ObjectGetter,
|
9023
9135
|
AST_ObjectSetter,
|
9136
|
+
AST_PrivateGetter,
|
9137
|
+
AST_PrivateSetter,
|
9024
9138
|
], function(output) {
|
9025
|
-
output.add_mapping(this.
|
9139
|
+
output.add_mapping(this.key.end, this.key.name);
|
9026
9140
|
});
|
9027
9141
|
|
9028
9142
|
DEFMAP([ AST_ObjectProperty ], function(output) {
|
@@ -9877,8 +9991,9 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
|
9877
9991
|
|
9878
9992
|
function next_mangled(scope, options) {
|
9879
9993
|
var ext = scope.enclosed;
|
9994
|
+
var nth_identifier = options.nth_identifier;
|
9880
9995
|
out: while (true) {
|
9881
|
-
var m =
|
9996
|
+
var m = nth_identifier.get(++scope.cname);
|
9882
9997
|
if (ALL_RESERVED_WORDS.has(m)) continue; // skip over "do"
|
9883
9998
|
|
9884
9999
|
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
@@ -9954,6 +10069,7 @@ AST_Symbol.DEFMETHOD("global", function() {
|
|
9954
10069
|
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
|
9955
10070
|
options = defaults(options, {
|
9956
10071
|
eval : false,
|
10072
|
+
nth_identifier : base54,
|
9957
10073
|
ie8 : false,
|
9958
10074
|
keep_classnames: false,
|
9959
10075
|
keep_fnames : false,
|
@@ -9975,6 +10091,7 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
|
|
9975
10091
|
|
9976
10092
|
AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
9977
10093
|
options = this._default_mangler_options(options);
|
10094
|
+
var nth_identifier = options.nth_identifier;
|
9978
10095
|
|
9979
10096
|
// We only need to mangle declaration nodes. Special logic wired
|
9980
10097
|
// into the code generator will display the mangled name if it's
|
@@ -9988,6 +10105,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
|
9988
10105
|
}
|
9989
10106
|
|
9990
10107
|
const mangled_names = this.mangled_names = new Set();
|
10108
|
+
unmangleable_names = new Set();
|
10109
|
+
|
9991
10110
|
if (options.cache) {
|
9992
10111
|
this.globals.forEach(collect);
|
9993
10112
|
if (options.cache.props) {
|
@@ -10026,7 +10145,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
|
10026
10145
|
if (node instanceof AST_Label) {
|
10027
10146
|
let name;
|
10028
10147
|
do {
|
10029
|
-
name =
|
10148
|
+
name = nth_identifier.get(++lname);
|
10030
10149
|
} while (ALL_RESERVED_WORDS.has(name));
|
10031
10150
|
node.mangled_name = name;
|
10032
10151
|
return true;
|
@@ -10040,7 +10159,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
|
10040
10159
|
this.walk(tw);
|
10041
10160
|
|
10042
10161
|
if (options.keep_fnames || options.keep_classnames) {
|
10043
|
-
unmangleable_names = new Set();
|
10044
10162
|
// Collect a set of short names which are unmangleable,
|
10045
10163
|
// for use in avoiding collisions in next_mangled.
|
10046
10164
|
to_mangle.forEach(def => {
|
@@ -10056,9 +10174,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
|
10056
10174
|
unmangleable_names = null;
|
10057
10175
|
|
10058
10176
|
function collect(symbol) {
|
10059
|
-
|
10060
|
-
|
10061
|
-
if (
|
10177
|
+
if (symbol.export & MASK_EXPORT_DONT_MANGLE) {
|
10178
|
+
unmangleable_names.add(symbol.name);
|
10179
|
+
} else if (!options.reserved.has(symbol.name)) {
|
10062
10180
|
to_mangle.push(symbol);
|
10063
10181
|
}
|
10064
10182
|
}
|
@@ -10088,9 +10206,12 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
|
|
10088
10206
|
});
|
10089
10207
|
|
10090
10208
|
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
10091
|
-
base54.reset();
|
10092
|
-
base54.sort();
|
10093
10209
|
options = this._default_mangler_options(options);
|
10210
|
+
var nth_identifier = options.nth_identifier;
|
10211
|
+
if (nth_identifier.reset && nth_identifier.sort) {
|
10212
|
+
nth_identifier.reset();
|
10213
|
+
nth_identifier.sort();
|
10214
|
+
}
|
10094
10215
|
var avoid = this.find_colliding_names(options);
|
10095
10216
|
var cname = 0;
|
10096
10217
|
this.globals.forEach(rename);
|
@@ -10102,7 +10223,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
|
10102
10223
|
function next_name() {
|
10103
10224
|
var name;
|
10104
10225
|
do {
|
10105
|
-
name =
|
10226
|
+
name = nth_identifier.get(cname++);
|
10106
10227
|
} while (avoid.has(name) || ALL_RESERVED_WORDS.has(name));
|
10107
10228
|
return name;
|
10108
10229
|
}
|
@@ -10129,30 +10250,37 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
|
|
10129
10250
|
|
10130
10251
|
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
|
10131
10252
|
options = this._default_mangler_options(options);
|
10253
|
+
var nth_identifier = options.nth_identifier;
|
10254
|
+
if (!nth_identifier.reset || !nth_identifier.consider || !nth_identifier.sort) {
|
10255
|
+
// If the identifier mangler is invariant, skip computing character frequency.
|
10256
|
+
return;
|
10257
|
+
}
|
10258
|
+
nth_identifier.reset();
|
10259
|
+
|
10132
10260
|
try {
|
10133
10261
|
AST_Node.prototype.print = function(stream, force_parens) {
|
10134
10262
|
this._print(stream, force_parens);
|
10135
10263
|
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
|
10136
|
-
|
10264
|
+
nth_identifier.consider(this.name, -1);
|
10137
10265
|
} else if (options.properties) {
|
10138
10266
|
if (this instanceof AST_DotHash) {
|
10139
|
-
|
10267
|
+
nth_identifier.consider("#" + this.property, -1);
|
10140
10268
|
} else if (this instanceof AST_Dot) {
|
10141
|
-
|
10269
|
+
nth_identifier.consider(this.property, -1);
|
10142
10270
|
} else if (this instanceof AST_Sub) {
|
10143
10271
|
skip_string(this.property);
|
10144
10272
|
}
|
10145
10273
|
}
|
10146
10274
|
};
|
10147
|
-
|
10275
|
+
nth_identifier.consider(this.print_to_string(), 1);
|
10148
10276
|
} finally {
|
10149
10277
|
AST_Node.prototype.print = AST_Node.prototype._print;
|
10150
10278
|
}
|
10151
|
-
|
10279
|
+
nth_identifier.sort();
|
10152
10280
|
|
10153
10281
|
function skip_string(node) {
|
10154
10282
|
if (node instanceof AST_String) {
|
10155
|
-
|
10283
|
+
nth_identifier.consider(node.value, -1);
|
10156
10284
|
} else if (node instanceof AST_Conditional) {
|
10157
10285
|
skip_string(node.consequent);
|
10158
10286
|
skip_string(node.alternative);
|
@@ -10176,19 +10304,20 @@ const base54 = (() => {
|
|
10176
10304
|
frequency.set(ch, 0);
|
10177
10305
|
});
|
10178
10306
|
}
|
10179
|
-
|
10307
|
+
function consider(str, delta) {
|
10180
10308
|
for (var i = str.length; --i >= 0;) {
|
10181
10309
|
frequency.set(str[i], frequency.get(str[i]) + delta);
|
10182
10310
|
}
|
10183
|
-
}
|
10311
|
+
}
|
10184
10312
|
function compare(a, b) {
|
10185
10313
|
return frequency.get(b) - frequency.get(a);
|
10186
10314
|
}
|
10187
|
-
|
10315
|
+
function sort() {
|
10188
10316
|
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
|
10189
|
-
}
|
10190
|
-
|
10317
|
+
}
|
10318
|
+
// Ensure this is in a usable initial state.
|
10191
10319
|
reset();
|
10320
|
+
sort();
|
10192
10321
|
function base54(num) {
|
10193
10322
|
var ret = "", base = 54;
|
10194
10323
|
num++;
|
@@ -10200,7 +10329,13 @@ const base54 = (() => {
|
|
10200
10329
|
} while (num > 0);
|
10201
10330
|
return ret;
|
10202
10331
|
}
|
10203
|
-
|
10332
|
+
|
10333
|
+
return {
|
10334
|
+
get: base54,
|
10335
|
+
consider,
|
10336
|
+
reset,
|
10337
|
+
sort
|
10338
|
+
};
|
10204
10339
|
})();
|
10205
10340
|
|
10206
10341
|
let mangle_options = undefined;
|
@@ -11079,6 +11214,7 @@ const is_pure_native_fn = make_nested_lookup({
|
|
11079
11214
|
"isExtensible",
|
11080
11215
|
"isFrozen",
|
11081
11216
|
"isSealed",
|
11217
|
+
"hasOwn",
|
11082
11218
|
"keys",
|
11083
11219
|
],
|
11084
11220
|
String: [
|
@@ -11252,9 +11388,8 @@ function is_undefined(node, compressor) {
|
|
11252
11388
|
);
|
11253
11389
|
}
|
11254
11390
|
|
11255
|
-
//
|
11256
|
-
|
11257
|
-
function is_nullish(node, compressor) {
|
11391
|
+
// Is the node explicitly null or undefined.
|
11392
|
+
function is_null_or_undefined(node, compressor) {
|
11258
11393
|
let fixed;
|
11259
11394
|
return (
|
11260
11395
|
node instanceof AST_Null
|
@@ -11264,13 +11399,29 @@ function is_nullish(node, compressor) {
|
|
11264
11399
|
&& (fixed = node.definition().fixed) instanceof AST_Node
|
11265
11400
|
&& is_nullish(fixed, compressor)
|
11266
11401
|
)
|
11267
|
-
// Recurse into those optional chains!
|
11268
|
-
|| node instanceof AST_PropAccess && node.optional && is_nullish(node.expression, compressor)
|
11269
|
-
|| node instanceof AST_Call && node.optional && is_nullish(node.expression, compressor)
|
11270
|
-
|| node instanceof AST_Chain && is_nullish(node.expression, compressor)
|
11271
11402
|
);
|
11272
11403
|
}
|
11273
11404
|
|
11405
|
+
// Find out if this expression is optionally chained from a base-point that we
|
11406
|
+
// can statically analyze as null or undefined.
|
11407
|
+
function is_nullish_shortcircuited(node, compressor) {
|
11408
|
+
if (node instanceof AST_PropAccess || node instanceof AST_Call) {
|
11409
|
+
return (
|
11410
|
+
(node.optional && is_null_or_undefined(node.expression, compressor))
|
11411
|
+
|| is_nullish_shortcircuited(node.expression, compressor)
|
11412
|
+
);
|
11413
|
+
}
|
11414
|
+
if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor);
|
11415
|
+
return false;
|
11416
|
+
}
|
11417
|
+
|
11418
|
+
// Find out if something is == null, or can short circuit into nullish.
|
11419
|
+
// Used to optimize ?. and ??
|
11420
|
+
function is_nullish(node, compressor) {
|
11421
|
+
if (is_null_or_undefined(node, compressor)) return true;
|
11422
|
+
return is_nullish_shortcircuited(node, compressor);
|
11423
|
+
}
|
11424
|
+
|
11274
11425
|
// Determine if expression might cause side effects
|
11275
11426
|
// If there's a possibility that a node may change something when it's executed, this returns true
|
11276
11427
|
(function(def_has_side_effects) {
|
@@ -11378,13 +11529,12 @@ function is_nullish(node, compressor) {
|
|
11378
11529
|
return any(this.elements, compressor);
|
11379
11530
|
});
|
11380
11531
|
def_has_side_effects(AST_Dot, function(compressor) {
|
11532
|
+
if (is_nullish(this, compressor)) return false;
|
11381
11533
|
return !this.optional && this.expression.may_throw_on_access(compressor)
|
11382
11534
|
|| this.expression.has_side_effects(compressor);
|
11383
11535
|
});
|
11384
11536
|
def_has_side_effects(AST_Sub, function(compressor) {
|
11385
|
-
if (
|
11386
|
-
return false;
|
11387
|
-
}
|
11537
|
+
if (is_nullish(this, compressor)) return false;
|
11388
11538
|
|
11389
11539
|
return !this.optional && this.expression.may_throw_on_access(compressor)
|
11390
11540
|
|| this.expression.has_side_effects(compressor)
|
@@ -11452,7 +11602,7 @@ function is_nullish(node, compressor) {
|
|
11452
11602
|
return any(this.body, compressor);
|
11453
11603
|
});
|
11454
11604
|
def_may_throw(AST_Call, function(compressor) {
|
11455
|
-
if (
|
11605
|
+
if (is_nullish(this, compressor)) return false;
|
11456
11606
|
if (any(this.args, compressor)) return true;
|
11457
11607
|
if (this.is_callee_pure(compressor)) return false;
|
11458
11608
|
if (this.expression.may_throw(compressor)) return true;
|
@@ -11511,12 +11661,12 @@ function is_nullish(node, compressor) {
|
|
11511
11661
|
return this.body.may_throw(compressor);
|
11512
11662
|
});
|
11513
11663
|
def_may_throw(AST_Dot, function(compressor) {
|
11664
|
+
if (is_nullish(this, compressor)) return false;
|
11514
11665
|
return !this.optional && this.expression.may_throw_on_access(compressor)
|
11515
11666
|
|| this.expression.may_throw(compressor);
|
11516
11667
|
});
|
11517
11668
|
def_may_throw(AST_Sub, function(compressor) {
|
11518
|
-
if (
|
11519
|
-
|
11669
|
+
if (is_nullish(this, compressor)) return false;
|
11520
11670
|
return !this.optional && this.expression.may_throw_on_access(compressor)
|
11521
11671
|
|| this.expression.may_throw(compressor)
|
11522
11672
|
|| this.property.may_throw(compressor);
|
@@ -12008,6 +12158,9 @@ function def_eval(node, func) {
|
|
12008
12158
|
node.DEFMETHOD("_eval", func);
|
12009
12159
|
}
|
12010
12160
|
|
12161
|
+
// Used to propagate a nullish short-circuit signal upwards through the chain.
|
12162
|
+
const nullish = Symbol("This AST_Chain is nullish");
|
12163
|
+
|
12011
12164
|
// If the node has been successfully reduced to a constant,
|
12012
12165
|
// then its value is returned; otherwise the element itself
|
12013
12166
|
// is returned.
|
@@ -12019,7 +12172,7 @@ AST_Node.DEFMETHOD("evaluate", function (compressor) {
|
|
12019
12172
|
var val = this._eval(compressor, 1);
|
12020
12173
|
if (!val || val instanceof RegExp)
|
12021
12174
|
return val;
|
12022
|
-
if (typeof val == "function" || typeof val == "object")
|
12175
|
+
if (typeof val == "function" || typeof val == "object" || val == nullish)
|
12023
12176
|
return this;
|
12024
12177
|
return val;
|
12025
12178
|
});
|
@@ -12264,11 +12417,8 @@ const regexp_flags = new Set([
|
|
12264
12417
|
]);
|
12265
12418
|
|
12266
12419
|
def_eval(AST_PropAccess, function (compressor, depth) {
|
12267
|
-
|
12268
|
-
|
12269
|
-
if (obj == null)
|
12270
|
-
return undefined;
|
12271
|
-
}
|
12420
|
+
const obj = this.expression._eval(compressor, depth);
|
12421
|
+
if (obj === nullish || (this.optional && obj == null)) return nullish;
|
12272
12422
|
if (compressor.option("unsafe")) {
|
12273
12423
|
var key = this.property;
|
12274
12424
|
if (key instanceof AST_Node) {
|
@@ -12323,16 +12473,19 @@ def_eval(AST_PropAccess, function (compressor, depth) {
|
|
12323
12473
|
|
12324
12474
|
def_eval(AST_Chain, function (compressor, depth) {
|
12325
12475
|
const evaluated = this.expression._eval(compressor, depth);
|
12326
|
-
return evaluated ===
|
12476
|
+
return evaluated === nullish
|
12477
|
+
? undefined
|
12478
|
+
: evaluated === this.expression
|
12479
|
+
? this
|
12480
|
+
: evaluated;
|
12327
12481
|
});
|
12328
12482
|
|
12329
12483
|
def_eval(AST_Call, function (compressor, depth) {
|
12330
12484
|
var exp = this.expression;
|
12331
|
-
|
12332
|
-
|
12333
|
-
|
12334
|
-
|
12335
|
-
}
|
12485
|
+
|
12486
|
+
const callee = exp._eval(compressor, depth);
|
12487
|
+
if (callee === nullish || (this.optional && callee == null)) return nullish;
|
12488
|
+
|
12336
12489
|
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
12337
12490
|
var key = exp.property;
|
12338
12491
|
if (key instanceof AST_Node) {
|
@@ -12464,8 +12617,8 @@ def_drop_side_effect_free(AST_Constant, return_null);
|
|
12464
12617
|
def_drop_side_effect_free(AST_This, return_null);
|
12465
12618
|
|
12466
12619
|
def_drop_side_effect_free(AST_Call, function (compressor, first_in_statement) {
|
12467
|
-
if (
|
12468
|
-
return
|
12620
|
+
if (is_nullish_shortcircuited(this, compressor)) {
|
12621
|
+
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
12469
12622
|
}
|
12470
12623
|
|
12471
12624
|
if (!this.is_callee_pure(compressor)) {
|
@@ -12641,21 +12794,19 @@ def_drop_side_effect_free(AST_Array, function (compressor, first_in_statement) {
|
|
12641
12794
|
});
|
12642
12795
|
|
12643
12796
|
def_drop_side_effect_free(AST_Dot, function (compressor, first_in_statement) {
|
12644
|
-
if (this
|
12645
|
-
return
|
12797
|
+
if (is_nullish_shortcircuited(this, compressor)) {
|
12798
|
+
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
12646
12799
|
}
|
12647
|
-
if (this.expression.may_throw_on_access(compressor))
|
12648
|
-
return this;
|
12800
|
+
if (this.expression.may_throw_on_access(compressor)) return this;
|
12649
12801
|
|
12650
12802
|
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
12651
12803
|
});
|
12652
12804
|
|
12653
12805
|
def_drop_side_effect_free(AST_Sub, function (compressor, first_in_statement) {
|
12654
|
-
if (this
|
12655
|
-
return
|
12806
|
+
if (is_nullish_shortcircuited(this, compressor)) {
|
12807
|
+
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
12656
12808
|
}
|
12657
|
-
if (this.expression.may_throw_on_access(compressor))
|
12658
|
-
return this;
|
12809
|
+
if (this.expression.may_throw_on_access(compressor)) return this;
|
12659
12810
|
|
12660
12811
|
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
12661
12812
|
if (!expression)
|
@@ -13527,7 +13678,9 @@ function tighten_body(statements, compressor) {
|
|
13527
13678
|
// Replace variable with assignment when found
|
13528
13679
|
if (can_replace
|
13529
13680
|
&& !(node instanceof AST_SymbolDeclaration)
|
13530
|
-
&& lhs.equivalent_to(node)
|
13681
|
+
&& lhs.equivalent_to(node)
|
13682
|
+
&& !shadows(node.scope, lvalues)
|
13683
|
+
) {
|
13531
13684
|
if (stop_if_hit) {
|
13532
13685
|
abort = true;
|
13533
13686
|
return node;
|
@@ -13575,7 +13728,7 @@ function tighten_body(statements, compressor) {
|
|
13575
13728
|
|| node instanceof AST_PropAccess
|
13576
13729
|
&& (side_effects || node.expression.may_throw_on_access(compressor))
|
13577
13730
|
|| node instanceof AST_SymbolRef
|
13578
|
-
&& (lvalues.get(node.name) || side_effects && may_modify(node))
|
13731
|
+
&& ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node))
|
13579
13732
|
|| node instanceof AST_VarDef && node.value
|
13580
13733
|
&& (lvalues.has(node.name.name) || side_effects && may_modify(node.name))
|
13581
13734
|
|| (sym = is_lhs(node.left, node))
|
@@ -13648,8 +13801,9 @@ function tighten_body(statements, compressor) {
|
|
13648
13801
|
// Locate symbols which may execute code outside of scanning range
|
13649
13802
|
var lvalues = get_lvalues(candidate);
|
13650
13803
|
var lhs_local = is_lhs_local(lhs);
|
13651
|
-
if (lhs instanceof AST_SymbolRef)
|
13652
|
-
lvalues.set(lhs.name, false);
|
13804
|
+
if (lhs instanceof AST_SymbolRef) {
|
13805
|
+
lvalues.set(lhs.name, { def: lhs.definition(), modified: false });
|
13806
|
+
}
|
13653
13807
|
var side_effects = value_has_side_effects(candidate);
|
13654
13808
|
var replace_all = replace_all_symbols();
|
13655
13809
|
var may_throw = candidate.may_throw(compressor);
|
@@ -13715,8 +13869,9 @@ function tighten_body(statements, compressor) {
|
|
13715
13869
|
return false;
|
13716
13870
|
let cur_scope = def.scope;
|
13717
13871
|
while (cur_scope && cur_scope !== scope) {
|
13718
|
-
if (cur_scope.variables.has(def.name))
|
13872
|
+
if (cur_scope.variables.has(def.name)) {
|
13719
13873
|
return true;
|
13874
|
+
}
|
13720
13875
|
cur_scope = cur_scope.parent_scope;
|
13721
13876
|
}
|
13722
13877
|
return false;
|
@@ -13997,8 +14152,14 @@ function tighten_body(statements, compressor) {
|
|
13997
14152
|
var sym = node;
|
13998
14153
|
while (sym instanceof AST_PropAccess)
|
13999
14154
|
sym = sym.expression;
|
14000
|
-
if (sym instanceof AST_SymbolRef
|
14001
|
-
|
14155
|
+
if (sym instanceof AST_SymbolRef) {
|
14156
|
+
const prev = lvalues.get(sym.name);
|
14157
|
+
if (!prev || !prev.modified) {
|
14158
|
+
lvalues.set(sym.name, {
|
14159
|
+
def: sym.definition(),
|
14160
|
+
modified: is_modified(compressor, tw, node, node, 0)
|
14161
|
+
});
|
14162
|
+
}
|
14002
14163
|
}
|
14003
14164
|
});
|
14004
14165
|
get_rvalue(expr).walk(tw);
|
@@ -14110,6 +14271,18 @@ function tighten_body(statements, compressor) {
|
|
14110
14271
|
}
|
14111
14272
|
return false;
|
14112
14273
|
}
|
14274
|
+
|
14275
|
+
function shadows(newScope, lvalues) {
|
14276
|
+
for (const {def} of lvalues.values()) {
|
14277
|
+
let current = newScope;
|
14278
|
+
while (current && current !== def.scope) {
|
14279
|
+
let nested_def = current.variables.get(def.name);
|
14280
|
+
if (nested_def && nested_def !== def) return true;
|
14281
|
+
current = current.parent_scope;
|
14282
|
+
}
|
14283
|
+
}
|
14284
|
+
return false;
|
14285
|
+
}
|
14113
14286
|
}
|
14114
14287
|
|
14115
14288
|
function eliminate_spurious_blocks(statements) {
|
@@ -14853,7 +15026,8 @@ class Compressor extends TreeWalker {
|
|
14853
15026
|
var passes = +this.options.passes || 1;
|
14854
15027
|
var min_count = 1 / 0;
|
14855
15028
|
var stopping = false;
|
14856
|
-
var
|
15029
|
+
var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
|
15030
|
+
var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
|
14857
15031
|
for (var pass = 0; pass < passes; pass++) {
|
14858
15032
|
this._toplevel.figure_out_scope(mangle);
|
14859
15033
|
if (pass === 0 && this.option("drop_console")) {
|
@@ -16055,58 +16229,294 @@ def_optimize(AST_Switch, function(self, compressor) {
|
|
16055
16229
|
}
|
16056
16230
|
}
|
16057
16231
|
}
|
16058
|
-
if (aborts(branch)) {
|
16059
|
-
var prev = body[body.length - 1];
|
16060
|
-
if (aborts(prev) && prev.body.length == branch.body.length
|
16061
|
-
&& make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
|
16062
|
-
prev.body = [];
|
16063
|
-
}
|
16064
|
-
}
|
16065
16232
|
body.push(branch);
|
16066
16233
|
}
|
16067
16234
|
while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
|
16235
|
+
self.body = body;
|
16236
|
+
|
16237
|
+
let default_or_exact = default_branch || exact_match;
|
16238
|
+
default_branch = null;
|
16239
|
+
exact_match = null;
|
16240
|
+
|
16241
|
+
// group equivalent branches so they will be located next to each other,
|
16242
|
+
// that way the next micro-optimization will merge them.
|
16243
|
+
// ** bail micro-optimization if not a simple switch case with breaks
|
16244
|
+
if (body.every((branch, i) =>
|
16245
|
+
(branch === default_or_exact || branch.expression instanceof AST_Constant)
|
16246
|
+
&& (branch.body.length === 0 || aborts(branch) || body.length - 1 === i))
|
16247
|
+
) {
|
16248
|
+
for (let i = 0; i < body.length; i++) {
|
16249
|
+
const branch = body[i];
|
16250
|
+
for (let j = i + 1; j < body.length; j++) {
|
16251
|
+
const next = body[j];
|
16252
|
+
if (next.body.length === 0) continue;
|
16253
|
+
const last_branch = j === (body.length - 1);
|
16254
|
+
const equivalentBranch = branches_equivalent(next, branch, false);
|
16255
|
+
if (equivalentBranch || (last_branch && branches_equivalent(next, branch, true))) {
|
16256
|
+
if (!equivalentBranch && last_branch) {
|
16257
|
+
next.body.push(make_node(AST_Break));
|
16258
|
+
}
|
16259
|
+
|
16260
|
+
// let's find previous siblings with inert fallthrough...
|
16261
|
+
let x = j - 1;
|
16262
|
+
let fallthroughDepth = 0;
|
16263
|
+
while (x > i) {
|
16264
|
+
if (is_inert_body(body[x--])) {
|
16265
|
+
fallthroughDepth++;
|
16266
|
+
} else {
|
16267
|
+
break;
|
16268
|
+
}
|
16269
|
+
}
|
16270
|
+
|
16271
|
+
const plucked = body.splice(j - fallthroughDepth, 1 + fallthroughDepth);
|
16272
|
+
body.splice(i + 1, 0, ...plucked);
|
16273
|
+
i += plucked.length;
|
16274
|
+
}
|
16275
|
+
}
|
16276
|
+
}
|
16277
|
+
}
|
16278
|
+
|
16279
|
+
// merge equivalent branches in a row
|
16280
|
+
for (let i = 0; i < body.length; i++) {
|
16281
|
+
let branch = body[i];
|
16282
|
+
if (branch.body.length === 0) continue;
|
16283
|
+
if (!aborts(branch)) continue;
|
16284
|
+
|
16285
|
+
for (let j = i + 1; j < body.length; i++, j++) {
|
16286
|
+
let next = body[j];
|
16287
|
+
if (next.body.length === 0) continue;
|
16288
|
+
if (
|
16289
|
+
branches_equivalent(next, branch, false)
|
16290
|
+
|| (j === body.length - 1 && branches_equivalent(next, branch, true))
|
16291
|
+
) {
|
16292
|
+
branch.body = [];
|
16293
|
+
branch = next;
|
16294
|
+
continue;
|
16295
|
+
}
|
16296
|
+
break;
|
16297
|
+
}
|
16298
|
+
}
|
16299
|
+
|
16300
|
+
// Prune any empty branches at the end of the switch statement.
|
16301
|
+
{
|
16302
|
+
let i = body.length - 1;
|
16303
|
+
for (; i >= 0; i--) {
|
16304
|
+
let bbody = body[i].body;
|
16305
|
+
if (is_break(bbody[bbody.length - 1], compressor)) bbody.pop();
|
16306
|
+
if (!is_inert_body(body[i])) break;
|
16307
|
+
}
|
16308
|
+
// i now points to the index of a branch that contains a body. By incrementing, it's
|
16309
|
+
// pointing to the first branch that's empty.
|
16310
|
+
i++;
|
16311
|
+
if (!default_or_exact || body.indexOf(default_or_exact) >= i) {
|
16312
|
+
// The default behavior is to do nothing. We can take advantage of that to
|
16313
|
+
// remove all case expressions that are side-effect free that also do
|
16314
|
+
// nothing, since they'll default to doing nothing. But we can't remove any
|
16315
|
+
// case expressions before one that would side-effect, since they may cause
|
16316
|
+
// the side-effect to be skipped.
|
16317
|
+
for (let j = body.length - 1; j >= i; j--) {
|
16318
|
+
let branch = body[j];
|
16319
|
+
if (branch === default_or_exact) {
|
16320
|
+
default_or_exact = null;
|
16321
|
+
body.pop();
|
16322
|
+
} else if (!branch.expression.has_side_effects(compressor)) {
|
16323
|
+
body.pop();
|
16324
|
+
} else {
|
16325
|
+
break;
|
16326
|
+
}
|
16327
|
+
}
|
16328
|
+
}
|
16329
|
+
}
|
16330
|
+
|
16331
|
+
|
16332
|
+
// Prune side-effect free branches that fall into default.
|
16333
|
+
DEFAULT: if (default_or_exact) {
|
16334
|
+
let default_index = body.indexOf(default_or_exact);
|
16335
|
+
let default_body_index = default_index;
|
16336
|
+
for (; default_body_index < body.length - 1; default_body_index++) {
|
16337
|
+
if (!is_inert_body(body[default_body_index])) break;
|
16338
|
+
}
|
16339
|
+
if (default_body_index < body.length - 1) {
|
16340
|
+
break DEFAULT;
|
16341
|
+
}
|
16342
|
+
|
16343
|
+
let side_effect_index = body.length - 1;
|
16344
|
+
for (; side_effect_index >= 0; side_effect_index--) {
|
16345
|
+
let branch = body[side_effect_index];
|
16346
|
+
if (branch === default_or_exact) continue;
|
16347
|
+
if (branch.expression.has_side_effects(compressor)) break;
|
16348
|
+
}
|
16349
|
+
// If the default behavior comes after any side-effect case expressions,
|
16350
|
+
// then we can fold all side-effect free cases into the default branch.
|
16351
|
+
// If the side-effect case is after the default, then any side-effect
|
16352
|
+
// free cases could prevent the side-effect from occurring.
|
16353
|
+
if (default_body_index > side_effect_index) {
|
16354
|
+
let prev_body_index = default_index - 1;
|
16355
|
+
for (; prev_body_index >= 0; prev_body_index--) {
|
16356
|
+
if (!is_inert_body(body[prev_body_index])) break;
|
16357
|
+
}
|
16358
|
+
let before = Math.max(side_effect_index, prev_body_index) + 1;
|
16359
|
+
let after = default_index;
|
16360
|
+
if (side_effect_index > default_index) {
|
16361
|
+
// If the default falls into the same body as a side-effect
|
16362
|
+
// case, then we need preserve that case and only prune the
|
16363
|
+
// cases after it.
|
16364
|
+
after = side_effect_index;
|
16365
|
+
body[side_effect_index].body = body[default_body_index].body;
|
16366
|
+
} else {
|
16367
|
+
// The default will be the last branch.
|
16368
|
+
default_or_exact.body = body[default_body_index].body;
|
16369
|
+
}
|
16370
|
+
|
16371
|
+
// Prune everything after the default (or last side-effect case)
|
16372
|
+
// until the next case with a body.
|
16373
|
+
body.splice(after + 1, default_body_index - after);
|
16374
|
+
// Prune everything before the default that falls into it.
|
16375
|
+
body.splice(before, default_index - before);
|
16376
|
+
}
|
16377
|
+
}
|
16378
|
+
|
16379
|
+
// See if we can remove the switch entirely if all cases (the default) fall into the same case body.
|
16380
|
+
DEFAULT: if (default_or_exact) {
|
16381
|
+
let i = body.findIndex(branch => !is_inert_body(branch));
|
16382
|
+
let caseBody;
|
16383
|
+
// `i` is equal to one of the following:
|
16384
|
+
// - `-1`, there is no body in the switch statement.
|
16385
|
+
// - `body.length - 1`, all cases fall into the same body.
|
16386
|
+
// - anything else, there are multiple bodies in the switch.
|
16387
|
+
if (i === body.length - 1) {
|
16388
|
+
// All cases fall into the case body.
|
16389
|
+
let branch = body[i];
|
16390
|
+
if (has_nested_break(self)) break DEFAULT;
|
16391
|
+
|
16392
|
+
// This is the last case body, and we've already pruned any breaks, so it's
|
16393
|
+
// safe to hoist.
|
16394
|
+
caseBody = make_node(AST_BlockStatement, branch, {
|
16395
|
+
body: branch.body
|
16396
|
+
});
|
16397
|
+
branch.body = [];
|
16398
|
+
} else if (i !== -1) {
|
16399
|
+
// If there are multiple bodies, then we cannot optimize anything.
|
16400
|
+
break DEFAULT;
|
16401
|
+
}
|
16402
|
+
|
16403
|
+
let sideEffect = body.find(branch => {
|
16404
|
+
return (
|
16405
|
+
branch !== default_or_exact
|
16406
|
+
&& branch.expression.has_side_effects(compressor)
|
16407
|
+
);
|
16408
|
+
});
|
16409
|
+
// If no cases cause a side-effect, we can eliminate the switch entirely.
|
16410
|
+
if (!sideEffect) {
|
16411
|
+
return make_node(AST_BlockStatement, self, {
|
16412
|
+
body: decl.concat(
|
16413
|
+
statement(self.expression),
|
16414
|
+
default_or_exact.expression ? statement(default_or_exact.expression) : [],
|
16415
|
+
caseBody || []
|
16416
|
+
)
|
16417
|
+
}).optimize(compressor);
|
16418
|
+
}
|
16419
|
+
|
16420
|
+
// If we're this far, either there was no body or all cases fell into the same body.
|
16421
|
+
// If there was no body, then we don't need a default branch (because the default is
|
16422
|
+
// do nothing). If there was a body, we'll extract it to after the switch, so the
|
16423
|
+
// switch's new default is to do nothing and we can still prune it.
|
16424
|
+
const default_index = body.indexOf(default_or_exact);
|
16425
|
+
body.splice(default_index, 1);
|
16426
|
+
default_or_exact = null;
|
16427
|
+
|
16428
|
+
if (caseBody) {
|
16429
|
+
// Recurse into switch statement one more time so that we can append the case body
|
16430
|
+
// outside of the switch. This recursion will only happen once since we've pruned
|
16431
|
+
// the default case.
|
16432
|
+
return make_node(AST_BlockStatement, self, {
|
16433
|
+
body: decl.concat(self, caseBody)
|
16434
|
+
}).optimize(compressor);
|
16435
|
+
}
|
16436
|
+
// If we fall here, there is a default branch somewhere, there are no case bodies,
|
16437
|
+
// and there's a side-effect somewhere. Just let the below paths take care of it.
|
16438
|
+
}
|
16439
|
+
|
16068
16440
|
if (body.length > 0) {
|
16069
16441
|
body[0].body = decl.concat(body[0].body);
|
16070
16442
|
}
|
16071
|
-
|
16072
|
-
while (branch = body[body.length - 1]) {
|
16073
|
-
var stat = branch.body[branch.body.length - 1];
|
16074
|
-
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
|
16075
|
-
branch.body.pop();
|
16076
|
-
if (branch.body.length || branch instanceof AST_Case
|
16077
|
-
&& (default_branch || branch.expression.has_side_effects(compressor))) break;
|
16078
|
-
if (body.pop() === default_branch) default_branch = null;
|
16079
|
-
}
|
16443
|
+
|
16080
16444
|
if (body.length == 0) {
|
16081
16445
|
return make_node(AST_BlockStatement, self, {
|
16082
|
-
body: decl.concat(
|
16083
|
-
body: self.expression
|
16084
|
-
}))
|
16446
|
+
body: decl.concat(statement(self.expression))
|
16085
16447
|
}).optimize(compressor);
|
16086
16448
|
}
|
16087
|
-
if (body.length == 1 && (
|
16088
|
-
|
16089
|
-
|
16090
|
-
|
16091
|
-
|
16092
|
-
|
16093
|
-
|
16094
|
-
|
16095
|
-
|
16096
|
-
|
16097
|
-
|
16098
|
-
|
16099
|
-
|
16100
|
-
|
16101
|
-
|
16102
|
-
|
16103
|
-
|
16104
|
-
|
16105
|
-
|
16106
|
-
|
16107
|
-
|
16449
|
+
if (body.length == 1 && !has_nested_break(self)) {
|
16450
|
+
// This is the last case body, and we've already pruned any breaks, so it's
|
16451
|
+
// safe to hoist.
|
16452
|
+
let branch = body[0];
|
16453
|
+
return make_node(AST_If, self, {
|
16454
|
+
condition: make_node(AST_Binary, self, {
|
16455
|
+
operator: "===",
|
16456
|
+
left: self.expression,
|
16457
|
+
right: branch.expression,
|
16458
|
+
}),
|
16459
|
+
body: make_node(AST_BlockStatement, branch, {
|
16460
|
+
body: branch.body
|
16461
|
+
}),
|
16462
|
+
alternative: null
|
16463
|
+
}).optimize(compressor);
|
16464
|
+
}
|
16465
|
+
if (body.length === 2 && default_or_exact && !has_nested_break(self)) {
|
16466
|
+
let branch = body[0] === default_or_exact ? body[1] : body[0];
|
16467
|
+
let exact_exp = default_or_exact.expression && statement(default_or_exact.expression);
|
16468
|
+
if (aborts(body[0])) {
|
16469
|
+
// Only the first branch body could have a break (at the last statement)
|
16470
|
+
let first = body[0];
|
16471
|
+
if (is_break(first.body[first.body.length - 1], compressor)) {
|
16472
|
+
first.body.pop();
|
16473
|
+
}
|
16474
|
+
return make_node(AST_If, self, {
|
16475
|
+
condition: make_node(AST_Binary, self, {
|
16476
|
+
operator: "===",
|
16477
|
+
left: self.expression,
|
16478
|
+
right: branch.expression,
|
16479
|
+
}),
|
16480
|
+
body: make_node(AST_BlockStatement, branch, {
|
16481
|
+
body: branch.body
|
16482
|
+
}),
|
16483
|
+
alternative: make_node(AST_BlockStatement, default_or_exact, {
|
16484
|
+
body: [].concat(
|
16485
|
+
exact_exp || [],
|
16486
|
+
default_or_exact.body
|
16487
|
+
)
|
16488
|
+
})
|
16108
16489
|
}).optimize(compressor);
|
16109
16490
|
}
|
16491
|
+
let operator = "===";
|
16492
|
+
let consequent = make_node(AST_BlockStatement, branch, {
|
16493
|
+
body: branch.body,
|
16494
|
+
});
|
16495
|
+
let always = make_node(AST_BlockStatement, default_or_exact, {
|
16496
|
+
body: [].concat(
|
16497
|
+
exact_exp || [],
|
16498
|
+
default_or_exact.body
|
16499
|
+
)
|
16500
|
+
});
|
16501
|
+
if (body[0] === default_or_exact) {
|
16502
|
+
operator = "!==";
|
16503
|
+
let tmp = always;
|
16504
|
+
always = consequent;
|
16505
|
+
consequent = tmp;
|
16506
|
+
}
|
16507
|
+
return make_node(AST_BlockStatement, self, {
|
16508
|
+
body: [
|
16509
|
+
make_node(AST_If, self, {
|
16510
|
+
condition: make_node(AST_Binary, self, {
|
16511
|
+
operator: operator,
|
16512
|
+
left: self.expression,
|
16513
|
+
right: branch.expression,
|
16514
|
+
}),
|
16515
|
+
body: consequent,
|
16516
|
+
alternative: null
|
16517
|
+
})
|
16518
|
+
].concat(always)
|
16519
|
+
}).optimize(compressor);
|
16110
16520
|
}
|
16111
16521
|
return self;
|
16112
16522
|
|
@@ -16117,6 +16527,50 @@ def_optimize(AST_Switch, function(self, compressor) {
|
|
16117
16527
|
trim_unreachable_code(compressor, branch, decl);
|
16118
16528
|
}
|
16119
16529
|
}
|
16530
|
+
function branches_equivalent(branch, prev, insertBreak) {
|
16531
|
+
let bbody = branch.body;
|
16532
|
+
let pbody = prev.body;
|
16533
|
+
if (insertBreak) {
|
16534
|
+
bbody = bbody.concat(make_node(AST_Break));
|
16535
|
+
}
|
16536
|
+
if (bbody.length !== pbody.length) return false;
|
16537
|
+
let bblock = make_node(AST_BlockStatement, branch, { body: bbody });
|
16538
|
+
let pblock = make_node(AST_BlockStatement, prev, { body: pbody });
|
16539
|
+
return bblock.equivalent_to(pblock);
|
16540
|
+
}
|
16541
|
+
function statement(expression) {
|
16542
|
+
return make_node(AST_SimpleStatement, expression, {
|
16543
|
+
body: expression
|
16544
|
+
});
|
16545
|
+
}
|
16546
|
+
function has_nested_break(root) {
|
16547
|
+
let has_break = false;
|
16548
|
+
let tw = new TreeWalker(node => {
|
16549
|
+
if (has_break) return true;
|
16550
|
+
if (node instanceof AST_Lambda) return true;
|
16551
|
+
if (node instanceof AST_SimpleStatement) return true;
|
16552
|
+
if (!is_break(node, tw)) return;
|
16553
|
+
let parent = tw.parent();
|
16554
|
+
if (
|
16555
|
+
parent instanceof AST_SwitchBranch
|
16556
|
+
&& parent.body[parent.body.length - 1] === node
|
16557
|
+
) {
|
16558
|
+
return;
|
16559
|
+
}
|
16560
|
+
has_break = true;
|
16561
|
+
});
|
16562
|
+
root.walk(tw);
|
16563
|
+
return has_break;
|
16564
|
+
}
|
16565
|
+
function is_break(node, stack) {
|
16566
|
+
return node instanceof AST_Break
|
16567
|
+
&& stack.loopcontrol_target(node) === self;
|
16568
|
+
}
|
16569
|
+
function is_inert_body(branch) {
|
16570
|
+
return !aborts(branch) && !make_node(AST_BlockStatement, branch, {
|
16571
|
+
body: branch.body
|
16572
|
+
}).has_side_effects(compressor);
|
16573
|
+
}
|
16120
16574
|
});
|
16121
16575
|
|
16122
16576
|
def_optimize(AST_Try, function(self, compressor) {
|
@@ -16237,10 +16691,6 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
16237
16691
|
}
|
16238
16692
|
}
|
16239
16693
|
|
16240
|
-
if (self.optional && is_nullish(fn, compressor)) {
|
16241
|
-
return make_node(AST_Undefined, self);
|
16242
|
-
}
|
16243
|
-
|
16244
16694
|
var is_func = fn instanceof AST_Lambda;
|
16245
16695
|
|
16246
16696
|
if (is_func && fn.pinned()) return self;
|
@@ -16283,6 +16733,14 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
16283
16733
|
}
|
16284
16734
|
|
16285
16735
|
if (compressor.option("unsafe")) {
|
16736
|
+
if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
|
16737
|
+
const [argument] = self.args;
|
16738
|
+
if (argument instanceof AST_Array) {
|
16739
|
+
return make_node(AST_Array, argument, {
|
16740
|
+
elements: argument.elements
|
16741
|
+
}).optimize(compressor);
|
16742
|
+
}
|
16743
|
+
}
|
16286
16744
|
if (is_undeclared_ref(exp)) switch (exp.name) {
|
16287
16745
|
case "Array":
|
16288
16746
|
if (self.args.length != 1) {
|
@@ -16488,6 +16946,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
16488
16946
|
argnames: [],
|
16489
16947
|
body: []
|
16490
16948
|
}).optimize(compressor);
|
16949
|
+
var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
|
16491
16950
|
if (self.args.every((x) => x instanceof AST_String)) {
|
16492
16951
|
// quite a corner-case, but we can handle it:
|
16493
16952
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
@@ -16497,14 +16956,13 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
16497
16956
|
return arg.value;
|
16498
16957
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
|
16499
16958
|
var ast = parse(code);
|
16500
|
-
var mangle = { ie8: compressor.option("ie8") };
|
16959
|
+
var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
|
16501
16960
|
ast.figure_out_scope(mangle);
|
16502
16961
|
var comp = new Compressor(compressor.options, {
|
16503
16962
|
mangle_options: compressor.mangle_options
|
16504
16963
|
});
|
16505
16964
|
ast = ast.transform(comp);
|
16506
16965
|
ast.figure_out_scope(mangle);
|
16507
|
-
base54.reset();
|
16508
16966
|
ast.compute_char_frequency(mangle);
|
16509
16967
|
ast.mangle_names(mangle);
|
16510
16968
|
var fun;
|
@@ -16629,6 +17087,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
16629
17087
|
if (can_inline && has_annotation(self, _INLINE)) {
|
16630
17088
|
set_flag(fn, SQUEEZED);
|
16631
17089
|
fn = make_node(fn.CTOR === AST_Defun ? AST_Function : fn.CTOR, fn, fn);
|
17090
|
+
fn = fn.clone(true);
|
16632
17091
|
fn.figure_out_scope({}, {
|
16633
17092
|
parent_scope: find_scope(compressor),
|
16634
17093
|
toplevel: compressor.get_toplevel()
|
@@ -16927,16 +17386,16 @@ def_optimize(AST_UnaryPostfix, function(self, compressor) {
|
|
16927
17386
|
|
16928
17387
|
def_optimize(AST_UnaryPrefix, function(self, compressor) {
|
16929
17388
|
var e = self.expression;
|
16930
|
-
if (
|
16931
|
-
|
16932
|
-
|
16933
|
-
|
16934
|
-
|
16935
|
-
|
16936
|
-
|
16937
|
-
|
16938
|
-
|
16939
|
-
return make_sequence(self, [
|
17389
|
+
if (
|
17390
|
+
self.operator == "delete" &&
|
17391
|
+
!(
|
17392
|
+
e instanceof AST_SymbolRef ||
|
17393
|
+
e instanceof AST_PropAccess ||
|
17394
|
+
e instanceof AST_Chain ||
|
17395
|
+
is_identifier_atom(e)
|
17396
|
+
)
|
17397
|
+
) {
|
17398
|
+
return make_sequence(self, [e, make_node(AST_True, self)]).optimize(compressor);
|
16940
17399
|
}
|
16941
17400
|
var seq = self.lift_sequences(compressor);
|
16942
17401
|
if (seq !== self) {
|
@@ -17768,7 +18227,15 @@ function is_reachable(self, defs) {
|
|
17768
18227
|
if (node instanceof AST_Scope && node !== self) {
|
17769
18228
|
var parent = info.parent();
|
17770
18229
|
|
17771
|
-
if (
|
18230
|
+
if (
|
18231
|
+
parent instanceof AST_Call
|
18232
|
+
&& parent.expression === node
|
18233
|
+
// Async/Generators aren't guaranteed to sync evaluate all of
|
18234
|
+
// their body steps, so it's possible they close over the variable.
|
18235
|
+
&& !(node.async || node.is_generator)
|
18236
|
+
) {
|
18237
|
+
return;
|
18238
|
+
}
|
17772
18239
|
|
17773
18240
|
if (walk(node, find_ref)) return walk_abort;
|
17774
18241
|
|
@@ -18416,14 +18883,20 @@ def_optimize(AST_Sub, function(self, compressor) {
|
|
18416
18883
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
18417
18884
|
return best_of(compressor, ev, self);
|
18418
18885
|
}
|
18419
|
-
if (self.optional && is_nullish(self.expression, compressor)) {
|
18420
|
-
return make_node(AST_Undefined, self);
|
18421
|
-
}
|
18422
18886
|
return self;
|
18423
18887
|
});
|
18424
18888
|
|
18425
18889
|
def_optimize(AST_Chain, function (self, compressor) {
|
18426
|
-
self.expression
|
18890
|
+
if (is_nullish(self.expression, compressor)) {
|
18891
|
+
let parent = compressor.parent();
|
18892
|
+
// It's valid to delete a nullish optional chain, but if we optimized
|
18893
|
+
// this to `delete undefined` then it would appear to be a syntax error
|
18894
|
+
// when we try to optimize the delete. Thankfully, `delete 0` is fine.
|
18895
|
+
if (parent instanceof AST_UnaryPrefix && parent.operator === "delete") {
|
18896
|
+
return make_node_from_constant(0, self);
|
18897
|
+
}
|
18898
|
+
return make_node(AST_Undefined, self);
|
18899
|
+
}
|
18427
18900
|
return self;
|
18428
18901
|
});
|
18429
18902
|
|
@@ -18490,9 +18963,6 @@ def_optimize(AST_Dot, function(self, compressor) {
|
|
18490
18963
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
18491
18964
|
return best_of(compressor, ev, self);
|
18492
18965
|
}
|
18493
|
-
if (self.optional && is_nullish(self.expression, compressor)) {
|
18494
|
-
return make_node(AST_Undefined, self);
|
18495
|
-
}
|
18496
18966
|
return self;
|
18497
18967
|
});
|
18498
18968
|
|
@@ -18552,9 +19022,11 @@ function inline_object_prop_spread(props, compressor) {
|
|
18552
19022
|
// non-iterable value silently does nothing; it is thus safe
|
18553
19023
|
// to remove. AST_String is the only iterable AST_Constant.
|
18554
19024
|
props.splice(i, 1);
|
19025
|
+
i--;
|
18555
19026
|
} else if (is_nullish(expr, compressor)) {
|
18556
19027
|
// Likewise, null and undefined can be silently removed.
|
18557
19028
|
props.splice(i, 1);
|
19029
|
+
i--;
|
18558
19030
|
}
|
18559
19031
|
}
|
18560
19032
|
}
|
@@ -18702,12 +19174,15 @@ function lift_key(self, compressor) {
|
|
18702
19174
|
if (self.key.value == "constructor"
|
18703
19175
|
&& compressor.parent() instanceof AST_Class) return self;
|
18704
19176
|
if (self instanceof AST_ObjectKeyVal) {
|
19177
|
+
self.quote = self.key.quote;
|
18705
19178
|
self.key = self.key.value;
|
18706
19179
|
} else if (self instanceof AST_ClassProperty) {
|
19180
|
+
self.quote = self.key.quote;
|
18707
19181
|
self.key = make_node(AST_SymbolClassProperty, self.key, {
|
18708
19182
|
name: self.key.value
|
18709
19183
|
});
|
18710
19184
|
} else {
|
19185
|
+
self.quote = self.key.quote;
|
18711
19186
|
self.key = make_node(AST_SymbolMethod, self.key, {
|
18712
19187
|
name: self.key.value
|
18713
19188
|
});
|
@@ -26815,33 +27290,61 @@ function addStrings(node, add) {
|
|
26815
27290
|
}));
|
26816
27291
|
}
|
26817
27292
|
|
27293
|
+
function mangle_private_properties(ast, options) {
|
27294
|
+
var cprivate = -1;
|
27295
|
+
var private_cache = new Map();
|
27296
|
+
var nth_identifier = options.nth_identifier || base54;
|
27297
|
+
|
27298
|
+
ast = ast.transform(new TreeTransformer(function(node) {
|
27299
|
+
if (
|
27300
|
+
node instanceof AST_ClassPrivateProperty
|
27301
|
+
|| node instanceof AST_PrivateMethod
|
27302
|
+
|| node instanceof AST_PrivateGetter
|
27303
|
+
|| node instanceof AST_PrivateSetter
|
27304
|
+
) {
|
27305
|
+
node.key.name = mangle_private(node.key.name);
|
27306
|
+
} else if (node instanceof AST_DotHash) {
|
27307
|
+
node.property = mangle_private(node.property);
|
27308
|
+
}
|
27309
|
+
}));
|
27310
|
+
return ast;
|
27311
|
+
|
27312
|
+
function mangle_private(name) {
|
27313
|
+
let mangled = private_cache.get(name);
|
27314
|
+
if (!mangled) {
|
27315
|
+
mangled = nth_identifier.get(++cprivate);
|
27316
|
+
private_cache.set(name, mangled);
|
27317
|
+
}
|
27318
|
+
|
27319
|
+
return mangled;
|
27320
|
+
}
|
27321
|
+
}
|
27322
|
+
|
26818
27323
|
function mangle_properties(ast, options) {
|
26819
27324
|
options = defaults(options, {
|
26820
27325
|
builtins: false,
|
26821
27326
|
cache: null,
|
26822
27327
|
debug: false,
|
26823
27328
|
keep_quoted: false,
|
27329
|
+
nth_identifier: base54,
|
26824
27330
|
only_cache: false,
|
26825
27331
|
regex: null,
|
26826
27332
|
reserved: null,
|
26827
27333
|
undeclared: false,
|
26828
27334
|
}, true);
|
26829
27335
|
|
27336
|
+
var nth_identifier = options.nth_identifier;
|
27337
|
+
|
26830
27338
|
var reserved_option = options.reserved;
|
26831
27339
|
if (!Array.isArray(reserved_option)) reserved_option = [reserved_option];
|
26832
27340
|
var reserved = new Set(reserved_option);
|
26833
27341
|
if (!options.builtins) find_builtins(reserved);
|
26834
27342
|
|
26835
27343
|
var cname = -1;
|
26836
|
-
var cprivate = -1;
|
26837
27344
|
|
26838
27345
|
var cache;
|
26839
|
-
var private_cache = new Map();
|
26840
27346
|
if (options.cache) {
|
26841
27347
|
cache = options.cache.props;
|
26842
|
-
cache.forEach(function(mangled_name) {
|
26843
|
-
reserved.add(mangled_name);
|
26844
|
-
});
|
26845
27348
|
} else {
|
26846
27349
|
cache = new Map();
|
26847
27350
|
}
|
@@ -26859,27 +27362,27 @@ function mangle_properties(ast, options) {
|
|
26859
27362
|
|
26860
27363
|
var names_to_mangle = new Set();
|
26861
27364
|
var unmangleable = new Set();
|
26862
|
-
|
27365
|
+
// Track each already-mangled name to prevent nth_identifier from generating
|
27366
|
+
// the same name.
|
27367
|
+
cache.forEach((mangled_name) => unmangleable.add(mangled_name));
|
26863
27368
|
|
26864
|
-
var
|
27369
|
+
var keep_quoted = !!options.keep_quoted;
|
26865
27370
|
|
26866
27371
|
// step 1: find candidates to mangle
|
26867
27372
|
ast.walk(new TreeWalker(function(node) {
|
26868
27373
|
if (
|
26869
27374
|
node instanceof AST_ClassPrivateProperty
|
26870
27375
|
|| node instanceof AST_PrivateMethod
|
26871
|
-
|
26872
|
-
|
26873
|
-
|
26874
|
-
|
26875
|
-
|
26876
|
-
if (typeof node.key == "string" &&
|
26877
|
-
(!keep_quoted_strict || !node.quote)) {
|
27376
|
+
|| node instanceof AST_PrivateGetter
|
27377
|
+
|| node instanceof AST_PrivateSetter
|
27378
|
+
|| node instanceof AST_DotHash
|
27379
|
+
) ; else if (node instanceof AST_ObjectKeyVal) {
|
27380
|
+
if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
|
26878
27381
|
add(node.key);
|
26879
27382
|
}
|
26880
27383
|
} else if (node instanceof AST_ObjectProperty) {
|
26881
27384
|
// setter or getter, since KeyVal is handled above
|
26882
|
-
if (!
|
27385
|
+
if (!keep_quoted || !node.quote) {
|
26883
27386
|
add(node.key.name);
|
26884
27387
|
}
|
26885
27388
|
} else if (node instanceof AST_Dot) {
|
@@ -26892,11 +27395,11 @@ function mangle_properties(ast, options) {
|
|
26892
27395
|
declared = !(root.thedef && root.thedef.undeclared);
|
26893
27396
|
}
|
26894
27397
|
if (declared &&
|
26895
|
-
(!
|
27398
|
+
(!keep_quoted || !node.quote)) {
|
26896
27399
|
add(node.property);
|
26897
27400
|
}
|
26898
27401
|
} else if (node instanceof AST_Sub) {
|
26899
|
-
if (!
|
27402
|
+
if (!keep_quoted) {
|
26900
27403
|
addStrings(node.property, add);
|
26901
27404
|
}
|
26902
27405
|
} else if (node instanceof AST_Call
|
@@ -26912,25 +27415,23 @@ function mangle_properties(ast, options) {
|
|
26912
27415
|
if (
|
26913
27416
|
node instanceof AST_ClassPrivateProperty
|
26914
27417
|
|| node instanceof AST_PrivateMethod
|
26915
|
-
|
26916
|
-
node
|
26917
|
-
|
26918
|
-
|
26919
|
-
|
26920
|
-
if (typeof node.key == "string" &&
|
26921
|
-
(!keep_quoted_strict || !node.quote)) {
|
27418
|
+
|| node instanceof AST_PrivateGetter
|
27419
|
+
|| node instanceof AST_PrivateSetter
|
27420
|
+
|| node instanceof AST_DotHash
|
27421
|
+
) ; else if (node instanceof AST_ObjectKeyVal) {
|
27422
|
+
if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
|
26922
27423
|
node.key = mangle(node.key);
|
26923
27424
|
}
|
26924
27425
|
} else if (node instanceof AST_ObjectProperty) {
|
26925
27426
|
// setter, getter, method or class field
|
26926
|
-
if (!
|
27427
|
+
if (!keep_quoted || !node.quote) {
|
26927
27428
|
node.key.name = mangle(node.key.name);
|
26928
27429
|
}
|
26929
27430
|
} else if (node instanceof AST_Dot) {
|
26930
|
-
if (!
|
27431
|
+
if (!keep_quoted || !node.quote) {
|
26931
27432
|
node.property = mangle(node.property);
|
26932
27433
|
}
|
26933
|
-
} else if (!
|
27434
|
+
} else if (!keep_quoted && node instanceof AST_Sub) {
|
26934
27435
|
node.property = mangleStrings(node.property);
|
26935
27436
|
} else if (node instanceof AST_Call
|
26936
27437
|
&& node.expression.print_to_string() == "Object.defineProperty") {
|
@@ -26987,7 +27488,7 @@ function mangle_properties(ast, options) {
|
|
26987
27488
|
// either debug mode is off, or it is on and we could not use the mangled name
|
26988
27489
|
if (!mangled) {
|
26989
27490
|
do {
|
26990
|
-
mangled =
|
27491
|
+
mangled = nth_identifier.get(++cname);
|
26991
27492
|
} while (!can_mangle(mangled));
|
26992
27493
|
}
|
26993
27494
|
|
@@ -26996,16 +27497,6 @@ function mangle_properties(ast, options) {
|
|
26996
27497
|
return mangled;
|
26997
27498
|
}
|
26998
27499
|
|
26999
|
-
function mangle_private(name) {
|
27000
|
-
let mangled = private_cache.get(name);
|
27001
|
-
if (!mangled) {
|
27002
|
-
mangled = base54(++cprivate);
|
27003
|
-
private_cache.set(name, mangled);
|
27004
|
-
}
|
27005
|
-
|
27006
|
-
return mangled;
|
27007
|
-
}
|
27008
|
-
|
27009
27500
|
function mangleStrings(node) {
|
27010
27501
|
return node.transform(new TreeTransformer(function(node) {
|
27011
27502
|
if (node instanceof AST_Sequence) {
|
@@ -27117,6 +27608,7 @@ async function minify(files, options) {
|
|
27117
27608
|
keep_classnames: false,
|
27118
27609
|
keep_fnames: false,
|
27119
27610
|
module: false,
|
27611
|
+
nth_identifier: base54,
|
27120
27612
|
properties: false,
|
27121
27613
|
reserved: [],
|
27122
27614
|
safari10: false,
|
@@ -27201,9 +27693,9 @@ async function minify(files, options) {
|
|
27201
27693
|
if (options.mangle) toplevel.figure_out_scope(options.mangle);
|
27202
27694
|
if (timings) timings.mangle = Date.now();
|
27203
27695
|
if (options.mangle) {
|
27204
|
-
base54.reset();
|
27205
27696
|
toplevel.compute_char_frequency(options.mangle);
|
27206
27697
|
toplevel.mangle_names(options.mangle);
|
27698
|
+
toplevel = mangle_private_properties(toplevel, options.mangle);
|
27207
27699
|
}
|
27208
27700
|
if (timings) timings.properties = Date.now();
|
27209
27701
|
if (options.mangle && options.mangle.properties) {
|