terser 5.7.2 → 5.8.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.
@@ -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
- // right #__PURE__ comments for an expression
1217
+
1218
1218
  const outer_comments_before_counts = new WeakMap();
1219
1219
 
1220
1220
  options = defaults(options, {
@@ -4819,7 +4819,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", {
4819
4819
  }
4820
4820
  }, AST_ObjectProperty);
4821
4821
 
4822
- var AST_ClassPrivateProperty = DEFNODE("ClassProperty", "", {
4822
+ var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", {
4823
4823
  $documentation: "A class property for a private property",
4824
4824
  }, AST_ClassProperty);
4825
4825
 
@@ -5521,7 +5521,7 @@ def_transform(AST_Sequence, function(self, tw) {
5521
5521
  : [new AST_Number({ value: 0 })];
5522
5522
  });
5523
5523
 
5524
- def_transform(AST_Dot, function(self, tw) {
5524
+ def_transform(AST_PropAccess, function(self, tw) {
5525
5525
  self.expression = self.expression.transform(tw);
5526
5526
  });
5527
5527
 
@@ -7093,9 +7093,11 @@ function OutputStream(options) {
7093
7093
  var do_add_mapping = mappings ? function() {
7094
7094
  mappings.forEach(function(mapping) {
7095
7095
  try {
7096
- let name = !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name;
7097
- if (name instanceof AST_Symbol) {
7098
- name = name.name;
7096
+ let { name, token } = mapping;
7097
+ if (token.type == "name" || token.type === "privatename") {
7098
+ name = token.value;
7099
+ } else if (name instanceof AST_Symbol) {
7100
+ name = token.type === "string" ? token.value : name.name;
7099
7101
  }
7100
7102
  options.source_map.add(
7101
7103
  mapping.token.file,
@@ -8568,6 +8570,7 @@ function OutputStream(options) {
8568
8570
 
8569
8571
  if (self.optional) output.print("?");
8570
8572
  output.print(".#");
8573
+ output.add_mapping(self.end);
8571
8574
  output.print_name(prop);
8572
8575
  });
8573
8576
  DEFPRINT(AST_Sub, function(self, output) {
@@ -9021,8 +9024,10 @@ function OutputStream(options) {
9021
9024
  DEFMAP([
9022
9025
  AST_ObjectGetter,
9023
9026
  AST_ObjectSetter,
9027
+ AST_PrivateGetter,
9028
+ AST_PrivateSetter,
9024
9029
  ], function(output) {
9025
- output.add_mapping(this.start, this.key.name);
9030
+ output.add_mapping(this.key.end, this.key.name);
9026
9031
  });
9027
9032
 
9028
9033
  DEFMAP([ AST_ObjectProperty ], function(output) {
@@ -9877,8 +9882,9 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
9877
9882
 
9878
9883
  function next_mangled(scope, options) {
9879
9884
  var ext = scope.enclosed;
9885
+ var nth_identifier = options.nth_identifier;
9880
9886
  out: while (true) {
9881
- var m = base54(++scope.cname);
9887
+ var m = nth_identifier.get(++scope.cname);
9882
9888
  if (ALL_RESERVED_WORDS.has(m)) continue; // skip over "do"
9883
9889
 
9884
9890
  // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
@@ -9954,6 +9960,7 @@ AST_Symbol.DEFMETHOD("global", function() {
9954
9960
  AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
9955
9961
  options = defaults(options, {
9956
9962
  eval : false,
9963
+ nth_identifier : base54,
9957
9964
  ie8 : false,
9958
9965
  keep_classnames: false,
9959
9966
  keep_fnames : false,
@@ -9975,6 +9982,7 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
9975
9982
 
9976
9983
  AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
9977
9984
  options = this._default_mangler_options(options);
9985
+ var nth_identifier = options.nth_identifier;
9978
9986
 
9979
9987
  // We only need to mangle declaration nodes. Special logic wired
9980
9988
  // into the code generator will display the mangled name if it's
@@ -10026,7 +10034,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
10026
10034
  if (node instanceof AST_Label) {
10027
10035
  let name;
10028
10036
  do {
10029
- name = base54(++lname);
10037
+ name = nth_identifier.get(++lname);
10030
10038
  } while (ALL_RESERVED_WORDS.has(name));
10031
10039
  node.mangled_name = name;
10032
10040
  return true;
@@ -10088,9 +10096,12 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
10088
10096
  });
10089
10097
 
10090
10098
  AST_Toplevel.DEFMETHOD("expand_names", function(options) {
10091
- base54.reset();
10092
- base54.sort();
10093
10099
  options = this._default_mangler_options(options);
10100
+ var nth_identifier = options.nth_identifier;
10101
+ if (nth_identifier.reset && nth_identifier.sort) {
10102
+ nth_identifier.reset();
10103
+ nth_identifier.sort();
10104
+ }
10094
10105
  var avoid = this.find_colliding_names(options);
10095
10106
  var cname = 0;
10096
10107
  this.globals.forEach(rename);
@@ -10102,7 +10113,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
10102
10113
  function next_name() {
10103
10114
  var name;
10104
10115
  do {
10105
- name = base54(cname++);
10116
+ name = nth_identifier.get(cname++);
10106
10117
  } while (avoid.has(name) || ALL_RESERVED_WORDS.has(name));
10107
10118
  return name;
10108
10119
  }
@@ -10129,30 +10140,37 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
10129
10140
 
10130
10141
  AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
10131
10142
  options = this._default_mangler_options(options);
10143
+ var nth_identifier = options.nth_identifier;
10144
+ if (!nth_identifier.reset || !nth_identifier.consider || !nth_identifier.sort) {
10145
+ // If the identifier mangler is invariant, skip computing character frequency.
10146
+ return;
10147
+ }
10148
+ nth_identifier.reset();
10149
+
10132
10150
  try {
10133
10151
  AST_Node.prototype.print = function(stream, force_parens) {
10134
10152
  this._print(stream, force_parens);
10135
10153
  if (this instanceof AST_Symbol && !this.unmangleable(options)) {
10136
- base54.consider(this.name, -1);
10154
+ nth_identifier.consider(this.name, -1);
10137
10155
  } else if (options.properties) {
10138
10156
  if (this instanceof AST_DotHash) {
10139
- base54.consider("#" + this.property, -1);
10157
+ nth_identifier.consider("#" + this.property, -1);
10140
10158
  } else if (this instanceof AST_Dot) {
10141
- base54.consider(this.property, -1);
10159
+ nth_identifier.consider(this.property, -1);
10142
10160
  } else if (this instanceof AST_Sub) {
10143
10161
  skip_string(this.property);
10144
10162
  }
10145
10163
  }
10146
10164
  };
10147
- base54.consider(this.print_to_string(), 1);
10165
+ nth_identifier.consider(this.print_to_string(), 1);
10148
10166
  } finally {
10149
10167
  AST_Node.prototype.print = AST_Node.prototype._print;
10150
10168
  }
10151
- base54.sort();
10169
+ nth_identifier.sort();
10152
10170
 
10153
10171
  function skip_string(node) {
10154
10172
  if (node instanceof AST_String) {
10155
- base54.consider(node.value, -1);
10173
+ nth_identifier.consider(node.value, -1);
10156
10174
  } else if (node instanceof AST_Conditional) {
10157
10175
  skip_string(node.consequent);
10158
10176
  skip_string(node.alternative);
@@ -10176,19 +10194,20 @@ const base54 = (() => {
10176
10194
  frequency.set(ch, 0);
10177
10195
  });
10178
10196
  }
10179
- base54.consider = function(str, delta) {
10197
+ function consider(str, delta) {
10180
10198
  for (var i = str.length; --i >= 0;) {
10181
10199
  frequency.set(str[i], frequency.get(str[i]) + delta);
10182
10200
  }
10183
- };
10201
+ }
10184
10202
  function compare(a, b) {
10185
10203
  return frequency.get(b) - frequency.get(a);
10186
10204
  }
10187
- base54.sort = function() {
10205
+ function sort() {
10188
10206
  chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
10189
- };
10190
- base54.reset = reset;
10207
+ }
10208
+ // Ensure this is in a usable initial state.
10191
10209
  reset();
10210
+ sort();
10192
10211
  function base54(num) {
10193
10212
  var ret = "", base = 54;
10194
10213
  num++;
@@ -10200,7 +10219,13 @@ const base54 = (() => {
10200
10219
  } while (num > 0);
10201
10220
  return ret;
10202
10221
  }
10203
- return base54;
10222
+
10223
+ return {
10224
+ get: base54,
10225
+ consider,
10226
+ reset,
10227
+ sort
10228
+ };
10204
10229
  })();
10205
10230
 
10206
10231
  let mangle_options = undefined;
@@ -13527,7 +13552,9 @@ function tighten_body(statements, compressor) {
13527
13552
  // Replace variable with assignment when found
13528
13553
  if (can_replace
13529
13554
  && !(node instanceof AST_SymbolDeclaration)
13530
- && lhs.equivalent_to(node)) {
13555
+ && lhs.equivalent_to(node)
13556
+ && !shadows(node.scope, lvalues)
13557
+ ) {
13531
13558
  if (stop_if_hit) {
13532
13559
  abort = true;
13533
13560
  return node;
@@ -13575,7 +13602,7 @@ function tighten_body(statements, compressor) {
13575
13602
  || node instanceof AST_PropAccess
13576
13603
  && (side_effects || node.expression.may_throw_on_access(compressor))
13577
13604
  || node instanceof AST_SymbolRef
13578
- && (lvalues.get(node.name) || side_effects && may_modify(node))
13605
+ && ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node))
13579
13606
  || node instanceof AST_VarDef && node.value
13580
13607
  && (lvalues.has(node.name.name) || side_effects && may_modify(node.name))
13581
13608
  || (sym = is_lhs(node.left, node))
@@ -13648,8 +13675,9 @@ function tighten_body(statements, compressor) {
13648
13675
  // Locate symbols which may execute code outside of scanning range
13649
13676
  var lvalues = get_lvalues(candidate);
13650
13677
  var lhs_local = is_lhs_local(lhs);
13651
- if (lhs instanceof AST_SymbolRef)
13652
- lvalues.set(lhs.name, false);
13678
+ if (lhs instanceof AST_SymbolRef) {
13679
+ lvalues.set(lhs.name, { def: lhs.definition(), modified: false });
13680
+ }
13653
13681
  var side_effects = value_has_side_effects(candidate);
13654
13682
  var replace_all = replace_all_symbols();
13655
13683
  var may_throw = candidate.may_throw(compressor);
@@ -13715,8 +13743,9 @@ function tighten_body(statements, compressor) {
13715
13743
  return false;
13716
13744
  let cur_scope = def.scope;
13717
13745
  while (cur_scope && cur_scope !== scope) {
13718
- if (cur_scope.variables.has(def.name))
13746
+ if (cur_scope.variables.has(def.name)) {
13719
13747
  return true;
13748
+ }
13720
13749
  cur_scope = cur_scope.parent_scope;
13721
13750
  }
13722
13751
  return false;
@@ -13997,8 +14026,14 @@ function tighten_body(statements, compressor) {
13997
14026
  var sym = node;
13998
14027
  while (sym instanceof AST_PropAccess)
13999
14028
  sym = sym.expression;
14000
- if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
14001
- lvalues.set(sym.name, lvalues.get(sym.name) || is_modified(compressor, tw, node, node, 0));
14029
+ if (sym instanceof AST_SymbolRef) {
14030
+ const prev = lvalues.get(sym.name);
14031
+ if (!prev || !prev.modified) {
14032
+ lvalues.set(sym.name, {
14033
+ def: sym.definition(),
14034
+ modified: is_modified(compressor, tw, node, node, 0)
14035
+ });
14036
+ }
14002
14037
  }
14003
14038
  });
14004
14039
  get_rvalue(expr).walk(tw);
@@ -14110,6 +14145,18 @@ function tighten_body(statements, compressor) {
14110
14145
  }
14111
14146
  return false;
14112
14147
  }
14148
+
14149
+ function shadows(newScope, lvalues) {
14150
+ for (const {def} of lvalues.values()) {
14151
+ let current = newScope;
14152
+ while (current && current !== def.scope) {
14153
+ let nested_def = current.variables.get(def.name);
14154
+ if (nested_def && nested_def !== def) return true;
14155
+ current = current.parent_scope;
14156
+ }
14157
+ }
14158
+ return false;
14159
+ }
14113
14160
  }
14114
14161
 
14115
14162
  function eliminate_spurious_blocks(statements) {
@@ -14853,7 +14900,8 @@ class Compressor extends TreeWalker {
14853
14900
  var passes = +this.options.passes || 1;
14854
14901
  var min_count = 1 / 0;
14855
14902
  var stopping = false;
14856
- var mangle = { ie8: this.option("ie8") };
14903
+ var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
14904
+ var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
14857
14905
  for (var pass = 0; pass < passes; pass++) {
14858
14906
  this._toplevel.figure_out_scope(mangle);
14859
14907
  if (pass === 0 && this.option("drop_console")) {
@@ -16055,58 +16103,251 @@ def_optimize(AST_Switch, function(self, compressor) {
16055
16103
  }
16056
16104
  }
16057
16105
  }
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
16106
  body.push(branch);
16066
16107
  }
16067
16108
  while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
16109
+ self.body = body;
16110
+
16111
+ for (let i = 0; i < body.length; i++) {
16112
+ let branch = body[i];
16113
+ if (branch.body.length === 0) continue;
16114
+ if (!aborts(branch)) continue;
16115
+
16116
+ for (let j = i + 1; j < body.length; i++, j++) {
16117
+ let next = body[j];
16118
+ if (next.body.length === 0) continue;
16119
+ if (
16120
+ branches_equivalent(next, branch, false)
16121
+ || (j === body.length - 1 && branches_equivalent(next, branch, true))
16122
+ ) {
16123
+ branch.body = [];
16124
+ branch = next;
16125
+ continue;
16126
+ }
16127
+ break;
16128
+ }
16129
+ }
16130
+
16131
+ let default_or_exact = default_branch || exact_match;
16132
+ default_branch = null;
16133
+ exact_match = null;
16134
+
16135
+ // Prune any empty branches at the end of the switch statement.
16136
+ {
16137
+ let i = body.length - 1;
16138
+ for (; i >= 0; i--) {
16139
+ let bbody = body[i].body;
16140
+ if (is_break(bbody[bbody.length - 1], compressor)) bbody.pop();
16141
+ if (!is_inert_body(body[i])) break;
16142
+ }
16143
+ // i now points to the index of a branch that contains a body. By incrementing, it's
16144
+ // pointing to the first branch that's empty.
16145
+ i++;
16146
+ if (!default_or_exact || body.indexOf(default_or_exact) >= i) {
16147
+ // The default behavior is to do nothing. We can take advantage of that to
16148
+ // remove all case expressions that are side-effect free that also do
16149
+ // nothing, since they'll default to doing nothing. But we can't remove any
16150
+ // case expressions before one that would side-effect, since they may cause
16151
+ // the side-effect to be skipped.
16152
+ for (let j = body.length - 1; j >= i; j--) {
16153
+ let branch = body[j];
16154
+ if (branch === default_or_exact) {
16155
+ default_or_exact = null;
16156
+ body.pop();
16157
+ } else if (!branch.expression.has_side_effects(compressor)) {
16158
+ body.pop();
16159
+ } else {
16160
+ break;
16161
+ }
16162
+ }
16163
+ }
16164
+ }
16165
+
16166
+
16167
+ // Prune side-effect free branches that fall into default.
16168
+ if (default_or_exact) {
16169
+ let default_index = body.indexOf(default_or_exact);
16170
+ let default_body_index = default_index;
16171
+ for (; default_body_index < body.length - 1; default_body_index++) {
16172
+ if (!is_inert_body(body[default_body_index])) break;
16173
+ }
16174
+ let side_effect_index = body.length - 1;
16175
+ for (; side_effect_index >= 0; side_effect_index--) {
16176
+ let branch = body[side_effect_index];
16177
+ if (branch === default_or_exact) continue;
16178
+ if (branch.expression.has_side_effects(compressor)) break;
16179
+ }
16180
+ // If the default behavior comes after any side-effect case expressions,
16181
+ // then we can fold all side-effect free cases into the default branch.
16182
+ // If the side-effect case is after the default, then any side-effect
16183
+ // free cases could prevent the side-effect from occurring.
16184
+ if (default_body_index > side_effect_index) {
16185
+ let prev_body_index = default_index - 1;
16186
+ for (; prev_body_index >= 0; prev_body_index--) {
16187
+ if (!is_inert_body(body[prev_body_index])) break;
16188
+ }
16189
+ let before = Math.max(side_effect_index, prev_body_index) + 1;
16190
+ let after = default_index;
16191
+ if (side_effect_index > default_index) {
16192
+ // If the default falls into the same body as a side-effect
16193
+ // case, then we need preserve that case and only prune the
16194
+ // cases after it.
16195
+ after = side_effect_index;
16196
+ body[side_effect_index].body = body[default_body_index].body;
16197
+ } else {
16198
+ // The default will be the last branch.
16199
+ default_or_exact.body = body[default_body_index].body;
16200
+ }
16201
+
16202
+ // Prune everything after the default (or last side-effect case)
16203
+ // until the next case with a body.
16204
+ body.splice(after + 1, default_body_index - after);
16205
+ // Prune everything before the default that falls into it.
16206
+ body.splice(before, default_index - before);
16207
+ }
16208
+ }
16209
+
16210
+ // See if we can remove the switch entirely if all cases (the default) fall into the same case body.
16211
+ DEFAULT: if (default_or_exact) {
16212
+ let i = body.findIndex(branch => !is_inert_body(branch));
16213
+ let caseBody;
16214
+ // `i` is equal to one of the following:
16215
+ // - `-1`, there is no body in the switch statement.
16216
+ // - `body.length - 1`, all cases fall into the same body.
16217
+ // - anything else, there are multiple bodies in the switch.
16218
+ if (i === body.length - 1) {
16219
+ // All cases fall into the case body.
16220
+ let branch = body[i];
16221
+ if (has_nested_break(self)) break DEFAULT;
16222
+
16223
+ // This is the last case body, and we've already pruned any breaks, so it's
16224
+ // safe to hoist.
16225
+ caseBody = make_node(AST_BlockStatement, branch, {
16226
+ body: branch.body
16227
+ });
16228
+ branch.body = [];
16229
+ } else if (i !== -1) {
16230
+ // If there are multiple bodies, then we cannot optimize anything.
16231
+ break DEFAULT;
16232
+ }
16233
+
16234
+ let sideEffect = body.find(branch => {
16235
+ return (
16236
+ branch !== default_or_exact
16237
+ && branch.expression.has_side_effects(compressor)
16238
+ );
16239
+ });
16240
+ // If no cases cause a side-effect, we can eliminate the switch entirely.
16241
+ if (!sideEffect) {
16242
+ return make_node(AST_BlockStatement, self, {
16243
+ body: decl.concat(
16244
+ statement(self.expression),
16245
+ default_or_exact.expression ? statement(default_or_exact.expression) : [],
16246
+ caseBody || []
16247
+ )
16248
+ }).optimize(compressor);
16249
+ }
16250
+
16251
+ // If we're this far, either there was no body or all cases fell into the same body.
16252
+ // If there was no body, then we don't need a default branch (because the default is
16253
+ // do nothing). If there was a body, we'll extract it to after the switch, so the
16254
+ // switch's new default is to do nothing and we can still prune it.
16255
+ const default_index = body.indexOf(default_or_exact);
16256
+ body.splice(default_index, 1);
16257
+ default_or_exact = null;
16258
+
16259
+ if (caseBody) {
16260
+ // Recurse into switch statement one more time so that we can append the case body
16261
+ // outside of the switch. This recursion will only happen once since we've pruned
16262
+ // the default case.
16263
+ return make_node(AST_BlockStatement, self, {
16264
+ body: decl.concat(self, caseBody)
16265
+ }).optimize(compressor);
16266
+ }
16267
+ // If we fall here, there is a default branch somewhere, there are no case bodies,
16268
+ // and there's a side-effect somewhere. Just let the below paths take care of it.
16269
+ }
16270
+
16068
16271
  if (body.length > 0) {
16069
16272
  body[0].body = decl.concat(body[0].body);
16070
16273
  }
16071
- self.body = body;
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
- }
16274
+
16080
16275
  if (body.length == 0) {
16081
16276
  return make_node(AST_BlockStatement, self, {
16082
- body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
16083
- body: self.expression
16084
- }))
16277
+ body: decl.concat(statement(self.expression))
16085
16278
  }).optimize(compressor);
16086
16279
  }
16087
- if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
16088
- var has_break = false;
16089
- var tw = new TreeWalker(function(node) {
16090
- if (has_break
16091
- || node instanceof AST_Lambda
16092
- || node instanceof AST_SimpleStatement) return true;
16093
- if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
16094
- has_break = true;
16095
- });
16096
- self.walk(tw);
16097
- if (!has_break) {
16098
- var statements = body[0].body.slice();
16099
- var exp = body[0].expression;
16100
- if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
16101
- body: exp
16102
- }));
16103
- statements.unshift(make_node(AST_SimpleStatement, self.expression, {
16104
- body:self.expression
16105
- }));
16106
- return make_node(AST_BlockStatement, self, {
16107
- body: statements
16280
+ if (body.length == 1 && !has_nested_break(self)) {
16281
+ // This is the last case body, and we've already pruned any breaks, so it's
16282
+ // safe to hoist.
16283
+ let branch = body[0];
16284
+ return make_node(AST_If, self, {
16285
+ condition: make_node(AST_Binary, self, {
16286
+ operator: "===",
16287
+ left: self.expression,
16288
+ right: branch.expression,
16289
+ }),
16290
+ body: make_node(AST_BlockStatement, branch, {
16291
+ body: branch.body
16292
+ }),
16293
+ alternative: null
16294
+ }).optimize(compressor);
16295
+ }
16296
+ if (body.length === 2 && default_or_exact && !has_nested_break(self)) {
16297
+ let branch = body[0] === default_or_exact ? body[1] : body[0];
16298
+ let exact_exp = default_or_exact.expression && statement(default_or_exact.expression);
16299
+ if (aborts(body[0])) {
16300
+ // Only the first branch body could have a break (at the last statement)
16301
+ let first = body[0];
16302
+ if (is_break(first.body[first.body.length - 1], compressor)) {
16303
+ first.body.pop();
16304
+ }
16305
+ return make_node(AST_If, self, {
16306
+ condition: make_node(AST_Binary, self, {
16307
+ operator: "===",
16308
+ left: self.expression,
16309
+ right: branch.expression,
16310
+ }),
16311
+ body: make_node(AST_BlockStatement, branch, {
16312
+ body: branch.body
16313
+ }),
16314
+ alternative: make_node(AST_BlockStatement, default_or_exact, {
16315
+ body: [].concat(
16316
+ exact_exp || [],
16317
+ default_or_exact.body
16318
+ )
16319
+ })
16108
16320
  }).optimize(compressor);
16109
16321
  }
16322
+ let operator = "===";
16323
+ let consequent = make_node(AST_BlockStatement, branch, {
16324
+ body: branch.body,
16325
+ });
16326
+ let always = make_node(AST_BlockStatement, default_or_exact, {
16327
+ body: [].concat(
16328
+ exact_exp || [],
16329
+ default_or_exact.body
16330
+ )
16331
+ });
16332
+ if (body[0] === default_or_exact) {
16333
+ operator = "!==";
16334
+ let tmp = always;
16335
+ always = consequent;
16336
+ consequent = tmp;
16337
+ }
16338
+ return make_node(AST_BlockStatement, self, {
16339
+ body: [
16340
+ make_node(AST_If, self, {
16341
+ condition: make_node(AST_Binary, self, {
16342
+ operator: operator,
16343
+ left: self.expression,
16344
+ right: branch.expression,
16345
+ }),
16346
+ body: consequent,
16347
+ alternative: null
16348
+ })
16349
+ ].concat(always)
16350
+ }).optimize(compressor);
16110
16351
  }
16111
16352
  return self;
16112
16353
 
@@ -16117,6 +16358,50 @@ def_optimize(AST_Switch, function(self, compressor) {
16117
16358
  trim_unreachable_code(compressor, branch, decl);
16118
16359
  }
16119
16360
  }
16361
+ function branches_equivalent(branch, prev, insertBreak) {
16362
+ let bbody = branch.body;
16363
+ let pbody = prev.body;
16364
+ if (insertBreak) {
16365
+ bbody = bbody.concat(make_node(AST_Break));
16366
+ }
16367
+ if (bbody.length !== pbody.length) return false;
16368
+ let bblock = make_node(AST_BlockStatement, branch, { body: bbody });
16369
+ let pblock = make_node(AST_BlockStatement, prev, { body: pbody });
16370
+ return bblock.equivalent_to(pblock);
16371
+ }
16372
+ function statement(expression) {
16373
+ return make_node(AST_SimpleStatement, expression, {
16374
+ body: expression
16375
+ });
16376
+ }
16377
+ function has_nested_break(root) {
16378
+ let has_break = false;
16379
+ let tw = new TreeWalker(node => {
16380
+ if (has_break) return true;
16381
+ if (node instanceof AST_Lambda) return true;
16382
+ if (node instanceof AST_SimpleStatement) return true;
16383
+ if (!is_break(node, tw)) return;
16384
+ let parent = tw.parent();
16385
+ if (
16386
+ parent instanceof AST_SwitchBranch
16387
+ && parent.body[parent.body.length - 1] === node
16388
+ ) {
16389
+ return;
16390
+ }
16391
+ has_break = true;
16392
+ });
16393
+ root.walk(tw);
16394
+ return has_break;
16395
+ }
16396
+ function is_break(node, stack) {
16397
+ return node instanceof AST_Break
16398
+ && stack.loopcontrol_target(node) === self;
16399
+ }
16400
+ function is_inert_body(branch) {
16401
+ return !aborts(branch) && !make_node(AST_BlockStatement, branch, {
16402
+ body: branch.body
16403
+ }).has_side_effects(compressor);
16404
+ }
16120
16405
  });
16121
16406
 
16122
16407
  def_optimize(AST_Try, function(self, compressor) {
@@ -16283,6 +16568,14 @@ def_optimize(AST_Call, function(self, compressor) {
16283
16568
  }
16284
16569
 
16285
16570
  if (compressor.option("unsafe")) {
16571
+ if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
16572
+ const [argument] = self.args;
16573
+ if (argument instanceof AST_Array) {
16574
+ return make_node(AST_Array, argument, {
16575
+ elements: argument.elements
16576
+ }).optimize(compressor);
16577
+ }
16578
+ }
16286
16579
  if (is_undeclared_ref(exp)) switch (exp.name) {
16287
16580
  case "Array":
16288
16581
  if (self.args.length != 1) {
@@ -16488,6 +16781,7 @@ def_optimize(AST_Call, function(self, compressor) {
16488
16781
  argnames: [],
16489
16782
  body: []
16490
16783
  }).optimize(compressor);
16784
+ var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
16491
16785
  if (self.args.every((x) => x instanceof AST_String)) {
16492
16786
  // quite a corner-case, but we can handle it:
16493
16787
  // https://github.com/mishoo/UglifyJS2/issues/203
@@ -16497,14 +16791,13 @@ def_optimize(AST_Call, function(self, compressor) {
16497
16791
  return arg.value;
16498
16792
  }).join(",") + "){" + self.args[self.args.length - 1].value + "})";
16499
16793
  var ast = parse(code);
16500
- var mangle = { ie8: compressor.option("ie8") };
16794
+ var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
16501
16795
  ast.figure_out_scope(mangle);
16502
16796
  var comp = new Compressor(compressor.options, {
16503
16797
  mangle_options: compressor.mangle_options
16504
16798
  });
16505
16799
  ast = ast.transform(comp);
16506
16800
  ast.figure_out_scope(mangle);
16507
- base54.reset();
16508
16801
  ast.compute_char_frequency(mangle);
16509
16802
  ast.mangle_names(mangle);
16510
16803
  var fun;
@@ -18702,12 +18995,15 @@ function lift_key(self, compressor) {
18702
18995
  if (self.key.value == "constructor"
18703
18996
  && compressor.parent() instanceof AST_Class) return self;
18704
18997
  if (self instanceof AST_ObjectKeyVal) {
18998
+ self.quote = self.key.quote;
18705
18999
  self.key = self.key.value;
18706
19000
  } else if (self instanceof AST_ClassProperty) {
19001
+ self.quote = self.key.quote;
18707
19002
  self.key = make_node(AST_SymbolClassProperty, self.key, {
18708
19003
  name: self.key.value
18709
19004
  });
18710
19005
  } else {
19006
+ self.quote = self.key.quote;
18711
19007
  self.key = make_node(AST_SymbolMethod, self.key, {
18712
19008
  name: self.key.value
18713
19009
  });
@@ -26815,28 +27111,59 @@ function addStrings(node, add) {
26815
27111
  }));
26816
27112
  }
26817
27113
 
27114
+ function mangle_private_properties(ast, options) {
27115
+ var cprivate = -1;
27116
+ var private_cache = new Map();
27117
+ var nth_identifier = options.nth_identifier || base54;
27118
+
27119
+ ast = ast.transform(new TreeTransformer(function(node) {
27120
+ if (
27121
+ node instanceof AST_ClassPrivateProperty
27122
+ || node instanceof AST_PrivateMethod
27123
+ || node instanceof AST_PrivateGetter
27124
+ || node instanceof AST_PrivateSetter
27125
+ ) {
27126
+ node.key.name = mangle_private(node.key.name);
27127
+ } else if (node instanceof AST_DotHash) {
27128
+ node.property = mangle_private(node.property);
27129
+ }
27130
+ }));
27131
+ return ast;
27132
+
27133
+ function mangle_private(name) {
27134
+ let mangled = private_cache.get(name);
27135
+ if (!mangled) {
27136
+ mangled = nth_identifier.get(++cprivate);
27137
+ private_cache.set(name, mangled);
27138
+ }
27139
+
27140
+ return mangled;
27141
+ }
27142
+ }
27143
+
26818
27144
  function mangle_properties(ast, options) {
26819
27145
  options = defaults(options, {
26820
27146
  builtins: false,
26821
27147
  cache: null,
26822
27148
  debug: false,
26823
27149
  keep_quoted: false,
27150
+ nth_identifier: base54,
26824
27151
  only_cache: false,
26825
27152
  regex: null,
26826
27153
  reserved: null,
26827
27154
  undeclared: false,
26828
27155
  }, true);
26829
27156
 
27157
+ var nth_identifier = options.nth_identifier;
27158
+
26830
27159
  var reserved_option = options.reserved;
26831
27160
  if (!Array.isArray(reserved_option)) reserved_option = [reserved_option];
26832
27161
  var reserved = new Set(reserved_option);
26833
27162
  if (!options.builtins) find_builtins(reserved);
26834
27163
 
26835
27164
  var cname = -1;
26836
- var cprivate = -1;
26837
27165
 
26838
27166
  var cache;
26839
- var private_cache = new Map();
26840
27167
  if (options.cache) {
26841
27168
  cache = options.cache.props;
26842
27169
  cache.forEach(function(mangled_name) {
@@ -26859,27 +27186,24 @@ function mangle_properties(ast, options) {
26859
27186
 
26860
27187
  var names_to_mangle = new Set();
26861
27188
  var unmangleable = new Set();
26862
- var private_properties = new Set();
26863
27189
 
26864
- var keep_quoted_strict = options.keep_quoted === "strict";
27190
+ var keep_quoted = !!options.keep_quoted;
26865
27191
 
26866
27192
  // step 1: find candidates to mangle
26867
27193
  ast.walk(new TreeWalker(function(node) {
26868
27194
  if (
26869
27195
  node instanceof AST_ClassPrivateProperty
26870
27196
  || node instanceof AST_PrivateMethod
26871
- ) {
26872
- private_properties.add(node.key.name);
26873
- } else if (node instanceof AST_DotHash) {
26874
- private_properties.add(node.property);
26875
- } else if (node instanceof AST_ObjectKeyVal) {
26876
- if (typeof node.key == "string" &&
26877
- (!keep_quoted_strict || !node.quote)) {
27197
+ || node instanceof AST_PrivateGetter
27198
+ || node instanceof AST_PrivateSetter
27199
+ || node instanceof AST_DotHash
27200
+ ) ; else if (node instanceof AST_ObjectKeyVal) {
27201
+ if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
26878
27202
  add(node.key);
26879
27203
  }
26880
27204
  } else if (node instanceof AST_ObjectProperty) {
26881
27205
  // setter or getter, since KeyVal is handled above
26882
- if (!keep_quoted_strict || !node.key.end.quote) {
27206
+ if (!keep_quoted || !node.quote) {
26883
27207
  add(node.key.name);
26884
27208
  }
26885
27209
  } else if (node instanceof AST_Dot) {
@@ -26892,11 +27216,11 @@ function mangle_properties(ast, options) {
26892
27216
  declared = !(root.thedef && root.thedef.undeclared);
26893
27217
  }
26894
27218
  if (declared &&
26895
- (!keep_quoted_strict || !node.quote)) {
27219
+ (!keep_quoted || !node.quote)) {
26896
27220
  add(node.property);
26897
27221
  }
26898
27222
  } else if (node instanceof AST_Sub) {
26899
- if (!keep_quoted_strict) {
27223
+ if (!keep_quoted) {
26900
27224
  addStrings(node.property, add);
26901
27225
  }
26902
27226
  } else if (node instanceof AST_Call
@@ -26912,25 +27236,23 @@ function mangle_properties(ast, options) {
26912
27236
  if (
26913
27237
  node instanceof AST_ClassPrivateProperty
26914
27238
  || node instanceof AST_PrivateMethod
26915
- ) {
26916
- node.key.name = mangle_private(node.key.name);
26917
- } else if (node instanceof AST_DotHash) {
26918
- node.property = mangle_private(node.property);
26919
- } else if (node instanceof AST_ObjectKeyVal) {
26920
- if (typeof node.key == "string" &&
26921
- (!keep_quoted_strict || !node.quote)) {
27239
+ || node instanceof AST_PrivateGetter
27240
+ || node instanceof AST_PrivateSetter
27241
+ || node instanceof AST_DotHash
27242
+ ) ; else if (node instanceof AST_ObjectKeyVal) {
27243
+ if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
26922
27244
  node.key = mangle(node.key);
26923
27245
  }
26924
27246
  } else if (node instanceof AST_ObjectProperty) {
26925
27247
  // setter, getter, method or class field
26926
- if (!keep_quoted_strict || !node.key.end.quote) {
27248
+ if (!keep_quoted || !node.quote) {
26927
27249
  node.key.name = mangle(node.key.name);
26928
27250
  }
26929
27251
  } else if (node instanceof AST_Dot) {
26930
- if (!keep_quoted_strict || !node.quote) {
27252
+ if (!keep_quoted || !node.quote) {
26931
27253
  node.property = mangle(node.property);
26932
27254
  }
26933
- } else if (!options.keep_quoted && node instanceof AST_Sub) {
27255
+ } else if (!keep_quoted && node instanceof AST_Sub) {
26934
27256
  node.property = mangleStrings(node.property);
26935
27257
  } else if (node instanceof AST_Call
26936
27258
  && node.expression.print_to_string() == "Object.defineProperty") {
@@ -26987,7 +27309,7 @@ function mangle_properties(ast, options) {
26987
27309
  // either debug mode is off, or it is on and we could not use the mangled name
26988
27310
  if (!mangled) {
26989
27311
  do {
26990
- mangled = base54(++cname);
27312
+ mangled = nth_identifier.get(++cname);
26991
27313
  } while (!can_mangle(mangled));
26992
27314
  }
26993
27315
 
@@ -26996,16 +27318,6 @@ function mangle_properties(ast, options) {
26996
27318
  return mangled;
26997
27319
  }
26998
27320
 
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
27321
  function mangleStrings(node) {
27010
27322
  return node.transform(new TreeTransformer(function(node) {
27011
27323
  if (node instanceof AST_Sequence) {
@@ -27201,9 +27513,9 @@ async function minify(files, options) {
27201
27513
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
27202
27514
  if (timings) timings.mangle = Date.now();
27203
27515
  if (options.mangle) {
27204
- base54.reset();
27205
27516
  toplevel.compute_char_frequency(options.mangle);
27206
27517
  toplevel.mangle_names(options.mangle);
27518
+ toplevel = mangle_private_properties(toplevel, options.mangle);
27207
27519
  }
27208
27520
  if (timings) timings.properties = Date.now();
27209
27521
  if (options.mangle && options.mangle.properties) {