terser 5.28.1 → 5.29.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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.29.0
4
+ - Re-releases previously reverted 5.28.0
5
+ - Fix crash while optimizing some bitwise ops
6
+ - (internal) Remove needless wrapper for from_moz (#1499)
7
+
3
8
  ## v5.28.1
4
9
  (hotfix release)
5
10
  - Reverts v5.28.0
@@ -7583,9 +7583,7 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
7583
7583
  start: my_start_token(M),
7584
7584
  end: my_end_token(M),
7585
7585
  exported_definition: from_moz(M.declaration),
7586
- exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(function (specifier) {
7587
- return from_moz(specifier);
7588
- }) : null,
7586
+ exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(from_moz) : null,
7589
7587
  module_name: from_moz(M.source),
7590
7588
  assert_clause: assert_clause_from_moz(M.assertions)
7591
7589
  });
@@ -12778,8 +12776,8 @@ AST_Class.prototype._size = function () {
12778
12776
  };
12779
12777
 
12780
12778
  AST_ClassStaticBlock.prototype._size = function () {
12781
- // "class{}" + semicolons
12782
- return 7 + list_overhead(this.body);
12779
+ // "static{}" + semicolons
12780
+ return 8 + list_overhead(this.body);
12783
12781
  };
12784
12782
 
12785
12783
  AST_ClassProperty.prototype._size = function () {
@@ -13008,6 +13006,8 @@ function make_node_from_constant(val, orig) {
13008
13006
  operator: "-",
13009
13007
  expression: make_node(AST_Infinity, orig)
13010
13008
  }) : make_node(AST_Infinity, orig);
13009
+ case "bigint":
13010
+ return make_node(AST_BigInt, orig, { value: val.toString() });
13011
13011
  case "boolean":
13012
13012
  return make_node(val ? AST_True : AST_False, orig);
13013
13013
  case "undefined":
@@ -13486,6 +13486,7 @@ const is_pure_native_value = make_nested_lookup({
13486
13486
  const is_undeclared_ref = (node) =>
13487
13487
  node instanceof AST_SymbolRef && node.definition().undeclared;
13488
13488
 
13489
+ const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
13489
13490
  const lazy_op = makePredicate("&& || ??");
13490
13491
  const unary_side_effects = makePredicate("delete ++ --");
13491
13492
 
@@ -13546,6 +13547,24 @@ const unary_side_effects = makePredicate("delete ++ --");
13546
13547
  node.DEFMETHOD("is_number", func);
13547
13548
  });
13548
13549
 
13550
+ // methods to determine if an expression is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size
13551
+ (function(def_is_32_bit_integer) {
13552
+ def_is_32_bit_integer(AST_Node, return_false);
13553
+ def_is_32_bit_integer(AST_Number, function() {
13554
+ return this.value === (this.value | 0);
13555
+ });
13556
+ def_is_32_bit_integer(AST_UnaryPrefix, function() {
13557
+ return this.operator == "~" ? this.expression.is_number()
13558
+ : this.operator === "+" ? this.expression.is_32_bit_integer()
13559
+ : false;
13560
+ });
13561
+ def_is_32_bit_integer(AST_Binary, function() {
13562
+ return bitwise_binop.has(this.operator);
13563
+ });
13564
+ }(function (node, func) {
13565
+ node.DEFMETHOD("is_32_bit_integer", func);
13566
+ }));
13567
+
13549
13568
  // methods to determine if an expression has a string result type
13550
13569
  (function(def_is_string) {
13551
13570
  def_is_string(AST_Node, return_false);
@@ -14160,6 +14179,37 @@ function is_lhs(node, parent) {
14160
14179
  });
14161
14180
  });
14162
14181
 
14182
+ (function (def_bitwise_negate) {
14183
+ function basic_negation(exp) {
14184
+ return make_node(AST_UnaryPrefix, exp, {
14185
+ operator: "~",
14186
+ expression: exp
14187
+ });
14188
+ }
14189
+
14190
+ def_bitwise_negate(AST_Node, function() {
14191
+ return basic_negation(this);
14192
+ });
14193
+
14194
+ def_bitwise_negate(AST_Number, function() {
14195
+ const neg = ~this.value;
14196
+ if (neg.toString().length > this.value.toString().length) {
14197
+ return basic_negation(this);
14198
+ }
14199
+ return make_node(AST_Number, this, { value: neg });
14200
+ });
14201
+
14202
+ def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
14203
+ if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
14204
+ return this.expression;
14205
+ } else {
14206
+ return basic_negation(this);
14207
+ }
14208
+ });
14209
+ })(function (node, func) {
14210
+ node.DEFMETHOD("bitwise_negate", func);
14211
+ });
14212
+
14163
14213
  // Is the callee of this function pure?
