terser 5.38.2 → 5.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.39.1
4
+
5
+ - Fix bitwise operations that could mix `BigInt` and `number`
6
+
7
+ ## v5.39.0
8
+
9
+ - Remove unnecessary `console.assert` calls (#1590)
10
+
3
11
  ## v5.38.2
4
12
 
5
13
  - internal: Flatten inheritance tree for object/class members
package/README.md CHANGED
@@ -871,7 +871,8 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
871
871
  allow improved compression. This might be unsafe when an at least one of two
872
872
  operands is an object with computed values due the use of methods like `get`,
873
873
  or `valueOf`. This could cause change in execution order after operands in the
874
- comparison are switching. Compression only works if both `comparisons` and
874
+ comparison are switching. Or if one of two operands is `NaN`, the result is always
875
+ `false`. Compression only works if both `comparisons` and
875
876
  `unsafe_comps` are both set to true.
876
877
 
877
878
  - `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
@@ -13045,7 +13045,6 @@ const TRUTHY = 0b00000010;
13045
13045
  const FALSY = 0b00000100;
13046
13046
  const UNDEFINED = 0b00001000;
13047
13047
  const INLINED = 0b00010000;
13048
-
13049
13048
  // Nodes to which values are ever written. Used when keep_assign is part of the unused option string.
13050
13049
  const WRITE_ONLY = 0b00100000;
13051
13050
 
@@ -13677,18 +13676,24 @@ const unary_side_effects = makePredicate("delete ++ --");
13677
13676
  def_is_number(AST_Node, return_false);
13678
13677
  def_is_number(AST_Number, return_true);
13679
13678
  const unary = makePredicate("+ - ~ ++ --");
13680
- def_is_number(AST_Unary, function() {
13681
- return unary.has(this.operator) && !(this.expression instanceof AST_BigInt);
13679
+ def_is_number(AST_Unary, function(compressor) {
13680
+ return unary.has(this.operator) && this.expression.is_number(compressor);
13682
13681
  });
13683
13682
  const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
13684
13683
  def_is_number(AST_Binary, function(compressor) {
13685
- return numeric_ops.has(this.operator) || this.operator == "+"
13686
- && this.left.is_number(compressor)
13687
- && this.right.is_number(compressor);
13684
+ if (this.operator === "+") {
13685
+ // Both sides need to be `number`. Or one is a `number` and the other is number-ish.
13686
+ return this.left.is_number(compressor) && this.right.is_number_or_bigint(compressor)
13687
+ || this.right.is_number(compressor) && this.left.is_number_or_bigint(compressor);
13688
+ } else if (numeric_ops.has(this.operator)) {
13689
+ return this.left.is_number(compressor) || this.right.is_number(compressor);
13690
+ } else {
13691
+ return false;
13692
+ }
13688
13693
  });
13689
13694
  def_is_number(AST_Assign, function(compressor) {
13690
- return numeric_ops.has(this.operator.slice(0, -1))
13691
- || this.operator == "=" && this.right.is_number(compressor);
13695
+ return (this.operator === "=" || numeric_ops.has(this.operator.slice(0, -1)))
13696
+ && this.right.is_number(compressor);
13692
13697
  });
13693
13698
  def_is_number(AST_Sequence, function(compressor) {
13694
13699
  return this.tail_node().is_number(compressor);
@@ -13700,19 +13705,83 @@ const unary_side_effects = makePredicate("delete ++ --");
13700
13705
  node.DEFMETHOD("is_number", func);
13701
13706
  });
13702
13707
 
13708
+ // methods to determine if an expression returns a BigInt
13709
+ (function(def_is_bigint) {
13710
+ def_is_bigint(AST_Node, return_false);
13711
+ def_is_bigint(AST_BigInt, return_true);
13712
+ const unary = makePredicate("+ - ~ ++ --");
13713
+ def_is_bigint(AST_Unary, function(compressor) {
13714
+ return unary.has(this.operator) && this.expression.is_bigint(compressor);
13715
+ });
13716
+ const numeric_ops = makePredicate("- * / % & | ^ << >>");
13717
+ def_is_bigint(AST_Binary, function(compressor) {
13718
+ if (this.operator === "+") {
13719
+ return this.left.is_bigint(compressor) && this.right.is_number_or_bigint(compressor)
13720
+ || this.right.is_bigint(compressor) && this.left.is_number_or_bigint(compressor);
13721
+ } else if (numeric_ops.has(this.operator)) {
13722
+ return this.left.is_bigint(compressor) || this.right.is_bigint(compressor);
13723
+ } else {
13724
+ return false;
13725
+ }
13726
+ });
13727
+ def_is_bigint(AST_Assign, function(compressor) {
13728
+ return (numeric_ops.has(this.operator.slice(0, -1)) || this.operator == "=")
13729
+ && this.right.is_bigint(compressor);
13730
+ });
13731
+ def_is_bigint(AST_Sequence, function(compressor) {
13732
+ return this.tail_node().is_bigint(compressor);
13733
+ });
13734
+ def_is_bigint(AST_Conditional, function(compressor) {
13735
+ return this.consequent.is_bigint(compressor) && this.alternative.is_bigint(compressor);
13736
+ });
13737
+ })(function(node, func) {
13738
+ node.DEFMETHOD("is_bigint", func);
13739
+ });
13740
+
13741
+ // methods to determine if an expression is a number or a bigint
13742
+ (function(def_is_number_or_bigint) {
13743
+ def_is_number_or_bigint(AST_Node, return_false);
13744
+ def_is_number_or_bigint(AST_Number, return_true);
13745
+ def_is_number_or_bigint(AST_BigInt, return_true);
13746
+ const numeric_unary_ops = makePredicate("+ - ~ ++ --");
13747
+ def_is_number_or_bigint(AST_Unary, function(_compressor) {
13748
+ return numeric_unary_ops.has(this.operator);
13749
+ });
13750
+ const numeric_ops = makePredicate("- * / % & | ^ << >>");
13751
+ def_is_number_or_bigint(AST_Binary, function(compressor) {
13752
+ return this.operator === "+"
13753
+ ? this.left.is_number_or_bigint(compressor) && this.right.is_number_or_bigint(compressor)
13754
+ : numeric_ops.has(this.operator);
13755
+ });
13756
+ def_is_number_or_bigint(AST_Assign, function(compressor) {
13757
+ return numeric_ops.has(this.operator.slice(0, -1))
13758
+ || this.operator == "=" && this.right.is_number_or_bigint(compressor);
13759
+ });
13760
+ def_is_number_or_bigint(AST_Sequence, function(compressor) {
13761
+ return this.tail_node().is_number_or_bigint(compressor);
13762
+ });
13763
+ def_is_number_or_bigint(AST_Conditional, function(compressor) {
13764
+ return this.consequent.is_number_or_bigint(compressor) && this.alternative.is_number_or_bigint(compressor);
13765
+ });
13766
+ }(function (node, func) {
13767
+ node.DEFMETHOD("is_number_or_bigint", func);
13768
+ }));
13769
+
13770
+
13703
13771
  // 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
13704
13772
  (function(def_is_32_bit_integer) {
13705
13773
  def_is_32_bit_integer(AST_Node, return_false);
13706
- def_is_32_bit_integer(AST_Number, function() {
13774
+ def_is_32_bit_integer(AST_Number, function(_compressor) {
13707
13775
  return this.value === (this.value | 0);
13708
13776
  });
13709
- def_is_32_bit_integer(AST_UnaryPrefix, function() {
13710
- return this.operator == "~" ? this.expression.is_number()
13711
- : this.operator === "+" ? this.expression.is_32_bit_integer()
13777
+ def_is_32_bit_integer(AST_UnaryPrefix, function(compressor) {
13778
+ return this.operator == "~" ? this.expression.is_number(compressor)
13779
+ : this.operator === "+" ? this.expression.is_32_bit_integer(compressor)
13712
13780
  : false;
13713
13781
  });
13714
- def_is_32_bit_integer(AST_Binary, function() {
13715
- return bitwise_binop.has(this.operator);
13782
+ def_is_32_bit_integer(AST_Binary, function(compressor) {
13783
+ return bitwise_binop.has(this.operator)
13784
+ && (this.left.is_number(compressor) || this.right.is_number(compressor));
13716
13785
  });
13717
13786
  }(function (node, func) {
13718
13787
  node.DEFMETHOD("is_32_bit_integer", func);
@@ -14349,30 +14418,36 @@ function is_lhs(node, parent) {
14349
14418
  });
14350
14419
 
14351
14420
  (function (def_bitwise_negate) {
14352
- function basic_negation(exp) {
14421
+ function basic_bitwise_negation(exp) {
14353
14422
  return make_node(AST_UnaryPrefix, exp, {
14354
14423
  operator: "~",
14355
14424
  expression: exp
14356
14425
  });
14357
14426
  }
14358
14427
 
14359
- def_bitwise_negate(AST_Node, function() {
14360
- return basic_negation(this);
14428
+ def_bitwise_negate(AST_Node, function(_compressor) {
14429
+ return basic_bitwise_negation(this);
14361
14430
  });
14362
14431
 
14363
- def_bitwise_negate(AST_Number, function() {
14432
+ def_bitwise_negate(AST_Number, function(_compressor) {
14364
14433
  const neg = ~this.value;
14365
14434
  if (neg.toString().length > this.value.toString().length) {
14366
- return basic_negation(this);
14435
+ return basic_bitwise_negation(this);
14367
14436
  }
14368
14437
  return make_node(AST_Number, this, { value: neg });
14369
14438
  });
14370
14439
 
14371
- def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
14372
- if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
14440
+ def_bitwise_negate(AST_UnaryPrefix, function(compressor, in_32_bit_context) {
14441
+ if (
14442
+ this.operator == "~"
14443
+ && (
14444
+ this.expression.is_32_bit_integer(compressor) ||
14445
+ (in_32_bit_context != null ? in_32_bit_context : compressor.in_32_bit_context())
14446
+ )
14447
+ ) {
14373
14448
  return this.expression;
14374
14449
  } else {
14375
- return basic_negation(this);
14450
+ return basic_bitwise_negation(this);
14376
14451
  }
14377
14452
  });
14378
14453
  })(function (node, func) {
@@ -18909,12 +18984,16 @@ class Compressor extends TreeWalker {
18909
18984
  }
18910
18985
  }
18911
18986
 
18912
- in_32_bit_context() {
18987
+ in_32_bit_context(other_operand_must_be_number) {
18913
18988
  if (!this.option("evaluate")) return false;
18914
18989
  var self = this.self();
18915
18990
  for (var i = 0, p; p = this.parent(i); i++) {
18916
18991
  if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
18917
- return true;
18992
+ if (other_operand_must_be_number) {
18993
+ return (self === p.left ? p.right : p.left).is_number(this);
18994
+ } else {
18995
+ return true;
18996
+ }
18918
18997
  }
18919
18998
  if (p instanceof AST_UnaryPrefix) {
18920
18999
  return p.operator === "~";
@@ -19029,6 +19108,7 @@ class Compressor extends TreeWalker {
19029
19108
  }
19030
19109
  }
19031
19110
 
19111
+
19032
19112
  function def_optimize(node, optimizer) {
19033
19113
  node.DEFMETHOD("optimize", function(compressor) {
19034
19114
  var self = this;
@@ -20253,6 +20333,23 @@ def_optimize(AST_Call, function(self, compressor) {
20253
20333
  self.args.length = last;
20254
20334
  }
20255
20335
 
20336
+ if (
20337
+ exp instanceof AST_Dot
20338
+ && exp.expression instanceof AST_SymbolRef
20339
+ && exp.expression.name === "console"
20340
+ && exp.expression.definition().undeclared
20341
+ && exp.property === "assert"
20342
+ ) {
20343
+ const condition = self.args[0];
20344
+ if (condition) {
20345
+ const value = condition.evaluate(compressor);
20346
+
20347
+ if (value === 1 || value === true) {
20348
+ return make_node(AST_Undefined, self);
20349
+ }
20350
+ }
20351
+ }
20352
+
20256
20353
  if (compressor.option("unsafe") && !exp.contains_optional()) {
20257
20354
  if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
20258
20355
  const [argument] = self.args;
@@ -20667,7 +20764,7 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
20667
20764
  self.operator === "~"
20668
20765
  && self.expression instanceof AST_UnaryPrefix
20669
20766
  && self.expression.operator === "~"
20670
- && (compressor.in_32_bit_context() || self.expression.expression.is_32_bit_integer())
20767
+ && (compressor.in_32_bit_context(false) || self.expression.expression.is_32_bit_integer(compressor))
20671
20768
  ) {
20672
20769
  return self.expression.expression;
20673
20770
  }
@@ -20680,9 +20777,9 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
20680
20777
  ) {
20681
20778
  if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
20682
20779
  // ~(~x ^ y) => x ^ y
20683
- e.left = e.left.bitwise_negate(true);
20780
+ e.left = e.left.bitwise_negate(compressor, true);
20684
20781
  } else {
20685
- e.right = e.right.bitwise_negate(true);
20782
+ e.right = e.right.bitwise_negate(compressor, true);
20686
20783
  }
20687
20784
  return e;
20688
20785
  }
@@ -20777,10 +20874,13 @@ def_optimize(AST_Binary, function(self, compressor) {
20777
20874
  case "===":
20778
20875
  case "!==":
20779
20876
  var is_strict_comparison = true;
20780
- if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
20877
+ if (
20878
+ (self.left.is_string(compressor) && self.right.is_string(compressor)) ||
20781
20879
  (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
20880
+ (self.left.is_bigint(compressor) && self.right.is_bigint(compressor)) ||
20782
20881
  (self.left.is_boolean() && self.right.is_boolean()) ||
20783
- self.left.equivalent_to(self.right)) {
20882
+ self.left.equivalent_to(self.right)
20883
+ ) {
20784
20884
  self.operator = self.operator.substr(0, 2);
20785
20885
  }
20786
20886
 
@@ -20825,7 +20925,7 @@ def_optimize(AST_Binary, function(self, compressor) {
20825
20925
  && self.left.definition() === self.right.definition()
20826
20926
  && is_object(self.left.fixed_value())) {
20827
20927
  return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
20828
- } else if (self.left.is_32_bit_integer() && self.right.is_32_bit_integer()) {
20928
+ } else if (self.left.is_32_bit_integer(compressor) && self.right.is_32_bit_integer(compressor)) {
20829
20929
  const not = node => make_node(AST_UnaryPrefix, node, {
20830
20930
  operator: "!",
20831
20931
  expression: node
@@ -20858,7 +20958,7 @@ def_optimize(AST_Binary, function(self, compressor) {
20858
20958
  && (mask = and_op === self.left ? self.right : self.left)
20859
20959
  && and_op.operator === "&"
20860
20960
  && mask instanceof AST_Number
20861
- && mask.is_32_bit_integer()
20961
+ && mask.is_32_bit_integer(compressor)
20862
20962
  && (x =
20863
20963
  and_op.left.equivalent_to(mask) ? and_op.right
20864
20964
  : and_op.right.equivalent_to(mask) ? and_op.left : null)
@@ -21101,7 +21201,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21101
21201
  // a + -b => a - b
21102
21202
  if (self.right instanceof AST_UnaryPrefix
21103
21203
  && self.right.operator == "-"
21104
- && self.left.is_number(compressor)) {
21204
+ && self.left.is_number_or_bigint(compressor)) {
21105
21205
  self = make_node(AST_Binary, self, {
21106
21206
  operator: "-",
21107
21207
  left: self.left,
@@ -21113,7 +21213,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21113
21213
  if (self.left instanceof AST_UnaryPrefix
21114
21214
  && self.left.operator == "-"
21115
21215
  && reversible()
21116
- && self.right.is_number(compressor)) {
21216
+ && self.right.is_number_or_bigint(compressor)) {
21117
21217
  self = make_node(AST_Binary, self, {
21118
21218
  operator: "-",
21119
21219
  left: self.right,
@@ -21157,8 +21257,9 @@ def_optimize(AST_Binary, function(self, compressor) {
21157
21257
  case "|":
21158
21258
  case "^":
21159
21259
  // a + +b => +b + a
21160
- if (self.left.is_number(compressor)
21161
- && self.right.is_number(compressor)
21260
+ if (
21261
+ self.left.is_number_or_bigint(compressor)
21262
+ && self.right.is_number_or_bigint(compressor)
21162
21263
  && reversible()
21163
21264
  && !(self.left instanceof AST_Binary
21164
21265
  && self.left.operator != self.operator
@@ -21175,7 +21276,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21175
21276
  self = best_of(compressor, self, reversed);
21176
21277
  }
21177
21278
  }
21178
- if (associative && self.is_number(compressor)) {
21279
+ if (associative && self.is_number_or_bigint(compressor)) {
21179
21280
  // a + (b + c) => (a + b) + c
21180
21281
  if (self.right instanceof AST_Binary
21181
21282
  && self.right.operator == self.operator) {
@@ -21294,18 +21395,31 @@ def_optimize(AST_Binary, function(self, compressor) {
21294
21395
  }
21295
21396
  }
21296
21397
 
21297
- // x ^ x => 0
21298
21398
  // x | x => 0 | x
21299
21399
  // x & x => 0 | x
21300
- const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
21301
- if (same_operands) {
21302
- if (self.operator === "^") {
21303
- return make_node(AST_Number, self, { value: 0 });
21304
- }
21305
- if (self.operator === "|" || self.operator === "&") {
21306
- self.left = make_node(AST_Number, self, { value: 0 });
21307
- self.operator = "|";
21308
- }
21400
+ if (
21401
+ (self.operator === "|" || self.operator === "&")
21402
+ && self.left.equivalent_to(self.right)
21403
+ && !self.left.has_side_effects(compressor)
21404
+ && compressor.in_32_bit_context(true)
21405
+ ) {
21406
+ self.left = make_node(AST_Number, self, { value: 0 });
21407
+ self.operator = "|";
21408
+ }
21409
+
21410
+ // ~x ^ ~y => x ^ y
21411
+ if (
21412
+ self.operator === "^"
21413
+ && self.left instanceof AST_UnaryPrefix
21414
+ && self.left.operator === "~"
21415
+ && self.right instanceof AST_UnaryPrefix
21416
+ && self.right.operator === "~"
21417
+ ) {
21418
+ self = make_node(AST_Binary, self, {
21419
+ operator: "^",
21420
+ left: self.left.expression,
21421
+ right: self.right.expression
21422
+ });
21309
21423
  }
21310
21424
 
21311
21425
 
@@ -21329,7 +21443,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21329
21443
  if (
21330
21444
  zero_side
21331
21445
  && (self.operator === "|" || self.operator === "^")
21332
- && (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
21446
+ && (non_zero_side.is_32_bit_integer(compressor) || compressor.in_32_bit_context(true))
21333
21447
  ) {
21334
21448
  return non_zero_side;
21335
21449
  }
@@ -21339,65 +21453,48 @@ def_optimize(AST_Binary, function(self, compressor) {
21339
21453
  zero_side
21340
21454
  && self.operator === "&"
21341
21455
  && !non_zero_side.has_side_effects(compressor)
21456
+ && non_zero_side.is_32_bit_integer(compressor)
21342
21457
  ) {
21343
21458
  return zero_side;
21344
21459
  }
21345
21460
 
21461
+ // ~0 is all ones, as well as -1.
21462
+ // We can ellide some operations with it.
21346
21463
  const is_full_mask = (node) =>
21347
21464
  node instanceof AST_Number && node.value === -1
21348
21465
  ||
21349
- node instanceof AST_UnaryPrefix && (
21350
- node.operator === "-"
21351
- && node.expression instanceof AST_Number
21352
- && node.expression.value === 1
21353
- || node.operator === "~"
21354
- && node.expression instanceof AST_Number
21355
- && node.expression.value === 0);
21466
+ node instanceof AST_UnaryPrefix
21467
+ && node.operator === "-"
21468
+ && node.expression instanceof AST_Number
21469
+ && node.expression.value === 1;
21356
21470
 
21357
21471
  const full_mask = is_full_mask(self.right) ? self.right
21358
21472
  : is_full_mask(self.left) ? self.left
21359
21473
  : null;
21360
- const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
21361
-
21362
- switch (self.operator) {
21363
- case "|":
21364
- // {anything} | -1 => -1
21365
- if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
21366
- return full_mask;
21367
- }
21368
-
21369
- break;
21370
- case "&":
21371
- // {32 bit integer} & -1 => {32 bit integer}
21372
- if (
21373
- full_mask
21374
- && (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
21375
- ) {
21376
- return non_full_mask_side;
21377
- }
21474
+ const other_side = (full_mask === self.right ? self.left : self.right);
21378
21475
 
21379
- break;
21380
- case "^":
21381
- // {anything} ^ -1 => ~{anything}
21382
- if (full_mask) {
21383
- return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
21384
- }
21385
-
21386
- // ~x ^ ~y => x ^ y
21387
- if (
21388
- self.left instanceof AST_UnaryPrefix
21389
- && self.left.operator === "~"
21390
- && self.right instanceof AST_UnaryPrefix
21391
- && self.right.operator === "~"
21392
- ) {
21393
- self = make_node(AST_Binary, self, {
21394
- operator: "^",
21395
- left: self.left.expression,
21396
- right: self.right.expression
21397
- });
21398
- }
21476
+ // {32 bit integer} & -1 => {32 bit integer}
21477
+ if (
21478
+ full_mask
21479
+ && self.operator === "&"
21480
+ && (
21481
+ other_side.is_32_bit_integer(compressor)
21482
+ || compressor.in_32_bit_context(true)
21483
+ )
21484
+ ) {
21485
+ return other_side;
21486
+ }
21399
21487
 
21400
- break;
21488
+ // {anything} ^ -1 => ~{anything}
21489
+ if (
21490
+ full_mask
21491
+ && self.operator === "^"
21492
+ && (
21493
+ other_side.is_32_bit_integer(compressor)
21494
+ || compressor.in_32_bit_context(true)
21495
+ )
21496
+ ) {
21497
+ return other_side.bitwise_negate(compressor);
21401
21498
  }
21402
21499
  }
21403
21500
  }
@@ -22448,7 +22545,7 @@ def_optimize(AST_TemplateString, function(self, compressor) {
22448
22545
  && segments[1] instanceof AST_Node
22449
22546
  && (
22450
22547
  segments[1].is_string(compressor)
22451
- || segments[1].is_number(compressor)
22548
+ || segments[1].is_number_or_bigint(compressor)
22452
22549
  || is_nullish(segments[1], compressor)
22453
22550
  || compressor.option("unsafe")
22454
22551
  )
@@ -48,7 +48,6 @@ export const TRUTHY = 0b00000010;
48
48
  export const FALSY = 0b00000100;
49
49
  export const UNDEFINED = 0b00001000;
50
50
  export const INLINED = 0b00010000;
51
-
52
51
  // Nodes to which values are ever written. Used when keep_assign is part of the unused option string.
53
52
  export const WRITE_ONLY = 0b00100000;
54
53
 
@@ -188,7 +188,6 @@ import {
188
188
  UNUSED,
189
189
  TRUTHY,
190
190
  FALSY,
191
-
192
191
  has_flag,
193
192
  set_flag,
194
193
  clear_flag,
@@ -377,12 +376,16 @@ class Compressor extends TreeWalker {
377
376
  }
378
377
  }
379
378
 
380
- in_32_bit_context() {
379
+ in_32_bit_context(other_operand_must_be_number) {
381
380
  if (!this.option("evaluate")) return false;
382
381
  var self = this.self();
383
382
  for (var i = 0, p; p = this.parent(i); i++) {
384
383
  if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
385
- return true;
384
+ if (other_operand_must_be_number) {
385
+ return (self === p.left ? p.right : p.left).is_number(this);
386
+ } else {
387
+ return true;
388
+ }
386
389
  }
387
390
  if (p instanceof AST_UnaryPrefix) {
388
391
  return p.operator === "~";
@@ -497,6 +500,7 @@ class Compressor extends TreeWalker {
497
500
  }
498
501
  }
499
502
 
503
+
500
504
  function def_optimize(node, optimizer) {
501
505
  node.DEFMETHOD("optimize", function(compressor) {
502
506
  var self = this;
@@ -1721,6 +1725,23 @@ def_optimize(AST_Call, function(self, compressor) {
1721
1725
  self.args.length = last;
1722
1726
  }
1723
1727
 
1728
+ if (
1729
+ exp instanceof AST_Dot
1730
+ && exp.expression instanceof AST_SymbolRef
1731
+ && exp.expression.name === "console"
1732
+ && exp.expression.definition().undeclared
1733
+ && exp.property === "assert"
1734
+ ) {
1735
+ const condition = self.args[0];
1736
+ if (condition) {
1737
+ const value = condition.evaluate(compressor);
1738
+
1739
+ if (value === 1 || value === true) {
1740
+ return make_node(AST_Undefined, self);
1741
+ }
1742
+ }
1743
+ }
1744
+
1724
1745
  if (compressor.option("unsafe") && !exp.contains_optional()) {
1725
1746
  if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
1726
1747
  const [argument] = self.args;
@@ -2135,7 +2156,7 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
2135
2156
  self.operator === "~"
2136
2157
  && self.expression instanceof AST_UnaryPrefix
2137
2158
  && self.expression.operator === "~"
2138
- && (compressor.in_32_bit_context() || self.expression.expression.is_32_bit_integer())
2159
+ && (compressor.in_32_bit_context(false) || self.expression.expression.is_32_bit_integer(compressor))
2139
2160
  ) {
2140
2161
  return self.expression.expression;
2141
2162
  }
@@ -2148,9 +2169,9 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
2148
2169
  ) {
2149
2170
  if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
2150
2171
  // ~(~x ^ y) => x ^ y
2151
- e.left = e.left.bitwise_negate(true);
2172
+ e.left = e.left.bitwise_negate(compressor, true);
2152
2173
  } else {
2153
- e.right = e.right.bitwise_negate(true);
2174
+ e.right = e.right.bitwise_negate(compressor, true);
2154
2175
  }
2155
2176
  return e;
2156
2177
  }
@@ -2245,10 +2266,13 @@ def_optimize(AST_Binary, function(self, compressor) {
2245
2266
  case "===":
2246
2267
  case "!==":
2247
2268
  var is_strict_comparison = true;
2248
- if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
2269
+ if (
2270
+ (self.left.is_string(compressor) && self.right.is_string(compressor)) ||
2249
2271
  (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
2272
+ (self.left.is_bigint(compressor) && self.right.is_bigint(compressor)) ||
2250
2273
  (self.left.is_boolean() && self.right.is_boolean()) ||
2251
- self.left.equivalent_to(self.right)) {
2274
+ self.left.equivalent_to(self.right)
2275
+ ) {
2252
2276
  self.operator = self.operator.substr(0, 2);
2253
2277
  }
2254
2278
 
@@ -2293,7 +2317,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2293
2317
  && self.left.definition() === self.right.definition()
2294
2318
  && is_object(self.left.fixed_value())) {
2295
2319
  return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
2296
- } else if (self.left.is_32_bit_integer() && self.right.is_32_bit_integer()) {
2320
+ } else if (self.left.is_32_bit_integer(compressor) && self.right.is_32_bit_integer(compressor)) {
2297
2321
  const not = node => make_node(AST_UnaryPrefix, node, {
2298
2322
  operator: "!",
2299
2323
  expression: node
@@ -2326,7 +2350,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2326
2350
  && (mask = and_op === self.left ? self.right : self.left)
2327
2351
  && and_op.operator === "&"
2328
2352
  && mask instanceof AST_Number
2329
- && mask.is_32_bit_integer()
2353
+ && mask.is_32_bit_integer(compressor)
2330
2354
  && (x =
2331
2355
  and_op.left.equivalent_to(mask) ? and_op.right
2332
2356
  : and_op.right.equivalent_to(mask) ? and_op.left : null)
@@ -2569,7 +2593,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2569
2593
  // a + -b => a - b
2570
2594
  if (self.right instanceof AST_UnaryPrefix
2571
2595
  && self.right.operator == "-"
2572
- && self.left.is_number(compressor)) {
2596
+ && self.left.is_number_or_bigint(compressor)) {
2573
2597
  self = make_node(AST_Binary, self, {
2574
2598
  operator: "-",
2575
2599
  left: self.left,
@@ -2581,7 +2605,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2581
2605
  if (self.left instanceof AST_UnaryPrefix
2582
2606
  && self.left.operator == "-"
2583
2607
  && reversible()
2584
- && self.right.is_number(compressor)) {
2608
+ && self.right.is_number_or_bigint(compressor)) {
2585
2609
  self = make_node(AST_Binary, self, {
2586
2610
  operator: "-",
2587
2611
  left: self.right,
@@ -2625,8 +2649,9 @@ def_optimize(AST_Binary, function(self, compressor) {
2625
2649
  case "|":
2626
2650
  case "^":
2627
2651
  // a + +b => +b + a
2628
- if (self.left.is_number(compressor)
2629
- && self.right.is_number(compressor)
2652
+ if (
2653
+ self.left.is_number_or_bigint(compressor)
2654
+ && self.right.is_number_or_bigint(compressor)
2630
2655
  && reversible()
2631
2656
  && !(self.left instanceof AST_Binary
2632
2657
  && self.left.operator != self.operator
@@ -2643,7 +2668,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2643
2668
  self = best_of(compressor, self, reversed);
2644
2669
  }
2645
2670
  }
2646
- if (associative && self.is_number(compressor)) {
2671
+ if (associative && self.is_number_or_bigint(compressor)) {
2647
2672
  // a + (b + c) => (a + b) + c
2648
2673
  if (self.right instanceof AST_Binary
2649
2674
  && self.right.operator == self.operator) {
@@ -2762,18 +2787,31 @@ def_optimize(AST_Binary, function(self, compressor) {
2762
2787
  }
2763
2788
  }
2764
2789
 
2765
- // x ^ x => 0
2766
2790
  // x | x => 0 | x
2767
2791
  // x & x => 0 | x
2768
- const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
2769
- if (same_operands) {
2770
- if (self.operator === "^") {
2771
- return make_node(AST_Number, self, { value: 0 });
2772
- }
2773
- if (self.operator === "|" || self.operator === "&") {
2774
- self.left = make_node(AST_Number, self, { value: 0 });
2775
- self.operator = "|";
2776
- }
2792
+ if (
2793
+ (self.operator === "|" || self.operator === "&")
2794
+ && self.left.equivalent_to(self.right)
2795
+ && !self.left.has_side_effects(compressor)
2796
+ && compressor.in_32_bit_context(true)
2797
+ ) {
2798
+ self.left = make_node(AST_Number, self, { value: 0 });
2799
+ self.operator = "|";
2800
+ }
2801
+
2802
+ // ~x ^ ~y => x ^ y
2803
+ if (
2804
+ self.operator === "^"
2805
+ && self.left instanceof AST_UnaryPrefix
2806
+ && self.left.operator === "~"
2807
+ && self.right instanceof AST_UnaryPrefix
2808
+ && self.right.operator === "~"
2809
+ ) {
2810
+ self = make_node(AST_Binary, self, {
2811
+ operator: "^",
2812
+ left: self.left.expression,
2813
+ right: self.right.expression
2814
+ });
2777
2815
  }
2778
2816
 
2779
2817
 
@@ -2797,7 +2835,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2797
2835
  if (
2798
2836
  zero_side
2799
2837
  && (self.operator === "|" || self.operator === "^")
2800
- && (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
2838
+ && (non_zero_side.is_32_bit_integer(compressor) || compressor.in_32_bit_context(true))
2801
2839
  ) {
2802
2840
  return non_zero_side;
2803
2841
  }
@@ -2807,65 +2845,48 @@ def_optimize(AST_Binary, function(self, compressor) {
2807
2845
  zero_side
2808
2846
  && self.operator === "&"
2809
2847
  && !non_zero_side.has_side_effects(compressor)
2848
+ && non_zero_side.is_32_bit_integer(compressor)
2810
2849
  ) {
2811
2850
  return zero_side;
2812
2851
  }
2813
2852
 
2853
+ // ~0 is all ones, as well as -1.
2854
+ // We can ellide some operations with it.
2814
2855
  const is_full_mask = (node) =>
2815
2856
  node instanceof AST_Number && node.value === -1
2816
2857
  ||
2817
- node instanceof AST_UnaryPrefix && (
2818
- node.operator === "-"
2819
- && node.expression instanceof AST_Number
2820
- && node.expression.value === 1
2821
- || node.operator === "~"
2822
- && node.expression instanceof AST_Number
2823
- && node.expression.value === 0);
2858
+ node instanceof AST_UnaryPrefix
2859
+ && node.operator === "-"
2860
+ && node.expression instanceof AST_Number
2861
+ && node.expression.value === 1;
2824
2862
 
2825
2863
  const full_mask = is_full_mask(self.right) ? self.right
2826
2864
  : is_full_mask(self.left) ? self.left
2827
2865
  : null;
2828
- const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
2829
-
2830
- switch (self.operator) {
2831
- case "|":
2832
- // {anything} | -1 => -1
2833
- if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
2834
- return full_mask;
2835
- }
2836
-
2837
- break;
2838
- case "&":
2839
- // {32 bit integer} & -1 => {32 bit integer}
2840
- if (
2841
- full_mask
2842
- && (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
2843
- ) {
2844
- return non_full_mask_side;
2845
- }
2846
-
2847
- break;
2848
- case "^":
2849
- // {anything} ^ -1 => ~{anything}
2850
- if (full_mask) {
2851
- return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
2852
- }
2866
+ const other_side = (full_mask === self.right ? self.left : self.right);
2853
2867
 
2854
- // ~x ^ ~y => x ^ y
2855
- if (
2856
- self.left instanceof AST_UnaryPrefix
2857
- && self.left.operator === "~"
2858
- && self.right instanceof AST_UnaryPrefix
2859
- && self.right.operator === "~"
2860
- ) {
2861
- self = make_node(AST_Binary, self, {
2862
- operator: "^",
2863
- left: self.left.expression,
2864
- right: self.right.expression
2865
- });
2866
- }
2868
+ // {32 bit integer} & -1 => {32 bit integer}
2869
+ if (
2870
+ full_mask
2871
+ && self.operator === "&"
2872
+ && (
2873
+ other_side.is_32_bit_integer(compressor)
2874
+ || compressor.in_32_bit_context(true)
2875
+ )
2876
+ ) {
2877
+ return other_side;
2878
+ }
2867
2879
 
2868
- break;
2880
+ // {anything} ^ -1 => ~{anything}
2881
+ if (
2882
+ full_mask
2883
+ && self.operator === "^"
2884
+ && (
2885
+ other_side.is_32_bit_integer(compressor)
2886
+ || compressor.in_32_bit_context(true)
2887
+ )
2888
+ ) {
2889
+ return other_side.bitwise_negate(compressor);
2869
2890
  }
2870
2891
  }
2871
2892
  }
@@ -3916,7 +3937,7 @@ def_optimize(AST_TemplateString, function(self, compressor) {
3916
3937
  && segments[1] instanceof AST_Node
3917
3938
  && (
3918
3939
  segments[1].is_string(compressor)
3919
- || segments[1].is_number(compressor)
3940
+ || segments[1].is_number_or_bigint(compressor)
3920
3941
  || is_nullish(segments[1], compressor)
3921
3942
  || compressor.option("unsafe")
3922
3943
  )
@@ -175,18 +175,24 @@ export const unary_side_effects = makePredicate("delete ++ --");
175
175
  def_is_number(AST_Node, return_false);
176
176
  def_is_number(AST_Number, return_true);
177
177
  const unary = makePredicate("+ - ~ ++ --");
178
- def_is_number(AST_Unary, function() {
179
- return unary.has(this.operator) && !(this.expression instanceof AST_BigInt);
178
+ def_is_number(AST_Unary, function(compressor) {
179
+ return unary.has(this.operator) && this.expression.is_number(compressor);
180
180
  });
181
181
  const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
182
182
  def_is_number(AST_Binary, function(compressor) {
183
- return numeric_ops.has(this.operator) || this.operator == "+"
184
- && this.left.is_number(compressor)
185
- && this.right.is_number(compressor);
183
+ if (this.operator === "+") {
184
+ // Both sides need to be `number`. Or one is a `number` and the other is number-ish.
185
+ return this.left.is_number(compressor) && this.right.is_number_or_bigint(compressor)
186
+ || this.right.is_number(compressor) && this.left.is_number_or_bigint(compressor);
187
+ } else if (numeric_ops.has(this.operator)) {
188
+ return this.left.is_number(compressor) || this.right.is_number(compressor);
189
+ } else {
190
+ return false;
191
+ }
186
192
  });
187
193
  def_is_number(AST_Assign, function(compressor) {
188
- return numeric_ops.has(this.operator.slice(0, -1))
189
- || this.operator == "=" && this.right.is_number(compressor);
194
+ return (this.operator === "=" || numeric_ops.has(this.operator.slice(0, -1)))
195
+ && this.right.is_number(compressor);
190
196
  });
191
197
  def_is_number(AST_Sequence, function(compressor) {
192
198
  return this.tail_node().is_number(compressor);
@@ -198,19 +204,83 @@ export const unary_side_effects = makePredicate("delete ++ --");
198
204
  node.DEFMETHOD("is_number", func);
199
205
  });
200
206
 
207
+ // methods to determine if an expression returns a BigInt
208
+ (function(def_is_bigint) {
209
+ def_is_bigint(AST_Node, return_false);
210
+ def_is_bigint(AST_BigInt, return_true);
211
+ const unary = makePredicate("+ - ~ ++ --");
212
+ def_is_bigint(AST_Unary, function(compressor) {
213
+ return unary.has(this.operator) && this.expression.is_bigint(compressor);
214
+ });
215
+ const numeric_ops = makePredicate("- * / % & | ^ << >>");
216
+ def_is_bigint(AST_Binary, function(compressor) {
217
+ if (this.operator === "+") {
218
+ return this.left.is_bigint(compressor) && this.right.is_number_or_bigint(compressor)
219
+ || this.right.is_bigint(compressor) && this.left.is_number_or_bigint(compressor);
220
+ } else if (numeric_ops.has(this.operator)) {
221
+ return this.left.is_bigint(compressor) || this.right.is_bigint(compressor);
222
+ } else {
223
+ return false;
224
+ }
225
+ });
226
+ def_is_bigint(AST_Assign, function(compressor) {
227
+ return (numeric_ops.has(this.operator.slice(0, -1)) || this.operator == "=")
228
+ && this.right.is_bigint(compressor);
229
+ });
230
+ def_is_bigint(AST_Sequence, function(compressor) {
231
+ return this.tail_node().is_bigint(compressor);
232
+ });
233
+ def_is_bigint(AST_Conditional, function(compressor) {
234
+ return this.consequent.is_bigint(compressor) && this.alternative.is_bigint(compressor);
235
+ });
236
+ })(function(node, func) {
237
+ node.DEFMETHOD("is_bigint", func);
238
+ });
239
+
240
+ // methods to determine if an expression is a number or a bigint
241
+ (function(def_is_number_or_bigint) {
242
+ def_is_number_or_bigint(AST_Node, return_false);
243
+ def_is_number_or_bigint(AST_Number, return_true);
244
+ def_is_number_or_bigint(AST_BigInt, return_true);
245
+ const numeric_unary_ops = makePredicate("+ - ~ ++ --");
246
+ def_is_number_or_bigint(AST_Unary, function(_compressor) {
247
+ return numeric_unary_ops.has(this.operator);
248
+ });
249
+ const numeric_ops = makePredicate("- * / % & | ^ << >>");
250
+ def_is_number_or_bigint(AST_Binary, function(compressor) {
251
+ return this.operator === "+"
252
+ ? this.left.is_number_or_bigint(compressor) && this.right.is_number_or_bigint(compressor)
253
+ : numeric_ops.has(this.operator);
254
+ });
255
+ def_is_number_or_bigint(AST_Assign, function(compressor) {
256
+ return numeric_ops.has(this.operator.slice(0, -1))
257
+ || this.operator == "=" && this.right.is_number_or_bigint(compressor);
258
+ });
259
+ def_is_number_or_bigint(AST_Sequence, function(compressor) {
260
+ return this.tail_node().is_number_or_bigint(compressor);
261
+ });
262
+ def_is_number_or_bigint(AST_Conditional, function(compressor) {
263
+ return this.consequent.is_number_or_bigint(compressor) && this.alternative.is_number_or_bigint(compressor);
264
+ });
265
+ }(function (node, func) {
266
+ node.DEFMETHOD("is_number_or_bigint", func);
267
+ }));
268
+
269
+
201
270
  // 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
202
271
  (function(def_is_32_bit_integer) {
203
272
  def_is_32_bit_integer(AST_Node, return_false);
204
- def_is_32_bit_integer(AST_Number, function() {
273
+ def_is_32_bit_integer(AST_Number, function(_compressor) {
205
274
  return this.value === (this.value | 0);
206
275
  });
207
- def_is_32_bit_integer(AST_UnaryPrefix, function() {
208
- return this.operator == "~" ? this.expression.is_number()
209
- : this.operator === "+" ? this.expression.is_32_bit_integer()
276
+ def_is_32_bit_integer(AST_UnaryPrefix, function(compressor) {
277
+ return this.operator == "~" ? this.expression.is_number(compressor)
278
+ : this.operator === "+" ? this.expression.is_32_bit_integer(compressor)
210
279
  : false;
211
280
  });
212
- def_is_32_bit_integer(AST_Binary, function() {
213
- return bitwise_binop.has(this.operator);
281
+ def_is_32_bit_integer(AST_Binary, function(compressor) {
282
+ return bitwise_binop.has(this.operator)
283
+ && (this.left.is_number(compressor) || this.right.is_number(compressor));
214
284
  });
215
285
  }(function (node, func) {
216
286
  node.DEFMETHOD("is_32_bit_integer", func);
@@ -847,30 +917,36 @@ export function is_lhs(node, parent) {
847
917
  });
848
918
 
849
919
  (function (def_bitwise_negate) {
850
- function basic_negation(exp) {
920
+ function basic_bitwise_negation(exp) {
851
921
  return make_node(AST_UnaryPrefix, exp, {
852
922
  operator: "~",
853
923
  expression: exp
854
924
  });
855
925
  }
856
926
 
857
- def_bitwise_negate(AST_Node, function() {
858
- return basic_negation(this);
927
+ def_bitwise_negate(AST_Node, function(_compressor) {
928
+ return basic_bitwise_negation(this);
859
929
  });
860
930
 
861
- def_bitwise_negate(AST_Number, function() {
931
+ def_bitwise_negate(AST_Number, function(_compressor) {
862
932
  const neg = ~this.value;
863
933
  if (neg.toString().length > this.value.toString().length) {
864
- return basic_negation(this);
934
+ return basic_bitwise_negation(this);
865
935
  }
866
936
  return make_node(AST_Number, this, { value: neg });
867
937
  });
868
938
 
869
- def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
870
- if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
939
+ def_bitwise_negate(AST_UnaryPrefix, function(compressor, in_32_bit_context) {
940
+ if (
941
+ this.operator == "~"
942
+ && (
943
+ this.expression.is_32_bit_integer(compressor) ||
944
+ (in_32_bit_context != null ? in_32_bit_context : compressor.in_32_bit_context())
945
+ )
946
+ ) {
871
947
  return this.expression;
872
948
  } else {
873
- return basic_negation(this);
949
+ return basic_bitwise_negation(this);
874
950
  }
875
951
  });
876
952
  })(function (node, func) {
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.38.2",
7
+ "version": "5.39.1",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },
@@ -128,7 +128,8 @@
128
128
  "no-unused-vars": [
129
129
  "error",
130
130
  {
131
- "varsIgnorePattern": "^_"
131
+ "varsIgnorePattern": "^_",
132
+ "argsIgnorePattern": "^_"
132
133
  }
133
134
  ],
134
135
  "no-tabs": "error",