terser 5.39.0 → 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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.39.1
4
+
5
+ - Fix bitwise operations that could mix `BigInt` and `number`
6
+
3
7
  ## v5.39.0
4
8
 
5
9
  - Remove unnecessary `console.assert` calls (#1590)
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)`
@@ -13676,18 +13676,24 @@ const unary_side_effects = makePredicate("delete ++ --");
13676
13676
  def_is_number(AST_Node, return_false);
13677
13677
  def_is_number(AST_Number, return_true);
13678
13678
  const unary = makePredicate("+ - ~ ++ --");
13679
- def_is_number(AST_Unary, function() {
13680
- 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);
13681
13681
  });
13682
13682
  const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
13683
13683
  def_is_number(AST_Binary, function(compressor) {
13684
- return numeric_ops.has(this.operator) || this.operator == "+"
13685
- && this.left.is_number(compressor)
13686
- && 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
+ }
13687
13693
  });
13688
13694
  def_is_number(AST_Assign, function(compressor) {
13689
- return numeric_ops.has(this.operator.slice(0, -1))
13690
- || 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);
13691
13697
  });
13692
13698
  def_is_number(AST_Sequence, function(compressor) {
13693
13699
  return this.tail_node().is_number(compressor);
@@ -13699,19 +13705,83 @@ const unary_side_effects = makePredicate("delete ++ --");
13699
13705
  node.DEFMETHOD("is_number", func);
13700
13706
  });
13701
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
+
13702
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
13703
13772
  (function(def_is_32_bit_integer) {
13704
13773
  def_is_32_bit_integer(AST_Node, return_false);
13705
- def_is_32_bit_integer(AST_Number, function() {
13774
+ def_is_32_bit_integer(AST_Number, function(_compressor) {
13706
13775
  return this.value === (this.value | 0);
13707
13776
  });
13708
- def_is_32_bit_integer(AST_UnaryPrefix, function() {
13709
- return this.operator == "~" ? this.expression.is_number()
13710
- : 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)
13711
13780
  : false;
13712
13781
  });
13713
- def_is_32_bit_integer(AST_Binary, function() {
13714
- 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));
13715
13785
  });
13716
13786
  }(function (node, func) {
13717
13787
  node.DEFMETHOD("is_32_bit_integer", func);
@@ -14348,30 +14418,36 @@ function is_lhs(node, parent) {
14348
14418
  });
14349
14419
 
14350
14420
  (function (def_bitwise_negate) {
14351
- function basic_negation(exp) {
14421
+ function basic_bitwise_negation(exp) {
14352
14422
  return make_node(AST_UnaryPrefix, exp, {
14353
14423
  operator: "~",
14354
14424
  expression: exp
14355
14425
  });
14356
14426
  }
14357
14427
 
14358
- def_bitwise_negate(AST_Node, function() {
14359
- return basic_negation(this);
14428
+ def_bitwise_negate(AST_Node, function(_compressor) {
14429
+ return basic_bitwise_negation(this);
14360
14430
  });
14361
14431
 
14362
- def_bitwise_negate(AST_Number, function() {
14432
+ def_bitwise_negate(AST_Number, function(_compressor) {
14363
14433
  const neg = ~this.value;
14364
14434
  if (neg.toString().length > this.value.toString().length) {
14365
- return basic_negation(this);
14435
+ return basic_bitwise_negation(this);
14366
14436
  }
14367
14437
  return make_node(AST_Number, this, { value: neg });
14368
14438
  });
14369
14439
 
14370
- def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
14371
- 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
+ ) {
14372
14448
  return this.expression;
14373
14449
  } else {
14374
- return basic_negation(this);
14450
+ return basic_bitwise_negation(this);
14375
14451
  }
14376
14452
  });
14377
14453
  })(function (node, func) {
@@ -18908,12 +18984,16 @@ class Compressor extends TreeWalker {
18908
18984
  }
18909
18985
  }
18910
18986
 
18911
- in_32_bit_context() {
18987
+ in_32_bit_context(other_operand_must_be_number) {
18912
18988
  if (!this.option("evaluate")) return false;
18913
18989
  var self = this.self();
18914
18990
  for (var i = 0, p; p = this.parent(i); i++) {
18915
18991
  if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
18916
- 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
+ }
18917
18997
  }
18918
18998
  if (p instanceof AST_UnaryPrefix) {
18919
18999
  return p.operator === "~";
@@ -20684,7 +20764,7 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
20684
20764
  self.operator === "~"
20685
20765
  && self.expression instanceof AST_UnaryPrefix
20686
20766
  && self.expression.operator === "~"
20687
- && (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))
20688
20768
  ) {
20689
20769
  return self.expression.expression;
20690
20770
  }
@@ -20697,9 +20777,9 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
20697
20777
  ) {
20698
20778
  if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
20699
20779
  // ~(~x ^ y) => x ^ y
20700
- e.left = e.left.bitwise_negate(true);
20780
+ e.left = e.left.bitwise_negate(compressor, true);
20701
20781
  } else {
20702
- e.right = e.right.bitwise_negate(true);
20782
+ e.right = e.right.bitwise_negate(compressor, true);
20703
20783
  }
20704
20784
  return e;
20705
20785
  }
@@ -20794,10 +20874,13 @@ def_optimize(AST_Binary, function(self, compressor) {
20794
20874
  case "===":
20795
20875
  case "!==":
20796
20876
  var is_strict_comparison = true;
20797
- if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
20877
+ if (
20878
+ (self.left.is_string(compressor) && self.right.is_string(compressor)) ||
20798
20879
  (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
20880
+ (self.left.is_bigint(compressor) && self.right.is_bigint(compressor)) ||
20799
20881
  (self.left.is_boolean() && self.right.is_boolean()) ||
20800
- self.left.equivalent_to(self.right)) {
20882
+ self.left.equivalent_to(self.right)
20883
+ ) {
20801
20884
  self.operator = self.operator.substr(0, 2);
20802
20885
  }
20803
20886
 
@@ -20842,7 +20925,7 @@ def_optimize(AST_Binary, function(self, compressor) {
20842
20925
  && self.left.definition() === self.right.definition()
20843
20926
  && is_object(self.left.fixed_value())) {
20844
20927
  return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
20845
- } 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)) {
20846
20929
  const not = node => make_node(AST_UnaryPrefix, node, {
20847
20930
  operator: "!",
20848
20931
  expression: node
@@ -20875,7 +20958,7 @@ def_optimize(AST_Binary, function(self, compressor) {
20875
20958
  && (mask = and_op === self.left ? self.right : self.left)
20876
20959
  && and_op.operator === "&"
20877
20960
  && mask instanceof AST_Number
20878
- && mask.is_32_bit_integer()
20961
+ && mask.is_32_bit_integer(compressor)
20879
20962
  && (x =
20880
20963
  and_op.left.equivalent_to(mask) ? and_op.right
20881
20964
  : and_op.right.equivalent_to(mask) ? and_op.left : null)
@@ -21118,7 +21201,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21118
21201
  // a + -b => a - b
21119
21202
  if (self.right instanceof AST_UnaryPrefix
21120
21203
  && self.right.operator == "-"
21121
- && self.left.is_number(compressor)) {
21204
+ && self.left.is_number_or_bigint(compressor)) {
21122
21205
  self = make_node(AST_Binary, self, {
21123
21206
  operator: "-",
21124
21207
  left: self.left,
@@ -21130,7 +21213,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21130
21213
  if (self.left instanceof AST_UnaryPrefix
21131
21214
  && self.left.operator == "-"
21132
21215
  && reversible()
21133
- && self.right.is_number(compressor)) {
21216
+ && self.right.is_number_or_bigint(compressor)) {
21134
21217
  self = make_node(AST_Binary, self, {
21135
21218
  operator: "-",
21136
21219
  left: self.right,
@@ -21174,8 +21257,9 @@ def_optimize(AST_Binary, function(self, compressor) {
21174
21257
  case "|":
21175
21258
  case "^":
21176
21259
  // a + +b => +b + a
21177
- if (self.left.is_number(compressor)
21178
- && self.right.is_number(compressor)
21260
+ if (
21261
+ self.left.is_number_or_bigint(compressor)
21262
+ && self.right.is_number_or_bigint(compressor)
21179
21263
  && reversible()
21180
21264
  && !(self.left instanceof AST_Binary
21181
21265
  && self.left.operator != self.operator
@@ -21192,7 +21276,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21192
21276
  self = best_of(compressor, self, reversed);
21193
21277
  }
21194
21278
  }
21195
- if (associative && self.is_number(compressor)) {
21279
+ if (associative && self.is_number_or_bigint(compressor)) {
21196
21280
  // a + (b + c) => (a + b) + c
21197
21281
  if (self.right instanceof AST_Binary
21198
21282
  && self.right.operator == self.operator) {
@@ -21311,18 +21395,31 @@ def_optimize(AST_Binary, function(self, compressor) {
21311
21395
  }
21312
21396
  }
21313
21397
 
21314
- // x ^ x => 0
21315
21398
  // x | x => 0 | x
21316
21399
  // x & x => 0 | x
21317
- const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
21318
- if (same_operands) {
21319
- if (self.operator === "^") {
21320
- return make_node(AST_Number, self, { value: 0 });
21321
- }
21322
- if (self.operator === "|" || self.operator === "&") {
21323
- self.left = make_node(AST_Number, self, { value: 0 });
21324
- self.operator = "|";
21325
- }
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
+ });
21326
21423
  }
21327
21424
 
21328
21425
 
@@ -21346,7 +21443,7 @@ def_optimize(AST_Binary, function(self, compressor) {
21346
21443
  if (
21347
21444
  zero_side
21348
21445
  && (self.operator === "|" || self.operator === "^")
21349
- && (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))
21350
21447
  ) {
21351
21448
  return non_zero_side;
21352
21449
  }
@@ -21356,65 +21453,48 @@ def_optimize(AST_Binary, function(self, compressor) {
21356
21453
  zero_side
21357
21454
  && self.operator === "&"
21358
21455
  && !non_zero_side.has_side_effects(compressor)
21456
+ && non_zero_side.is_32_bit_integer(compressor)
21359
21457
  ) {
21360
21458
  return zero_side;
21361
21459
  }
21362
21460
 
21461
+ // ~0 is all ones, as well as -1.
21462
+ // We can ellide some operations with it.
21363
21463
  const is_full_mask = (node) =>
21364
21464
  node instanceof AST_Number && node.value === -1
21365
21465
  ||
21366
- node instanceof AST_UnaryPrefix && (
21367
- node.operator === "-"
21368
- && node.expression instanceof AST_Number
21369
- && node.expression.value === 1
21370
- || node.operator === "~"
21371
- && node.expression instanceof AST_Number
21372
- && node.expression.value === 0);
21466
+ node instanceof AST_UnaryPrefix
21467
+ && node.operator === "-"
21468
+ && node.expression instanceof AST_Number
21469
+ && node.expression.value === 1;
21373
21470
 
21374
21471
  const full_mask = is_full_mask(self.right) ? self.right
21375
21472
  : is_full_mask(self.left) ? self.left
21376
21473
  : null;
21377
- const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
21378
-
21379
- switch (self.operator) {
21380
- case "|":
21381
- // {anything} | -1 => -1
21382
- if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
21383
- return full_mask;
21384
- }
21474
+ const other_side = (full_mask === self.right ? self.left : self.right);
21385
21475
 
21386
- break;
21387
- case "&":
21388
- // {32 bit integer} & -1 => {32 bit integer}
21389
- if (
21390
- full_mask
21391
- && (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
21392
- ) {
21393
- return non_full_mask_side;
21394
- }
21395
-
21396
- break;
21397
- case "^":
21398
- // {anything} ^ -1 => ~{anything}
21399
- if (full_mask) {
21400
- return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
21401
- }
21402
-
21403
- // ~x ^ ~y => x ^ y
21404
- if (
21405
- self.left instanceof AST_UnaryPrefix
21406
- && self.left.operator === "~"
21407
- && self.right instanceof AST_UnaryPrefix
21408
- && self.right.operator === "~"
21409
- ) {
21410
- self = make_node(AST_Binary, self, {
21411
- operator: "^",
21412
- left: self.left.expression,
21413
- right: self.right.expression
21414
- });
21415
- }
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
+ }
21416
21487
 
21417
- 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);
21418
21498
  }
21419
21499
  }
21420
21500
  }
@@ -22465,7 +22545,7 @@ def_optimize(AST_TemplateString, function(self, compressor) {
22465
22545
  && segments[1] instanceof AST_Node
22466
22546
  && (
22467
22547
  segments[1].is_string(compressor)
22468
- || segments[1].is_number(compressor)
22548
+ || segments[1].is_number_or_bigint(compressor)
22469
22549
  || is_nullish(segments[1], compressor)
22470
22550
  || compressor.option("unsafe")
22471
22551
  )
@@ -376,12 +376,16 @@ class Compressor extends TreeWalker {
376
376
  }
377
377
  }
378
378
 
379
- in_32_bit_context() {
379
+ in_32_bit_context(other_operand_must_be_number) {
380
380
  if (!this.option("evaluate")) return false;
381
381
  var self = this.self();
382
382
  for (var i = 0, p; p = this.parent(i); i++) {
383
383
  if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
384
- 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
+ }
385
389
  }
386
390
  if (p instanceof AST_UnaryPrefix) {
387
391
  return p.operator === "~";
@@ -2152,7 +2156,7 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
2152
2156
  self.operator === "~"
2153
2157
  && self.expression instanceof AST_UnaryPrefix
2154
2158
  && self.expression.operator === "~"
2155
- && (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))
2156
2160
  ) {
2157
2161
  return self.expression.expression;
2158
2162
  }
@@ -2165,9 +2169,9 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
2165
2169
  ) {
2166
2170
  if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
2167
2171
  // ~(~x ^ y) => x ^ y
2168
- e.left = e.left.bitwise_negate(true);
2172
+ e.left = e.left.bitwise_negate(compressor, true);
2169
2173
  } else {
2170
- e.right = e.right.bitwise_negate(true);
2174
+ e.right = e.right.bitwise_negate(compressor, true);
2171
2175
  }
2172
2176
  return e;
2173
2177
  }
@@ -2262,10 +2266,13 @@ def_optimize(AST_Binary, function(self, compressor) {
2262
2266
  case "===":
2263
2267
  case "!==":
2264
2268
  var is_strict_comparison = true;
2265
- if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
2269
+ if (
2270
+ (self.left.is_string(compressor) && self.right.is_string(compressor)) ||
2266
2271
  (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
2272
+ (self.left.is_bigint(compressor) && self.right.is_bigint(compressor)) ||
2267
2273
  (self.left.is_boolean() && self.right.is_boolean()) ||
2268
- self.left.equivalent_to(self.right)) {
2274
+ self.left.equivalent_to(self.right)
2275
+ ) {
2269
2276
  self.operator = self.operator.substr(0, 2);
2270
2277
  }
2271
2278
 
@@ -2310,7 +2317,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2310
2317
  && self.left.definition() === self.right.definition()
2311
2318
  && is_object(self.left.fixed_value())) {
2312
2319
  return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
2313
- } 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)) {
2314
2321
  const not = node => make_node(AST_UnaryPrefix, node, {
2315
2322
  operator: "!",
2316
2323
  expression: node
@@ -2343,7 +2350,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2343
2350
  && (mask = and_op === self.left ? self.right : self.left)
2344
2351
  && and_op.operator === "&"
2345
2352
  && mask instanceof AST_Number
2346
- && mask.is_32_bit_integer()
2353
+ && mask.is_32_bit_integer(compressor)
2347
2354
  && (x =
2348
2355
  and_op.left.equivalent_to(mask) ? and_op.right
2349
2356
  : and_op.right.equivalent_to(mask) ? and_op.left : null)
@@ -2586,7 +2593,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2586
2593
  // a + -b => a - b
2587
2594
  if (self.right instanceof AST_UnaryPrefix
2588
2595
  && self.right.operator == "-"
2589
- && self.left.is_number(compressor)) {
2596
+ && self.left.is_number_or_bigint(compressor)) {
2590
2597
  self = make_node(AST_Binary, self, {
2591
2598
  operator: "-",
2592
2599
  left: self.left,
@@ -2598,7 +2605,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2598
2605
  if (self.left instanceof AST_UnaryPrefix
2599
2606
  && self.left.operator == "-"
2600
2607
  && reversible()
2601
- && self.right.is_number(compressor)) {
2608
+ && self.right.is_number_or_bigint(compressor)) {
2602
2609
  self = make_node(AST_Binary, self, {
2603
2610
  operator: "-",
2604
2611
  left: self.right,
@@ -2642,8 +2649,9 @@ def_optimize(AST_Binary, function(self, compressor) {
2642
2649
  case "|":
2643
2650
  case "^":
2644
2651
  // a + +b => +b + a
2645
- if (self.left.is_number(compressor)
2646
- && self.right.is_number(compressor)
2652
+ if (
2653
+ self.left.is_number_or_bigint(compressor)
2654
+ && self.right.is_number_or_bigint(compressor)
2647
2655
  && reversible()
2648
2656
  && !(self.left instanceof AST_Binary
2649
2657
  && self.left.operator != self.operator
@@ -2660,7 +2668,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2660
2668
  self = best_of(compressor, self, reversed);
2661
2669
  }
2662
2670
  }
2663
- if (associative && self.is_number(compressor)) {
2671
+ if (associative && self.is_number_or_bigint(compressor)) {
2664
2672
  // a + (b + c) => (a + b) + c
2665
2673
  if (self.right instanceof AST_Binary
2666
2674
  && self.right.operator == self.operator) {
@@ -2779,18 +2787,31 @@ def_optimize(AST_Binary, function(self, compressor) {
2779
2787
  }
2780
2788
  }
2781
2789
 
2782
- // x ^ x => 0
2783
2790
  // x | x => 0 | x
2784
2791
  // x & x => 0 | x
2785
- const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
2786
- if (same_operands) {
2787
- if (self.operator === "^") {
2788
- return make_node(AST_Number, self, { value: 0 });
2789
- }
2790
- if (self.operator === "|" || self.operator === "&") {
2791
- self.left = make_node(AST_Number, self, { value: 0 });
2792
- self.operator = "|";
2793
- }
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
+ });
2794
2815
  }
2795
2816
 
2796
2817
 
@@ -2814,7 +2835,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2814
2835
  if (
2815
2836
  zero_side
2816
2837
  && (self.operator === "|" || self.operator === "^")
2817
- && (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))
2818
2839
  ) {
2819
2840
  return non_zero_side;
2820
2841
  }
@@ -2824,65 +2845,48 @@ def_optimize(AST_Binary, function(self, compressor) {
2824
2845
  zero_side
2825
2846
  && self.operator === "&"
2826
2847
  && !non_zero_side.has_side_effects(compressor)
2848
+ && non_zero_side.is_32_bit_integer(compressor)
2827
2849
  ) {
2828
2850
  return zero_side;
2829
2851
  }
2830
2852
 
2853
+ // ~0 is all ones, as well as -1.
2854
+ // We can ellide some operations with it.
2831
2855
  const is_full_mask = (node) =>
2832
2856
  node instanceof AST_Number && node.value === -1
2833
2857
  ||
2834
- node instanceof AST_UnaryPrefix && (
2835
- node.operator === "-"
2836
- && node.expression instanceof AST_Number
2837
- && node.expression.value === 1
2838
- || node.operator === "~"
2839
- && node.expression instanceof AST_Number
2840
- && node.expression.value === 0);
2858
+ node instanceof AST_UnaryPrefix
2859
+ && node.operator === "-"
2860
+ && node.expression instanceof AST_Number
2861
+ && node.expression.value === 1;
2841
2862
 
2842
2863
  const full_mask = is_full_mask(self.right) ? self.right
2843
2864
  : is_full_mask(self.left) ? self.left
2844
2865
  : null;
2845
- const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
2866
+ const other_side = (full_mask === self.right ? self.left : self.right);
2846
2867
 
2847
- switch (self.operator) {
2848
- case "|":
2849
- // {anything} | -1 => -1
2850
- if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
2851
- return full_mask;
2852
- }
2853
-
2854
- break;
2855
- case "&":
2856
- // {32 bit integer} & -1 => {32 bit integer}
2857
- if (
2858
- full_mask
2859
- && (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
2860
- ) {
2861
- return non_full_mask_side;
2862
- }
2863
-
2864
- break;
2865
- case "^":
2866
- // {anything} ^ -1 => ~{anything}
2867
- if (full_mask) {
2868
- return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
2869
- }
2870
-
2871
- // ~x ^ ~y => x ^ y
2872
- if (
2873
- self.left instanceof AST_UnaryPrefix
2874
- && self.left.operator === "~"
2875
- && self.right instanceof AST_UnaryPrefix
2876
- && self.right.operator === "~"
2877
- ) {
2878
- self = make_node(AST_Binary, self, {
2879
- operator: "^",
2880
- left: self.left.expression,
2881
- right: self.right.expression
2882
- });
2883
- }
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
+ }
2884
2879
 
2885
- 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);
2886
2890
  }
2887
2891
  }
2888
2892
  }
@@ -3933,7 +3937,7 @@ def_optimize(AST_TemplateString, function(self, compressor) {
3933
3937
  && segments[1] instanceof AST_Node
3934
3938
  && (
3935
3939
  segments[1].is_string(compressor)
3936
- || segments[1].is_number(compressor)
3940
+ || segments[1].is_number_or_bigint(compressor)
3937
3941
  || is_nullish(segments[1], compressor)
3938
3942
  || compressor.option("unsafe")
3939
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.39.0",
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",