14164
14214
  var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
14165
14215
  AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
@@ -14385,7 +14435,14 @@ def_eval(AST_Constant, function () {
14385
14435
  return this.getValue();
14386
14436
  });
14387
14437
 
14388
- def_eval(AST_BigInt, return_this);
14438
+ const supports_bigint = typeof BigInt === "function";
14439
+ def_eval(AST_BigInt, function () {
14440
+ if (supports_bigint) {
14441
+ return BigInt(this.value);
14442
+ } else {
14443
+ return this;
14444
+ }
14445
+ });
14389
14446
 
14390
14447
  def_eval(AST_RegExp, function (compressor) {
14391
14448
  let evaluated = compressor.evaluated_regexps.get(this.value);
@@ -14509,7 +14566,6 @@ def_eval(AST_Binary, function (compressor, depth) {
14509
14566
  var right = this.right._eval(compressor, depth);
14510
14567
  if (right === this.right)
14511
14568
  return this;
14512
- var result;
14513
14569
 
14514
14570
  if (left != null
14515
14571
  && right != null
@@ -14521,6 +14577,17 @@ def_eval(AST_Binary, function (compressor, depth) {
14521
14577
  return this;
14522
14578
  }
14523
14579
 
14580
+ // Do not mix BigInt and Number; Don't use `>>>` on BigInt or `/ 0n`
14581
+ if (
14582
+ (typeof left === "bigint") !== (typeof right === "bigint")
14583
+ || typeof left === "bigint"
14584
+ && (this.operator === ">>>"
14585
+ || this.operator === "/" && Number(right) === 0)
14586
+ ) {
14587
+ return this;
14588
+ }
14589
+
14590
+ var result;
14524
14591
  switch (this.operator) {
14525
14592
  case "&&": result = left && right; break;
14526
14593
  case "||": result = left || right; break;
@@ -14530,7 +14597,7 @@ def_eval(AST_Binary, function (compressor, depth) {
14530
14597
  case "^": result = left ^ right; break;
14531
14598
  case "+": result = left + right; break;
14532
14599
  case "*": result = left * right; break;
14533
- case "**": result = Math.pow(left, right); break;
14600
+ case "**": result = left ** right; break;
14534
14601
  case "/": result = left / right; break;
14535
14602
  case "%": result = left % right; break;
14536
14603
  case "-": result = left - right; break;
@@ -14548,7 +14615,7 @@ def_eval(AST_Binary, function (compressor, depth) {
14548
14615
  default:
14549
14616
  return this;
14550
14617
  }
14551
- if (isNaN(result) && compressor.find_parent(AST_With)) {
14618
+ if (typeof result === "number" && isNaN(result) && compressor.find_parent(AST_With)) {
14552
14619
  // leave original expression as is
14553
14620
  return this;
14554
14621
  }
@@ -18546,6 +18613,33 @@ class Compressor extends TreeWalker {
18546
18613
  }
18547
18614
  }
18548
18615
 
18616
+ in_32_bit_context() {
18617
+ if (!this.option("evaluate")) return false;
18618
+ var self = this.self();
18619
+ for (var i = 0, p; p = this.parent(i); i++) {
18620
+ if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
18621
+ return true;
18622
+ }
18623
+ if (p instanceof AST_UnaryPrefix) {
18624
+ return p.operator === "~";
18625
+ }
18626
+ if (
18627
+ p instanceof AST_Binary
18628
+ && (
18629
+ p.operator == "&&"
18630
+ || p.operator == "||"
18631
+ || p.operator == "??"
18632
+ )
18633
+ || p instanceof AST_Conditional && p.condition !== self
18634
+ || p.tail_node() === self
18635
+ ) {
18636
+ self = p;
18637
+ } else {
18638
+ return false;
18639
+ }
18640
+ }
18641
+ }
18642
+
18549
18643
  get_toplevel() {
18550
18644
  return this._toplevel;
18551
18645
  }
@@ -20244,9 +20338,40 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
20244
20338
  right: e.right
20245
20339
  });
20246
20340
  }
20247
- // avoids infinite recursion of numerals
20248
- if (self.operator != "-"
20249
- || !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt)) {
20341
+
20342
+ if (compressor.option("evaluate")) {
20343
+ // ~~x => x (in 32-bit context)
20344
+ // ~~{32 bit integer} => {32 bit integer}
20345
+ if (
20346
+ self.operator === "~"
20347
+ && self.expression instanceof AST_UnaryPrefix
20348
+ && self.expression.operator === "~"
20349
+ && (compressor.in_32_bit_context() || self.expression.expression.is_32_bit_integer())
20350
+ ) {
20351
+ return self.expression.expression;
20352
+ }
20353
+
20354
+ // ~(x ^ y) => x ^ ~y
20355
+ if (
20356
+ self.operator === "~"
20357
+ && e instanceof AST_Binary
20358
+ && e.operator === "^"
20359
+ ) {
20360
+ if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
20361
+ // ~(~x ^ y) => x ^ y
20362
+ e.left = e.left.bitwise_negate(true);
20363
+ } else {
20364
+ e.right = e.right.bitwise_negate(true);
20365
+ }
20366
+ return e;
20367
+ }
20368
+ }
20369
+
20370
+ if (
20371
+ self.operator != "-"
20372
+ // avoid infinite recursion of numerals
20373
+ || !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt)
20374
+ ) {
20250
20375
  var ev = self.evaluate(compressor);
20251
20376
  if (ev !== self) {
20252
20377
  ev = make_node_from_constant(ev, self).optimize(compressor);
@@ -20337,6 +20462,7 @@ def_optimize(AST_Binary, function(self, compressor) {
20337
20462
  self.left.equivalent_to(self.right)) {
20338
20463
  self.operator = self.operator.substr(0, 2);
20339
20464
  }
20465
+
20340
20466
  // XXX: intentionally falling down to the next case
20341
20467
  case "==":
20342
20468
  case "!=":
@@ -20378,6 +20504,55 @@ def_optimize(AST_Binary, function(self, compressor) {
20378
20504
  && self.left.definition() === self.right.definition()
20379
20505
  && is_object(self.left.fixed_value())) {
20380
20506
  return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
20507
+ } else if (self.left.is_32_bit_integer() && self.right.is_32_bit_integer()) {
20508
+ const not = node => make_node(AST_UnaryPrefix, node, {
20509
+ operator: "!",
20510
+ expression: node
20511
+ });
20512
+ const booleanify = (node, truthy) => {
20513
+ if (truthy) {
20514
+ return compressor.in_boolean_context()
20515
+ ? node
20516
+ : not(not(node));
20517
+ } else {
20518
+ return not(node);
20519
+ }
20520
+ };
20521
+
20522
+ // The only falsy 32-bit integer is 0
20523
+ if (self.left instanceof AST_Number && self.left.value === 0) {
20524
+ return booleanify(self.right, self.operator[0] === "!");
20525
+ }
20526
+ if (self.right instanceof AST_Number && self.right.value === 0) {
20527
+ return booleanify(self.left, self.operator[0] === "!");
20528
+ }
20529
+
20530
+ // Mask all-bits check
20531
+ // (x & 0xFF) != 0xFF => !(~x & 0xFF)
20532
+ let and_op, x, mask;
20533
+ if (
20534
+ (and_op =
20535
+ self.left instanceof AST_Binary ? self.left
20536
+ : self.right instanceof AST_Binary ? self.right : null)
20537
+ && (mask = and_op === self.left ? self.right : self.left)
20538
+ && and_op.operator === "&"
20539
+ && mask instanceof AST_Number
20540
+ && mask.is_32_bit_integer()
20541
+ && (x =
20542
+ and_op.left.equivalent_to(mask) ? and_op.right
20543
+ : and_op.right.equivalent_to(mask) ? and_op.left : null)
20544
+ ) {
20545
+ let optimized = booleanify(make_node(AST_Binary, self, {
20546
+ operator: "&",
20547
+ left: mask,
20548
+ right: make_node(AST_UnaryPrefix, self, {
20549
+ operator: "~",
20550
+ expression: x
20551
+ })
20552
+ }), self.operator[0] === "=");
20553
+
20554
+ return best_of(compressor, optimized, self);
20555
+ }
20381
20556
  }
20382
20557
  break;
20383
20558
  case "&&":
@@ -20751,6 +20926,159 @@ def_optimize(AST_Binary, function(self, compressor) {
20751
20926
  }
20752
20927
  }
20753
20928
  }
20929
+
20930
+ // bitwise ops
20931
+ if (bitwise_binop.has(self.operator)) {
20932
+ // Use De Morgan's laws
20933
+ // z & (X | y)
20934
+ // => z & X (given y & z === 0)
20935
+ // => z & X | {y & z} (given y & z !== 0)
20936
+ let y, z, x_node, y_node, z_node = self.left;
20937
+ if (
20938
+ self.operator === "&"
20939
+ && self.right instanceof AST_Binary
20940
+ && self.right.operator === "|"
20941
+ && typeof (z = self.left.evaluate(compressor)) === "number"
20942
+ ) {
20943
+ if (typeof (y = self.right.right.evaluate(compressor)) === "number") {
20944
+ // z & (X | y)
20945
+ x_node = self.right.left;
20946
+ y_node = self.right.right;
20947
+ } else if (typeof (y = self.right.left.evaluate(compressor)) === "number") {
20948
+ // z & (y | X)
20949
+ x_node = self.right.right;
20950
+ y_node = self.right.left;
20951
+ }
20952
+
20953
+ if (x_node && y_node) {
20954
+ if ((y & z) === 0) {
20955
+ self = make_node(AST_Binary, self, {
20956
+ operator: self.operator,
20957
+ left: z_node,
20958
+ right: x_node
20959
+ });
20960
+ } else {
20961
+ const reordered_ops = make_node(AST_Binary, self, {
20962
+ operator: "|",
20963
+ left: make_node(AST_Binary, self, {
20964
+ operator: "&",
20965
+ left: x_node,
20966
+ right: z_node
20967
+ }),
20968
+ right: make_node_from_constant(y & z, y_node),
20969
+ });
20970
+
20971
+ self = best_of(compressor, self, reordered_ops);
20972
+ }
20973
+ }
20974
+ }
20975
+
20976
+ // x ^ x => 0
20977
+ // x | x => 0 | x
20978
+ // x & x => 0 | x
20979
+ const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
20980
+ if (same_operands) {
20981
+ if (self.operator === "^") {
20982
+ return make_node(AST_Number, self, { value: 0 });
20983
+ }
20984
+ if (self.operator === "|" || self.operator === "&") {
20985
+ self.left = make_node(AST_Number, self, { value: 0 });
20986
+ self.operator = "|";
20987
+ }
20988
+ }
20989
+
20990
+
20991
+ // Shifts that do nothing
20992
+ // {anything} >> 0 => {anything} | 0
20993
+ // {anything} << 0 => {anything} | 0
20994
+ if (
20995
+ (self.operator === "<<" || self.operator === ">>")
20996
+ && self.right instanceof AST_Number && self.right.value === 0
20997
+ ) {
20998
+ self.operator = "|";
20999
+ }
21000
+
21001
+ // Find useless to-bitwise conversions
21002
+ // {32 bit integer} | 0 => {32 bit integer}
21003
+ // {32 bit integer} ^ 0 => {32 bit integer}
21004
+ const zero_side = self.right instanceof AST_Number && self.right.value === 0 ? self.right
21005
+ : self.left instanceof AST_Number && self.left.value === 0 ? self.left
21006
+ : null;
21007
+ const non_zero_side = zero_side && (zero_side === self.right ? self.left : self.right);
21008
+ if (
21009
+ zero_side
21010
+ && (self.operator === "|" || self.operator === "^")
21011
+ && (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
21012
+ ) {
21013
+ return non_zero_side;
21014
+ }
21015
+
21016
+ // {anything} & 0 => 0
21017
+ if (
21018
+ zero_side
21019
+ && self.operator === "&"
21020
+ && !non_zero_side.has_side_effects(compressor)
21021
+ ) {
21022
+ return zero_side;
21023
+ }
21024
+
21025
+ const is_full_mask = (node) =>
21026
+ node instanceof AST_Number && node.value === -1
21027
+ ||
21028
+ node instanceof AST_UnaryPrefix && (
21029
+ node.operator === "-"
21030
+ && node.expression instanceof AST_Number
21031
+ && node.expression.value === 1
21032
+ || node.operator === "~"
21033
+ && node.expression instanceof AST_Number
21034
+ && node.expression.value === 0);
21035
+
21036
+ const full_mask = is_full_mask(self.right) ? self.right
21037
+ : is_full_mask(self.left) ? self.left
21038
+ : null;
21039
+ const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
21040
+
21041
+ switch (self.operator) {
21042
+ case "|":
21043
+ // {anything} | -1 => -1
21044
+ if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
21045
+ return full_mask;
21046
+ }
21047
+
21048
+ break;
21049
+ case "&":
21050
+ // {32 bit integer} & -1 => {32 bit integer}
21051
+ if (
21052
+ full_mask
21053
+ && (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
21054
+ ) {
21055
+ return non_full_mask_side;
21056
+ }
21057
+
21058
+ break;
21059
+ case "^":
21060
+ // {anything} ^ -1 => ~{anything}
21061
+ if (full_mask) {
21062
+ return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
21063
+ }
21064
+
21065
+ // ~x ^ ~y => x ^ y
21066
+ if (
21067
+ self.left instanceof AST_UnaryPrefix
21068
+ && self.left.operator === "~"
21069
+ && self.right instanceof AST_UnaryPrefix
21070
+ && self.right.operator === "~"
21071
+ ) {
21072
+ self = make_node(AST_Binary, self, {
21073
+ operator: "^",
21074
+ left: self.left.expression,
21075
+ right: self.right.expression
21076
+ });
21077
+ }
21078
+
21079
+ break;
21080
+ }
21081
+ }
20754
21082
  }
20755
21083
  // x && (y && z) ==> x && y && z
20756
21084
  // x || (y || z) ==> x || y || z
@@ -44,6 +44,7 @@
44
44
  import {
45
45
  AST_Array,
46
46
  AST_Arrow,
47
+ AST_BigInt,
47
48
  AST_BlockStatement,
48
49
  AST_Call,
49
50
  AST_Chain,
@@ -124,6 +125,8 @@ export function make_node_from_constant(val, orig) {
124
125
  operator: "-",
125
126
  expression: make_node(AST_Infinity, orig)
126
127
  }) : make_node(AST_Infinity, orig);
128
+ case "bigint":
129
+ return make_node(AST_BigInt, orig, { value: val.toString() });
127
130
  case "boolean":
128
131
  return make_node(val ? AST_True : AST_False, orig);
129
132
  case "undefined":
@@ -133,7 +133,14 @@ def_eval(AST_Constant, function () {
133
133
  return this.getValue();
134
134
  });
135
135
 
136
- def_eval(AST_BigInt, return_this);
136
+ const supports_bigint = typeof BigInt === "function";
137
+ def_eval(AST_BigInt, function () {
138
+ if (supports_bigint) {
139
+ return BigInt(this.value);
140
+ } else {
141
+ return this;
142
+ }
143
+ });
137
144
 
138
145
  def_eval(AST_RegExp, function (compressor) {
139
146
  let evaluated = compressor.evaluated_regexps.get(this.value);
@@ -257,7 +264,6 @@ def_eval(AST_Binary, function (compressor, depth) {
257
264
  var right = this.right._eval(compressor, depth);
258
265
  if (right === this.right)
259
266
  return this;
260
- var result;
261
267
 
262
268
  if (left != null
263
269
  && right != null
@@ -269,6 +275,17 @@ def_eval(AST_Binary, function (compressor, depth) {
269
275
  return this;
270
276
  }
271
277
 
278
+ // Do not mix BigInt and Number; Don't use `>>>` on BigInt or `/ 0n`
279
+ if (
280
+ (typeof left === "bigint") !== (typeof right === "bigint")
281
+ || typeof left === "bigint"
282
+ && (this.operator === ">>>"
283
+ || this.operator === "/" && Number(right) === 0)
284
+ ) {
285
+ return this;
286
+ }
287
+
288
+ var result;
272
289
  switch (this.operator) {
273
290
  case "&&": result = left && right; break;
274
291
  case "||": result = left || right; break;
@@ -278,7 +295,7 @@ def_eval(AST_Binary, function (compressor, depth) {
278
295
  case "^": result = left ^ right; break;
279
296
  case "+": result = left + right; break;
280
297
  case "*": result = left * right; break;
281
- case "**": result = Math.pow(left, right); break;
298
+ case "**": result = left ** right; break;
282
299
  case "/": result = left / right; break;
283
300
  case "%": result = left % right; break;
284
301
  case "-": result = left - right; break;
@@ -296,7 +313,7 @@ def_eval(AST_Binary, function (compressor, depth) {
296
313
  default:
297
314
  return this;
298
315
  }
299
- if (isNaN(result) && compressor.find_parent(AST_With)) {
316
+ if (typeof result === "number" && isNaN(result) && compressor.find_parent(AST_With)) {
300
317
  // leave original expression as is
301
318
  return this;
302
319
  }
@@ -170,6 +170,7 @@ import "./drop-unused.js";
170
170
  import "./reduce-vars.js";
171
171
  import {
172
172
  is_undeclared_ref,
173
+ bitwise_binop,
173
174
  lazy_op,
174
175
  is_nullish,
175
176
  is_undefined,
@@ -373,6 +374,33 @@ class Compressor extends TreeWalker {
373
374
  }
374
375
  }
375
376
 
377
+ in_32_bit_context() {
378
+ if (!this.option("evaluate")) return false;
379
+ var self = this.self();
380
+ for (var i = 0, p; p = this.parent(i); i++) {
381
+ if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
382
+ return true;
383
+ }
384
+ if (p instanceof AST_UnaryPrefix) {
385
+ return p.operator === "~";
386
+ }
387
+ if (
388
+ p instanceof AST_Binary
389
+ && (
390
+ p.operator == "&&"
391
+ || p.operator == "||"
392
+ || p.operator == "??"
393
+ )
394
+ || p instanceof AST_Conditional && p.condition !== self
395
+ || p.tail_node() === self
396
+ ) {
397
+ self = p;
398
+ } else {
399
+ return false;
400
+ }
401
+ }
402
+ }
403
+
376
404
  get_toplevel() {
377
405
  return this._toplevel;
378
406
  }
@@ -2071,9 +2099,40 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
2071
2099
  right: e.right
2072
2100
  });
2073
2101
  }
2074
- // avoids infinite recursion of numerals
2075
- if (self.operator != "-"
2076
- || !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt)) {
2102
+
2103
+ if (compressor.option("evaluate")) {
2104
+ // ~~x => x (in 32-bit context)
2105
+ // ~~{32 bit integer} => {32 bit integer}
2106
+ if (
2107
+ self.operator === "~"
2108
+ && self.expression instanceof AST_UnaryPrefix
2109
+ && self.expression.operator === "~"
2110
+ && (compressor.in_32_bit_context() || self.expression.expression.is_32_bit_integer())
2111
+ ) {
2112
+ return self.expression.expression;
2113
+ }
2114
+
2115
+ // ~(x ^ y) => x ^ ~y
2116
+ if (
2117
+ self.operator === "~"
2118
+ && e instanceof AST_Binary
2119
+ && e.operator === "^"
2120
+ ) {
2121
+ if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
2122
+ // ~(~x ^ y) => x ^ y
2123
+ e.left = e.left.bitwise_negate(true);
2124
+ } else {
2125
+ e.right = e.right.bitwise_negate(true);
2126
+ }
2127
+ return e;
2128
+ }
2129
+ }
2130
+
2131
+ if (
2132
+ self.operator != "-"
2133
+ // avoid infinite recursion of numerals
2134
+ || !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt)
2135
+ ) {
2077
2136
  var ev = self.evaluate(compressor);
2078
2137
  if (ev !== self) {
2079
2138
  ev = make_node_from_constant(ev, self).optimize(compressor);
@@ -2164,6 +2223,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2164
2223
  self.left.equivalent_to(self.right)) {
2165
2224
  self.operator = self.operator.substr(0, 2);
2166
2225
  }
2226
+
2167
2227
  // XXX: intentionally falling down to the next case
2168
2228
  case "==":
2169
2229
  case "!=":
@@ -2205,6 +2265,55 @@ def_optimize(AST_Binary, function(self, compressor) {
2205
2265
  && self.left.definition() === self.right.definition()
2206
2266
  && is_object(self.left.fixed_value())) {
2207
2267
  return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
2268
+ } else if (self.left.is_32_bit_integer() && self.right.is_32_bit_integer()) {
2269
+ const not = node => make_node(AST_UnaryPrefix, node, {
2270
+ operator: "!",
2271
+ expression: node
2272
+ });
2273
+ const booleanify = (node, truthy) => {
2274
+ if (truthy) {
2275
+ return compressor.in_boolean_context()
2276
+ ? node
2277
+ : not(not(node));
2278
+ } else {
2279
+ return not(node);
2280
+ }
2281
+ };
2282
+
2283
+ // The only falsy 32-bit integer is 0
2284
+ if (self.left instanceof AST_Number && self.left.value === 0) {
2285
+ return booleanify(self.right, self.operator[0] === "!");
2286
+ }
2287
+ if (self.right instanceof AST_Number && self.right.value === 0) {
2288
+ return booleanify(self.left, self.operator[0] === "!");
2289
+ }
2290
+
2291
+ // Mask all-bits check
2292
+ // (x & 0xFF) != 0xFF => !(~x & 0xFF)
2293
+ let and_op, x, mask;
2294
+ if (
2295
+ (and_op =
2296
+ self.left instanceof AST_Binary ? self.left
2297
+ : self.right instanceof AST_Binary ? self.right : null)
2298
+ && (mask = and_op === self.left ? self.right : self.left)
2299
+ && and_op.operator === "&"
2300
+ && mask instanceof AST_Number
2301
+ && mask.is_32_bit_integer()
2302
+ && (x =
2303
+ and_op.left.equivalent_to(mask) ? and_op.right
2304
+ : and_op.right.equivalent_to(mask) ? and_op.left : null)
2305
+ ) {
2306
+ let optimized = booleanify(make_node(AST_Binary, self, {
2307
+ operator: "&",
2308
+ left: mask,
2309
+ right: make_node(AST_UnaryPrefix, self, {
2310
+ operator: "~",
2311
+ expression: x
2312
+ })
2313
+ }), self.operator[0] === "=");
2314
+
2315
+ return best_of(compressor, optimized, self);
2316
+ }
2208
2317
  }
2209
2318
  break;
2210
2319
  case "&&":
@@ -2578,6 +2687,159 @@ def_optimize(AST_Binary, function(self, compressor) {
2578
2687
  }
2579
2688
  }
2580
2689
  }
2690
+
2691
+ // bitwise ops
2692
+ if (bitwise_binop.has(self.operator)) {
2693
+ // Use De Morgan's laws
2694
+ // z & (X | y)
2695
+ // => z & X (given y & z === 0)
2696
+ // => z & X | {y & z} (given y & z !== 0)
2697
+ let y, z, x_node, y_node, z_node = self.left;
2698
+ if (
2699
+ self.operator === "&"
2700
+ && self.right instanceof AST_Binary
2701
+ && self.right.operator === "|"
2702
+ && typeof (z = self.left.evaluate(compressor)) === "number"
2703
+ ) {
2704
+ if (typeof (y = self.right.right.evaluate(compressor)) === "number") {
2705
+ // z & (X | y)
2706
+ x_node = self.right.left;
2707
+ y_node = self.right.right;
2708
+ } else if (typeof (y = self.right.left.evaluate(compressor)) === "number") {
2709
+ // z & (y | X)
2710
+ x_node = self.right.right;
2711
+ y_node = self.right.left;
2712
+ }
2713
+
2714
+ if (x_node && y_node) {
2715
+ if ((y & z) === 0) {
2716
+ self = make_node(AST_Binary, self, {
2717
+ operator: self.operator,
2718
+ left: z_node,
2719
+ right: x_node
2720
+ });
2721
+ } else {
2722
+ const reordered_ops = make_node(AST_Binary, self, {
2723
+ operator: "|",
2724
+ left: make_node(AST_Binary, self, {
2725
+ operator: "&",
2726
+ left: x_node,
2727
+ right: z_node
2728
+ }),
2729
+ right: make_node_from_constant(y & z, y_node),
2730
+ });
2731
+
2732
+ self = best_of(compressor, self, reordered_ops);
2733
+ }
2734
+ }
2735
+ }
2736
+
2737
+ // x ^ x => 0
2738
+ // x | x => 0 | x
2739
+ // x & x => 0 | x
2740
+ const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
2741
+ if (same_operands) {
2742
+ if (self.operator === "^") {
2743
+ return make_node(AST_Number, self, { value: 0 });
2744
+ }
2745
+ if (self.operator === "|" || self.operator === "&") {
2746
+ self.left = make_node(AST_Number, self, { value: 0 });
2747
+ self.operator = "|";
2748
+ }
2749
+ }
2750
+
2751
+
2752
+ // Shifts that do nothing
2753
+ // {anything} >> 0 => {anything} | 0
2754
+ // {anything} << 0 => {anything} | 0
2755
+ if (
2756
+ (self.operator === "<<" || self.operator === ">>")
2757
+ && self.right instanceof AST_Number && self.right.value === 0
2758
+ ) {
2759
+ self.operator = "|";
2760
+ }
2761
+
2762
+ // Find useless to-bitwise conversions
2763
+ // {32 bit integer} | 0 => {32 bit integer}
2764
+ // {32 bit integer} ^ 0 => {32 bit integer}
2765
+ const zero_side = self.right instanceof AST_Number && self.right.value === 0 ? self.right
2766
+ : self.left instanceof AST_Number && self.left.value === 0 ? self.left
2767
+ : null;
2768
+ const non_zero_side = zero_side && (zero_side === self.right ? self.left : self.right);
2769
+ if (
2770
+ zero_side
2771
+ && (self.operator === "|" || self.operator === "^")
2772
+ && (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
2773
+ ) {
2774
+ return non_zero_side;
2775
+ }
2776
+
2777
+ // {anything} & 0 => 0
2778
+ if (
2779
+ zero_side
2780
+ && self.operator === "&"
2781
+ && !non_zero_side.has_side_effects(compressor)
2782
+ ) {
2783
+ return zero_side;
2784
+ }
2785
+
2786
+ const is_full_mask = (node) =>
2787
+ node instanceof AST_Number && node.value === -1
2788
+ ||
2789
+ node instanceof AST_UnaryPrefix && (
2790
+ node.operator === "-"
2791
+ && node.expression instanceof AST_Number
2792
+ && node.expression.value === 1
2793
+ || node.operator === "~"
2794
+ && node.expression instanceof AST_Number
2795
+ && node.expression.value === 0);
2796
+
2797
+ const full_mask = is_full_mask(self.right) ? self.right
2798
+ : is_full_mask(self.left) ? self.left
2799
+ : null;
2800
+ const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
2801
+
2802
+ switch (self.operator) {
2803
+ case "|":
2804
+ // {anything} | -1 => -1
2805
+ if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
2806
+ return full_mask;
2807
+ }
2808
+
2809
+ break;
2810
+ case "&":
2811
+ // {32 bit integer} & -1 => {32 bit integer}
2812
+ if (
2813
+ full_mask
2814
+ && (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
2815
+ ) {
2816
+ return non_full_mask_side;
2817
+ }
2818
+
2819
+ break;
2820
+ case "^":
2821
+ // {anything} ^ -1 => ~{anything}
2822
+ if (full_mask) {
2823
+ return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
2824
+ }
2825
+
2826
+ // ~x ^ ~y => x ^ y
2827
+ if (
2828
+ self.left instanceof AST_UnaryPrefix
2829
+ && self.left.operator === "~"
2830
+ && self.right instanceof AST_UnaryPrefix
2831
+ && self.right.operator === "~"
2832
+ ) {
2833
+ self = make_node(AST_Binary, self, {
2834
+ operator: "^",
2835
+ left: self.left.expression,
2836
+ right: self.right.expression
2837
+ });
2838
+ }
2839
+
2840
+ break;
2841
+ }
2842
+ }
2581
2843
  }
2582
2844
  // x && (y && z) ==> x && y && z
2583
2845
  // x || (y || z) ==> x || y || z
@@ -133,6 +133,7 @@ import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } fr
133
133
  export const is_undeclared_ref = (node) =>
134
134
  node instanceof AST_SymbolRef && node.definition().undeclared;
135
135
 
136
+ export const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
136
137
  export const lazy_op = makePredicate("&& || ??");
137
138
  export const unary_side_effects = makePredicate("delete ++ --");
138
139
 
@@ -193,6 +194,24 @@ export const unary_side_effects = makePredicate("delete ++ --");
193
194
  node.DEFMETHOD("is_number", func);
194
195
  });
195
196
 
197
+ // methods to determine if an expression is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size
198
+ (function(def_is_32_bit_integer) {
199
+ def_is_32_bit_integer(AST_Node, return_false);
200
+ def_is_32_bit_integer(AST_Number, function() {
201
+ return this.value === (this.value | 0);
202
+ });
203
+ def_is_32_bit_integer(AST_UnaryPrefix, function() {
204
+ return this.operator == "~" ? this.expression.is_number()
205
+ : this.operator === "+" ? this.expression.is_32_bit_integer()
206
+ : false;
207
+ });
208
+ def_is_32_bit_integer(AST_Binary, function() {
209
+ return bitwise_binop.has(this.operator);
210
+ });
211
+ }(function (node, func) {
212
+ node.DEFMETHOD("is_32_bit_integer", func);
213
+ }));
214
+
196
215
  // methods to determine if an expression has a string result type
197
216
  (function(def_is_string) {
198
217
  def_is_string(AST_Node, return_false);
@@ -807,6 +826,37 @@ export function is_lhs(node, parent) {
807
826
  });
808
827
  });
809
828
 
829
+ (function (def_bitwise_negate) {
830
+ function basic_negation(exp) {
831
+ return make_node(AST_UnaryPrefix, exp, {
832
+ operator: "~",
833
+ expression: exp
834
+ });
835
+ }
836
+
837
+ def_bitwise_negate(AST_Node, function() {
838
+ return basic_negation(this);
839
+ });
840
+
841
+ def_bitwise_negate(AST_Number, function() {
842
+ const neg = ~this.value;
843
+ if (neg.toString().length > this.value.toString().length) {
844
+ return basic_negation(this);
845
+ }
846
+ return make_node(AST_Number, this, { value: neg });
847
+ });
848
+
849
+ def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
850
+ if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
851
+ return this.expression;
852
+ } else {
853
+ return basic_negation(this);
854
+ }
855
+ });
856
+ })(function (node, func) {
857
+ node.DEFMETHOD("bitwise_negate", func);
858
+ });
859
+
810
860
  // Is the callee of this function pure?
811
861
  var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
812
862
  AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
@@ -618,9 +618,7 @@ import { is_basic_identifier_string } from "./parse.js";
618
618
  start: my_start_token(M),
619
619
  end: my_end_token(M),
620
620
  exported_definition: from_moz(M.declaration),
621
- exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(function (specifier) {
622
- return from_moz(specifier);
623
- }) : null,
621
+ exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(from_moz) : null,
624
622
  module_name: from_moz(M.source),
625
623
  assert_clause: assert_clause_from_moz(M.assertions)
626
624
  });
package/lib/size.js CHANGED
@@ -406,8 +406,8 @@ AST_Class.prototype._size = function () {
406
406
  };
407
407
 
408
408
  AST_ClassStaticBlock.prototype._size = function () {
409
- // "class{}" + semicolons
410
- return 7 + list_overhead(this.body);
409
+ // "static{}" + semicolons
410
+ return 8 + list_overhead(this.body);
411
411
  };
412
412
 
413
413
  AST_ClassProperty.prototype._size = function () {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "homepage": "https://terser.org",
5
5
  "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
6
6
  "license": "BSD-2-Clause",
7
- "version": "5.28.1",
7
+ "version": "5.29.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },