terser 5.27.2 → 5.28.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 +4 -0
- package/dist/bundle.min.js +337 -9
- package/lib/compress/common.js +3 -0
- package/lib/compress/evaluate.js +21 -4
- package/lib/compress/index.js +263 -3
- package/lib/compress/inference.js +50 -0
- package/lib/size.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dist/bundle.min.js
CHANGED
@@ -12778,8 +12778,8 @@ AST_Class.prototype._size = function () {
|
|
12778
12778
|
};
|
12779
12779
|
|
12780
12780
|
AST_ClassStaticBlock.prototype._size = function () {
|
12781
|
-
// "
|
12782
|
-
return
|
12781
|
+
// "static{}" + semicolons
|
12782
|
+
return 8 + list_overhead(this.body);
|
12783
12783
|
};
|
12784
12784
|
|
12785
12785
|
AST_ClassProperty.prototype._size = function () {
|
@@ -13008,6 +13008,8 @@ function make_node_from_constant(val, orig) {
|
|
13008
13008
|
operator: "-",
|
13009
13009
|
expression: make_node(AST_Infinity, orig)
|
13010
13010
|
}) : make_node(AST_Infinity, orig);
|
13011
|
+
case "bigint":
|
13012
|
+
return make_node(AST_BigInt, orig, { value: val.toString() });
|
13011
13013
|
case "boolean":
|
13012
13014
|
return make_node(val ? AST_True : AST_False, orig);
|
13013
13015
|
case "undefined":
|
@@ -13486,6 +13488,7 @@ const is_pure_native_value = make_nested_lookup({
|
|
13486
13488
|
const is_undeclared_ref = (node) =>
|
13487
13489
|
node instanceof AST_SymbolRef && node.definition().undeclared;
|
13488
13490
|
|
13491
|
+
const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
|
13489
13492
|
const lazy_op = makePredicate("&& || ??");
|
13490
13493
|
const unary_side_effects = makePredicate("delete ++ --");
|
13491
13494
|
|
@@ -13546,6 +13549,24 @@ const unary_side_effects = makePredicate("delete ++ --");
|
|
13546
13549
|
node.DEFMETHOD("is_number", func);
|
13547
13550
|
});
|
13548
13551
|
|
13552
|
+
// 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
|
13553
|
+
(function(def_is_32_bit_integer) {
|
13554
|
+
def_is_32_bit_integer(AST_Node, return_false);
|
13555
|
+
def_is_32_bit_integer(AST_Number, function() {
|
13556
|
+
return this.value === (this.value | 0);
|
13557
|
+
});
|
13558
|
+
def_is_32_bit_integer(AST_UnaryPrefix, function() {
|
13559
|
+
return this.operator == "~" ? this.expression.is_number()
|
13560
|
+
: this.operator === "+" ? this.expression.is_32_bit_integer()
|
13561
|
+
: false;
|
13562
|
+
});
|
13563
|
+
def_is_32_bit_integer(AST_Binary, function() {
|
13564
|
+
return bitwise_binop.has(this.operator);
|
13565
|
+
});
|
13566
|
+
}(function (node, func) {
|
13567
|
+
node.DEFMETHOD("is_32_bit_integer", func);
|
13568
|
+
}));
|
13569
|
+
|
13549
13570
|
// methods to determine if an expression has a string result type
|
13550
13571
|
(function(def_is_string) {
|
13551
13572
|
def_is_string(AST_Node, return_false);
|
@@ -14160,6 +14181,37 @@ function is_lhs(node, parent) {
|
|
14160
14181
|
});
|
14161
14182
|
});
|
14162
14183
|
|
14184
|
+
(function (def_bitwise_negate) {
|
14185
|
+
function basic_negation(exp) {
|
14186
|
+
return make_node(AST_UnaryPrefix, exp, {
|
14187
|
+
operator: "~",
|
14188
|
+
expression: exp
|
14189
|
+
});
|
14190
|
+
}
|
14191
|
+
|
14192
|
+
def_bitwise_negate(AST_Node, function() {
|
14193
|
+
return basic_negation(this);
|
14194
|
+
});
|
14195
|
+
|
14196
|
+
def_bitwise_negate(AST_Number, function() {
|
14197
|
+
const neg = ~this.value;
|
14198
|
+
if (neg.toString().length > this.value.toString().length) {
|
14199
|
+
return basic_negation(this);
|
14200
|
+
}
|
14201
|
+
return make_node(AST_Number, this, { value: neg });
|
14202
|
+
});
|
14203
|
+
|
14204
|
+
def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
|
14205
|
+
if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
|
14206
|
+
return this.expression;
|
14207
|
+
} else {
|
14208
|
+
return basic_negation(this);
|
14209
|
+
}
|
14210
|
+
});
|
14211
|
+
})(function (node, func) {
|
14212
|
+
node.DEFMETHOD("bitwise_negate", func);
|
14213
|
+
});
|
14214
|
+
|
14163
14215
|
// Is the callee of this function pure?
|
14164
14216
|
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
14217
|
AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
|
@@ -14385,7 +14437,14 @@ def_eval(AST_Constant, function () {
|
|
14385
14437
|
return this.getValue();
|
14386
14438
|
});
|
14387
14439
|
|
14388
|
-
|
14440
|
+
const supports_bigint = typeof BigInt === "function";
|
14441
|
+
def_eval(AST_BigInt, function () {
|
14442
|
+
if (supports_bigint) {
|
14443
|
+
return BigInt(this.value);
|
14444
|
+
} else {
|
14445
|
+
return this;
|
14446
|
+
}
|
14447
|
+
});
|
14389
14448
|
|
14390
14449
|
def_eval(AST_RegExp, function (compressor) {
|
14391
14450
|
let evaluated = compressor.evaluated_regexps.get(this.value);
|
@@ -14509,7 +14568,6 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14509
14568
|
var right = this.right._eval(compressor, depth);
|
14510
14569
|
if (right === this.right)
|
14511
14570
|
return this;
|
14512
|
-
var result;
|
14513
14571
|
|
14514
14572
|
if (left != null
|
14515
14573
|
&& right != null
|
@@ -14521,6 +14579,17 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14521
14579
|
return this;
|
14522
14580
|
}
|
14523
14581
|
|
14582
|
+
// Do not mix BigInt and Number; Don't use `>>>` on BigInt or `/ 0n`
|
14583
|
+
if (
|
14584
|
+
(typeof left === "bigint") !== (typeof right === "bigint")
|
14585
|
+
|| typeof left === "bigint"
|
14586
|
+
&& (this.operator === ">>>"
|
14587
|
+
|| this.operator === "/" && Number(right) === 0)
|
14588
|
+
) {
|
14589
|
+
return this;
|
14590
|
+
}
|
14591
|
+
|
14592
|
+
var result;
|
14524
14593
|
switch (this.operator) {
|
14525
14594
|
case "&&": result = left && right; break;
|
14526
14595
|
case "||": result = left || right; break;
|
@@ -14530,7 +14599,7 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14530
14599
|
case "^": result = left ^ right; break;
|
14531
14600
|
case "+": result = left + right; break;
|
14532
14601
|
case "*": result = left * right; break;
|
14533
|
-
case "**": result =
|
14602
|
+
case "**": result = left ** right; break;
|
14534
14603
|
case "/": result = left / right; break;
|
14535
14604
|
case "%": result = left % right; break;
|
14536
14605
|
case "-": result = left - right; break;
|
@@ -14548,7 +14617,7 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14548
14617
|
default:
|
14549
14618
|
return this;
|
14550
14619
|
}
|
14551
|
-
if (isNaN(result) && compressor.find_parent(AST_With)) {
|
14620
|
+
if (typeof result === "number" && isNaN(result) && compressor.find_parent(AST_With)) {
|
14552
14621
|
// leave original expression as is
|
14553
14622
|
return this;
|
14554
14623
|
}
|
@@ -18546,6 +18615,33 @@ class Compressor extends TreeWalker {
|
|
18546
18615
|
}
|
18547
18616
|
}
|
18548
18617
|
|
18618
|
+
in_32_bit_context() {
|
18619
|
+
if (!this.option("evaluate")) return false;
|
18620
|
+
var self = this.self();
|
18621
|
+
for (var i = 0, p; p = this.parent(i); i++) {
|
18622
|
+
if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
|
18623
|
+
return true;
|
18624
|
+
}
|
18625
|
+
if (p instanceof AST_UnaryPrefix) {
|
18626
|
+
return p.operator === "~";
|
18627
|
+
}
|
18628
|
+
if (
|
18629
|
+
p instanceof AST_Binary
|
18630
|
+
&& (
|
18631
|
+
p.operator == "&&"
|
18632
|
+
|| p.operator == "||"
|
18633
|
+
|| p.operator == "??"
|
18634
|
+
)
|
18635
|
+
|| p instanceof AST_Conditional && p.condition !== self
|
18636
|
+
|| p.tail_node() === self
|
18637
|
+
) {
|
18638
|
+
self = p;
|
18639
|
+
} else {
|
18640
|
+
return false;
|
18641
|
+
}
|
18642
|
+
}
|
18643
|
+
}
|
18644
|
+
|
18549
18645
|
get_toplevel() {
|
18550
18646
|
return this._toplevel;
|
18551
18647
|
}
|
@@ -20244,9 +20340,40 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
|
|
20244
20340
|
right: e.right
|
20245
20341
|
});
|
20246
20342
|
}
|
20247
|
-
|
20248
|
-
if (
|
20249
|
-
|
20343
|
+
|
20344
|
+
if (compressor.option("evaluate")) {
|
20345
|
+
// ~~x => x (in 32-bit context)
|
20346
|
+
// ~~{32 bit integer} => {32 bit integer}
|
20347
|
+
if (
|
20348
|
+
self.operator === "~"
|
20349
|
+
&& self.expression instanceof AST_UnaryPrefix
|
20350
|
+
&& self.expression.operator === "~"
|
20351
|
+
&& (compressor.in_32_bit_context() || self.expression.expression.is_32_bit_integer())
|
20352
|
+
) {
|
20353
|
+
return self.expression.expression;
|
20354
|
+
}
|
20355
|
+
|
20356
|
+
// ~(x ^ y) => x ^ ~y
|
20357
|
+
if (
|
20358
|
+
self.operator === "~"
|
20359
|
+
&& e instanceof AST_Binary
|
20360
|
+
&& e.operator === "^"
|
20361
|
+
) {
|
20362
|
+
if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
|
20363
|
+
// ~(~x ^ y) => x ^ y
|
20364
|
+
e.left = e.left.bitwise_negate(true);
|
20365
|
+
} else {
|
20366
|
+
e.right = e.right.bitwise_negate(true);
|
20367
|
+
}
|
20368
|
+
return e;
|
20369
|
+
}
|
20370
|
+
}
|
20371
|
+
|
20372
|
+
if (
|
20373
|
+
self.operator != "-"
|
20374
|
+
// avoid infinite recursion of numerals
|
20375
|
+
|| !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt)
|
20376
|
+
) {
|
20250
20377
|
var ev = self.evaluate(compressor);
|
20251
20378
|
if (ev !== self) {
|
20252
20379
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
@@ -20337,6 +20464,7 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20337
20464
|
self.left.equivalent_to(self.right)) {
|
20338
20465
|
self.operator = self.operator.substr(0, 2);
|
20339
20466
|
}
|
20467
|
+
|
20340
20468
|
// XXX: intentionally falling down to the next case
|
20341
20469
|
case "==":
|
20342
20470
|
case "!=":
|
@@ -20378,6 +20506,55 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20378
20506
|
&& self.left.definition() === self.right.definition()
|
20379
20507
|
&& is_object(self.left.fixed_value())) {
|
20380
20508
|
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
|
20509
|
+
} else if (self.left.is_32_bit_integer() && self.right.is_32_bit_integer()) {
|
20510
|
+
const not = node => make_node(AST_UnaryPrefix, node, {
|
20511
|
+
operator: "!",
|
20512
|
+
expression: node
|
20513
|
+
});
|
20514
|
+
const booleanify = (node, truthy) => {
|
20515
|
+
if (truthy) {
|
20516
|
+
return compressor.in_boolean_context()
|
20517
|
+
? node
|
20518
|
+
: not(not(node));
|
20519
|
+
} else {
|
20520
|
+
return not(node);
|
20521
|
+
}
|
20522
|
+
};
|
20523
|
+
|
20524
|
+
// The only falsy 32-bit integer is 0
|
20525
|
+
if (self.left instanceof AST_Number && self.left.value === 0) {
|
20526
|
+
return booleanify(self.right, self.operator[0] === "!");
|
20527
|
+
}
|
20528
|
+
if (self.right instanceof AST_Number && self.right.value === 0) {
|
20529
|
+
return booleanify(self.left, self.operator[0] === "!");
|
20530
|
+
}
|
20531
|
+
|
20532
|
+
// Mask all-bits check
|
20533
|
+
// (x & 0xFF) != 0xFF => !(~x & 0xFF)
|
20534
|
+
let and_op, x, mask;
|
20535
|
+
if (
|
20536
|
+
(and_op =
|
20537
|
+
self.left instanceof AST_Binary ? self.left
|
20538
|
+
: self.right instanceof AST_Binary ? self.right : null)
|
20539
|
+
&& (mask = and_op === self.left ? self.right : self.left)
|
20540
|
+
&& and_op.operator === "&"
|
20541
|
+
&& mask instanceof AST_Number
|
20542
|
+
&& mask.is_32_bit_integer()
|
20543
|
+
&& (x =
|
20544
|
+
and_op.left.equivalent_to(mask) ? and_op.right
|
20545
|
+
: and_op.right.equivalent_to(mask) ? and_op.left : null)
|
20546
|
+
) {
|
20547
|
+
let optimized = booleanify(make_node(AST_Binary, self, {
|
20548
|
+
operator: "&",
|
20549
|
+
left: mask,
|
20550
|
+
right: make_node(AST_UnaryPrefix, self, {
|
20551
|
+
operator: "~",
|
20552
|
+
expression: x
|
20553
|
+
})
|
20554
|
+
}), self.operator[0] === "=");
|
20555
|
+
|
20556
|
+
return best_of(compressor, optimized, self);
|
20557
|
+
}
|
20381
20558
|
}
|
20382
20559
|
break;
|
20383
20560
|
case "&&":
|
@@ -20751,6 +20928,157 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20751
20928
|
}
|
20752
20929
|
}
|
20753
20930
|
}
|
20931
|
+
|
20932
|
+
// bitwise ops
|
20933
|
+
if (bitwise_binop.has(self.operator)) {
|
20934
|
+
// Use De Morgan's laws
|
20935
|
+
// z & (X | y)
|
20936
|
+
// => z & X (given y & z === 0)
|
20937
|
+
// => z & X | {y & z} (given y & z !== 0)
|
20938
|
+
let y, z, x_node, y_node, z_node = self.left;
|
20939
|
+
if (
|
20940
|
+
self.operator === "&"
|
20941
|
+
&& self.right instanceof AST_Binary
|
20942
|
+
&& self.right.operator === "|"
|
20943
|
+
&& typeof (z = self.left.evaluate(compressor)) === "number"
|
20944
|
+
) {
|
20945
|
+
if (typeof (y = self.right.right.evaluate(compressor)) === "number") {
|
20946
|
+
// z & (X | y)
|
20947
|
+
x_node = self.right.left;
|
20948
|
+
y_node = self.right.right;
|
20949
|
+
} else if (typeof (y = self.right.left.evaluate(compressor)) === "number") {
|
20950
|
+
// z & (y | X)
|
20951
|
+
x_node = self.right.right;
|
20952
|
+
y_node = self.right.left;
|
20953
|
+
}
|
20954
|
+
|
20955
|
+
if ((y & z) === 0) {
|
20956
|
+
self = make_node(AST_Binary, self, {
|
20957
|
+
operator: self.operator,
|
20958
|
+
left: z_node,
|
20959
|
+
right: x_node
|
20960
|
+
});
|
20961
|
+
} else {
|
20962
|
+
const reordered_ops = make_node(AST_Binary, self, {
|
20963
|
+
operator: "|",
|
20964
|
+
left: make_node(AST_Binary, self, {
|
20965
|
+
operator: "&",
|
20966
|
+
left: x_node,
|
20967
|
+
right: z_node
|
20968
|
+
}),
|
20969
|
+
right: make_node_from_constant(y & z, y_node),
|
20970
|
+
});
|
20971
|
+
|
20972
|
+
self = best_of(compressor, self, reordered_ops);
|
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
|
package/lib/compress/common.js
CHANGED
@@ -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":
|
package/lib/compress/evaluate.js
CHANGED
@@ -133,7 +133,14 @@ def_eval(AST_Constant, function () {
|
|
133
133
|
return this.getValue();
|
134
134
|
});
|
135
135
|
|
136
|
-
|
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 =
|
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
|
}
|
package/lib/compress/index.js
CHANGED
@@ -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
|
-
|
2075
|
-
if (
|
2076
|
-
|
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,157 @@ 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 ((y & z) === 0) {
|
2715
|
+
self = make_node(AST_Binary, self, {
|
2716
|
+
operator: self.operator,
|
2717
|
+
left: z_node,
|
2718
|
+
right: x_node
|
2719
|
+
});
|
2720
|
+
} else {
|
2721
|
+
const reordered_ops = make_node(AST_Binary, self, {
|
2722
|
+
operator: "|",
|
2723
|
+
left: make_node(AST_Binary, self, {
|
2724
|
+
operator: "&",
|
2725
|
+
left: x_node,
|
2726
|
+
right: z_node
|
2727
|
+
}),
|
2728
|
+
right: make_node_from_constant(y & z, y_node),
|
2729
|
+
});
|
2730
|
+
|
2731
|
+
self = best_of(compressor, self, reordered_ops);
|
2732
|
+
}
|
2733
|
+
}
|
2734
|
+
|
2735
|
+
// x ^ x => 0
|
2736
|
+
// x | x => 0 | x
|
2737
|
+
// x & x => 0 | x
|
2738
|
+
const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
|
2739
|
+
if (same_operands) {
|
2740
|
+
if (self.operator === "^") {
|
2741
|
+
return make_node(AST_Number, self, { value: 0 });
|
2742
|
+
}
|
2743
|
+
if (self.operator === "|" || self.operator === "&") {
|
2744
|
+
self.left = make_node(AST_Number, self, { value: 0 });
|
2745
|
+
self.operator = "|";
|
2746
|
+
}
|
2747
|
+
}
|
2748
|
+
|
2749
|
+
|
2750
|
+
// Shifts that do nothing
|
2751
|
+
// {anything} >> 0 => {anything} | 0
|
2752
|
+
// {anything} << 0 => {anything} | 0
|
2753
|
+
if (
|
2754
|
+
(self.operator === "<<" || self.operator === ">>")
|
2755
|
+
&& self.right instanceof AST_Number && self.right.value === 0
|
2756
|
+
) {
|
2757
|
+
self.operator = "|";
|
2758
|
+
}
|
2759
|
+
|
2760
|
+
// Find useless to-bitwise conversions
|
2761
|
+
// {32 bit integer} | 0 => {32 bit integer}
|
2762
|
+
// {32 bit integer} ^ 0 => {32 bit integer}
|
2763
|
+
const zero_side = self.right instanceof AST_Number && self.right.value === 0 ? self.right
|
2764
|
+
: self.left instanceof AST_Number && self.left.value === 0 ? self.left
|
2765
|
+
: null;
|
2766
|
+
const non_zero_side = zero_side && (zero_side === self.right ? self.left : self.right);
|
2767
|
+
if (
|
2768
|
+
zero_side
|
2769
|
+
&& (self.operator === "|" || self.operator === "^")
|
2770
|
+
&& (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
|
2771
|
+
) {
|
2772
|
+
return non_zero_side;
|
2773
|
+
}
|
2774
|
+
|
2775
|
+
// {anything} & 0 => 0
|
2776
|
+
if (
|
2777
|
+
zero_side
|
2778
|
+
&& self.operator === "&"
|
2779
|
+
&& !non_zero_side.has_side_effects(compressor)
|
2780
|
+
) {
|
2781
|
+
return zero_side;
|
2782
|
+
}
|
2783
|
+
|
2784
|
+
const is_full_mask = (node) =>
|
2785
|
+
node instanceof AST_Number && node.value === -1
|
2786
|
+
||
|
2787
|
+
node instanceof AST_UnaryPrefix && (
|
2788
|
+
node.operator === "-"
|
2789
|
+
&& node.expression instanceof AST_Number
|
2790
|
+
&& node.expression.value === 1
|
2791
|
+
|| node.operator === "~"
|
2792
|
+
&& node.expression instanceof AST_Number
|
2793
|
+
&& node.expression.value === 0);
|
2794
|
+
|
2795
|
+
const full_mask = is_full_mask(self.right) ? self.right
|
2796
|
+
: is_full_mask(self.left) ? self.left
|
2797
|
+
: null;
|
2798
|
+
const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
|
2799
|
+
|
2800
|
+
switch (self.operator) {
|
2801
|
+
case "|":
|
2802
|
+
// {anything} | -1 => -1
|
2803
|
+
if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
|
2804
|
+
return full_mask;
|
2805
|
+
}
|
2806
|
+
|
2807
|
+
break;
|
2808
|
+
case "&":
|
2809
|
+
// {32 bit integer} & -1 => {32 bit integer}
|
2810
|
+
if (
|
2811
|
+
full_mask
|
2812
|
+
&& (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
|
2813
|
+
) {
|
2814
|
+
return non_full_mask_side;
|
2815
|
+
}
|
2816
|
+
|
2817
|
+
break;
|
2818
|
+
case "^":
|
2819
|
+
// {anything} ^ -1 => ~{anything}
|
2820
|
+
if (full_mask) {
|
2821
|
+
return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
|
2822
|
+
}
|
2823
|
+
|
2824
|
+
// ~x ^ ~y => x ^ y
|
2825
|
+
if (
|
2826
|
+
self.left instanceof AST_UnaryPrefix
|
2827
|
+
&& self.left.operator === "~"
|
2828
|
+
&& self.right instanceof AST_UnaryPrefix
|
2829
|
+
&& self.right.operator === "~"
|
2830
|
+
) {
|
2831
|
+
self = make_node(AST_Binary, self, {
|
2832
|
+
operator: "^",
|
2833
|
+
left: self.left.expression,
|
2834
|
+
right: self.right.expression
|
2835
|
+
});
|
2836
|
+
}
|
2837
|
+
|
2838
|
+
break;
|
2839
|
+
}
|
2840
|
+
}
|
2581
2841
|
}
|
2582
2842
|
// x && (y && z) ==> x && y && z
|
2583
2843
|
// 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) {
|
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
|
-
// "
|
410
|
-
return
|
409
|
+
// "static{}" + semicolons
|
410
|
+
return 8 + list_overhead(this.body);
|
411
411
|
};
|
412
412
|
|
413
413
|
AST_ClassProperty.prototype._size = function () {
|