terser 5.28.1 → 5.29.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 +8 -0
- package/dist/bundle.min.js +340 -12
- package/lib/compress/common.js +3 -0
- package/lib/compress/evaluate.js +21 -4
- package/lib/compress/index.js +265 -3
- package/lib/compress/inference.js +50 -0
- package/lib/mozilla-ast.js +1 -3
- package/lib/size.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v5.29.1
|
4
|
+
- fix optimisation of all-bits mask check
|
5
|
+
|
6
|
+
## v5.29.0
|
7
|
+
- Re-releases previously reverted 5.28.0
|
8
|
+
- Fix crash while optimizing some bitwise ops
|
9
|
+
- (internal) Remove needless wrapper for from_moz (#1499)
|
10
|
+
|
3
11
|
## v5.28.1
|
4
12
|
(hotfix release)
|
5
13
|
- Reverts v5.28.0
|
package/dist/bundle.min.js
CHANGED
@@ -7583,9 +7583,7 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
|
|
7583
7583
|
start: my_start_token(M),
|
7584
7584
|
end: my_end_token(M),
|
7585
7585
|
exported_definition: from_moz(M.declaration),
|
7586
|
-
exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(
|
7587
|
-
return from_moz(specifier);
|
7588
|
-
}) : null,
|
7586
|
+
exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(from_moz) : null,
|
7589
7587
|
module_name: from_moz(M.source),
|
7590
7588
|
assert_clause: assert_clause_from_moz(M.assertions)
|
7591
7589
|
});
|
@@ -12778,8 +12776,8 @@ AST_Class.prototype._size = function () {
|
|
12778
12776
|
};
|
12779
12777
|
|
12780
12778
|
AST_ClassStaticBlock.prototype._size = function () {
|
12781
|
-
// "
|
12782
|
-
return
|
12779
|
+
// "static{}" + semicolons
|
12780
|
+
return 8 + list_overhead(this.body);
|
12783
12781
|
};
|
12784
12782
|
|
12785
12783
|
AST_ClassProperty.prototype._size = function () {
|
@@ -13008,6 +13006,8 @@ function make_node_from_constant(val, orig) {
|
|
13008
13006
|
operator: "-",
|
13009
13007
|
expression: make_node(AST_Infinity, orig)
|
13010
13008
|
}) : make_node(AST_Infinity, orig);
|
13009
|
+
case "bigint":
|
13010
|
+
return make_node(AST_BigInt, orig, { value: val.toString() });
|
13011
13011
|
case "boolean":
|
13012
13012
|
return make_node(val ? AST_True : AST_False, orig);
|
13013
13013
|
case "undefined":
|
@@ -13486,6 +13486,7 @@ const is_pure_native_value = make_nested_lookup({
|
|
13486
13486
|
const is_undeclared_ref = (node) =>
|
13487
13487
|
node instanceof AST_SymbolRef && node.definition().undeclared;
|
13488
13488
|
|
13489
|
+
const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
|
13489
13490
|
const lazy_op = makePredicate("&& || ??");
|
13490
13491
|
const unary_side_effects = makePredicate("delete ++ --");
|
13491
13492
|
|
@@ -13546,6 +13547,24 @@ const unary_side_effects = makePredicate("delete ++ --");
|
|
13546
13547
|
node.DEFMETHOD("is_number", func);
|
13547
13548
|
});
|
13548
13549
|
|
13550
|
+
// methods to determine if an expression is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size
|
13551
|
+
(function(def_is_32_bit_integer) {
|
13552
|
+
def_is_32_bit_integer(AST_Node, return_false);
|
13553
|
+
def_is_32_bit_integer(AST_Number, function() {
|
13554
|
+
return this.value === (this.value | 0);
|
13555
|
+
});
|
13556
|
+
def_is_32_bit_integer(AST_UnaryPrefix, function() {
|
13557
|
+
return this.operator == "~" ? this.expression.is_number()
|
13558
|
+
: this.operator === "+" ? this.expression.is_32_bit_integer()
|
13559
|
+
: false;
|
13560
|
+
});
|
13561
|
+
def_is_32_bit_integer(AST_Binary, function() {
|
13562
|
+
return bitwise_binop.has(this.operator);
|
13563
|
+
});
|
13564
|
+
}(function (node, func) {
|
13565
|
+
node.DEFMETHOD("is_32_bit_integer", func);
|
13566
|
+
}));
|
13567
|
+
|
13549
13568
|
// methods to determine if an expression has a string result type
|
13550
13569
|
(function(def_is_string) {
|
13551
13570
|
def_is_string(AST_Node, return_false);
|
@@ -14160,6 +14179,37 @@ function is_lhs(node, parent) {
|
|
14160
14179
|
});
|
14161
14180
|
});
|
14162
14181
|
|
14182
|
+
(function (def_bitwise_negate) {
|
14183
|
+
function basic_negation(exp) {
|
14184
|
+
return make_node(AST_UnaryPrefix, exp, {
|
14185
|
+
operator: "~",
|
14186
|
+
expression: exp
|
14187
|
+
});
|
14188
|
+
}
|
14189
|
+
|
14190
|
+
def_bitwise_negate(AST_Node, function() {
|
14191
|
+
return basic_negation(this);
|
14192
|
+
});
|
14193
|
+
|
14194
|
+
def_bitwise_negate(AST_Number, function() {
|
14195
|
+
const neg = ~this.value;
|
14196
|
+
if (neg.toString().length > this.value.toString().length) {
|
14197
|
+
return basic_negation(this);
|
14198
|
+
}
|
14199
|
+
return make_node(AST_Number, this, { value: neg });
|
14200
|
+
});
|
14201
|
+
|
14202
|
+
def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
|
14203
|
+
if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
|
14204
|
+
return this.expression;
|
14205
|
+
} else {
|
14206
|
+
return basic_negation(this);
|
14207
|
+
}
|
14208
|
+
});
|
14209
|
+
})(function (node, func) {
|
14210
|
+
node.DEFMETHOD("bitwise_negate", func);
|
14211
|
+
});
|
14212
|
+
|
14163
14213
|
// Is the callee of this function pure?
|
14164
14214
|
var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
|
14165
14215
|
AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
|
@@ -14385,7 +14435,14 @@ def_eval(AST_Constant, function () {
|
|
14385
14435
|
return this.getValue();
|
14386
14436
|
});
|
14387
14437
|
|
14388
|
-
|
14438
|
+
const supports_bigint = typeof BigInt === "function";
|
14439
|
+
def_eval(AST_BigInt, function () {
|
14440
|
+
if (supports_bigint) {
|
14441
|
+
return BigInt(this.value);
|
14442
|
+
} else {
|
14443
|
+
return this;
|
14444
|
+
}
|
14445
|
+
});
|
14389
14446
|
|
14390
14447
|
def_eval(AST_RegExp, function (compressor) {
|
14391
14448
|
let evaluated = compressor.evaluated_regexps.get(this.value);
|
@@ -14509,7 +14566,6 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14509
14566
|
var right = this.right._eval(compressor, depth);
|
14510
14567
|
if (right === this.right)
|
14511
14568
|
return this;
|
14512
|
-
var result;
|
14513
14569
|
|
14514
14570
|
if (left != null
|
14515
14571
|
&& right != null
|
@@ -14521,6 +14577,17 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14521
14577
|
return this;
|
14522
14578
|
}
|
14523
14579
|
|
14580
|
+
// Do not mix BigInt and Number; Don't use `>>>` on BigInt or `/ 0n`
|
14581
|
+
if (
|
14582
|
+
(typeof left === "bigint") !== (typeof right === "bigint")
|
14583
|
+
|| typeof left === "bigint"
|
14584
|
+
&& (this.operator === ">>>"
|
14585
|
+
|| this.operator === "/" && Number(right) === 0)
|
14586
|
+
) {
|
14587
|
+
return this;
|
14588
|
+
}
|
14589
|
+
|
14590
|
+
var result;
|
14524
14591
|
switch (this.operator) {
|
14525
14592
|
case "&&": result = left && right; break;
|
14526
14593
|
case "||": result = left || right; break;
|
@@ -14530,7 +14597,7 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14530
14597
|
case "^": result = left ^ right; break;
|
14531
14598
|
case "+": result = left + right; break;
|
14532
14599
|
case "*": result = left * right; break;
|
14533
|
-
case "**": result =
|
14600
|
+
case "**": result = left ** right; break;
|
14534
14601
|
case "/": result = left / right; break;
|
14535
14602
|
case "%": result = left % right; break;
|
14536
14603
|
case "-": result = left - right; break;
|
@@ -14548,7 +14615,7 @@ def_eval(AST_Binary, function (compressor, depth) {
|
|
14548
14615
|
default:
|
14549
14616
|
return this;
|
14550
14617
|
}
|
14551
|
-
if (isNaN(result) && compressor.find_parent(AST_With)) {
|
14618
|
+
if (typeof result === "number" && isNaN(result) && compressor.find_parent(AST_With)) {
|
14552
14619
|
// leave original expression as is
|
14553
14620
|
return this;
|
14554
14621
|
}
|
@@ -18546,6 +18613,33 @@ class Compressor extends TreeWalker {
|
|
18546
18613
|
}
|
18547
18614
|
}
|
18548
18615
|
|
18616
|
+
in_32_bit_context() {
|
18617
|
+
if (!this.option("evaluate")) return false;
|
18618
|
+
var self = this.self();
|
18619
|
+
for (var i = 0, p; p = this.parent(i); i++) {
|
18620
|
+
if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) {
|
18621
|
+
return true;
|
18622
|
+
}
|
18623
|
+
if (p instanceof AST_UnaryPrefix) {
|
18624
|
+
return p.operator === "~";
|
18625
|
+
}
|
18626
|
+
if (
|
18627
|
+
p instanceof AST_Binary
|
18628
|
+
&& (
|
18629
|
+
p.operator == "&&"
|
18630
|
+
|| p.operator == "||"
|
18631
|
+
|| p.operator == "??"
|
18632
|
+
)
|
18633
|
+
|| p instanceof AST_Conditional && p.condition !== self
|
18634
|
+
|| p.tail_node() === self
|
18635
|
+
) {
|
18636
|
+
self = p;
|
18637
|
+
} else {
|
18638
|
+
return false;
|
18639
|
+
}
|
18640
|
+
}
|
18641
|
+
}
|
18642
|
+
|
18549
18643
|
get_toplevel() {
|
18550
18644
|
return this._toplevel;
|
18551
18645
|
}
|
@@ -20244,9 +20338,40 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
|
|
20244
20338
|
right: e.right
|
20245
20339
|
});
|
20246
20340
|
}
|
20247
|
-
|
20248
|
-
if (
|
20249
|
-
|
20341
|
+
|
20342
|
+
if (compressor.option("evaluate")) {
|
20343
|
+
// ~~x => x (in 32-bit context)
|
20344
|
+
// ~~{32 bit integer} => {32 bit integer}
|
20345
|
+
if (
|
20346
|
+
self.operator === "~"
|
20347
|
+
&& self.expression instanceof AST_UnaryPrefix
|
20348
|
+
&& self.expression.operator === "~"
|
20349
|
+
&& (compressor.in_32_bit_context() || self.expression.expression.is_32_bit_integer())
|
20350
|
+
) {
|
20351
|
+
return self.expression.expression;
|
20352
|
+
}
|
20353
|
+
|
20354
|
+
// ~(x ^ y) => x ^ ~y
|
20355
|
+
if (
|
20356
|
+
self.operator === "~"
|
20357
|
+
&& e instanceof AST_Binary
|
20358
|
+
&& e.operator === "^"
|
20359
|
+
) {
|
20360
|
+
if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") {
|
20361
|
+
// ~(~x ^ y) => x ^ y
|
20362
|
+
e.left = e.left.bitwise_negate(true);
|
20363
|
+
} else {
|
20364
|
+
e.right = e.right.bitwise_negate(true);
|
20365
|
+
}
|
20366
|
+
return e;
|
20367
|
+
}
|
20368
|
+
}
|
20369
|
+
|
20370
|
+
if (
|
20371
|
+
self.operator != "-"
|
20372
|
+
// avoid infinite recursion of numerals
|
20373
|
+
|| !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt)
|
20374
|
+
) {
|
20250
20375
|
var ev = self.evaluate(compressor);
|
20251
20376
|
if (ev !== self) {
|
20252
20377
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
@@ -20337,6 +20462,7 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20337
20462
|
self.left.equivalent_to(self.right)) {
|
20338
20463
|
self.operator = self.operator.substr(0, 2);
|
20339
20464
|
}
|
20465
|
+
|
20340
20466
|
// XXX: intentionally falling down to the next case
|
20341
20467
|
case "==":
|
20342
20468
|
case "!=":
|
@@ -20378,6 +20504,55 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20378
20504
|
&& self.left.definition() === self.right.definition()
|
20379
20505
|
&& is_object(self.left.fixed_value())) {
|
20380
20506
|
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
|
20507
|
+
} else if (self.left.is_32_bit_integer() && self.right.is_32_bit_integer()) {
|
20508
|
+
const not = node => make_node(AST_UnaryPrefix, node, {
|
20509
|
+
operator: "!",
|
20510
|
+
expression: node
|
20511
|
+
});
|
20512
|
+
const booleanify = (node, truthy) => {
|
20513
|
+
if (truthy) {
|
20514
|
+
return compressor.in_boolean_context()
|
20515
|
+
? node
|
20516
|
+
: not(not(node));
|
20517
|
+
} else {
|
20518
|
+
return not(node);
|
20519
|
+
}
|
20520
|
+
};
|
20521
|
+
|
20522
|
+
// The only falsy 32-bit integer is 0
|
20523
|
+
if (self.left instanceof AST_Number && self.left.value === 0) {
|
20524
|
+
return booleanify(self.right, self.operator[0] === "!");
|
20525
|
+
}
|
20526
|
+
if (self.right instanceof AST_Number && self.right.value === 0) {
|
20527
|
+
return booleanify(self.left, self.operator[0] === "!");
|
20528
|
+
}
|
20529
|
+
|
20530
|
+
// Mask all-bits check
|
20531
|
+
// (x & 0xFF) != 0xFF => !(~x & 0xFF)
|
20532
|
+
let and_op, x, mask;
|
20533
|
+
if (
|
20534
|
+
(and_op =
|
20535
|
+
self.left instanceof AST_Binary ? self.left
|
20536
|
+
: self.right instanceof AST_Binary ? self.right : null)
|
20537
|
+
&& (mask = and_op === self.left ? self.right : self.left)
|
20538
|
+
&& and_op.operator === "&"
|
20539
|
+
&& mask instanceof AST_Number
|
20540
|
+
&& mask.is_32_bit_integer()
|
20541
|
+
&& (x =
|
20542
|
+
and_op.left.equivalent_to(mask) ? and_op.right
|
20543
|
+
: and_op.right.equivalent_to(mask) ? and_op.left : null)
|
20544
|
+
) {
|
20545
|
+
let optimized = booleanify(make_node(AST_Binary, self, {
|
20546
|
+
operator: "&",
|
20547
|
+
left: mask,
|
20548
|
+
right: make_node(AST_UnaryPrefix, self, {
|
20549
|
+
operator: "~",
|
20550
|
+
expression: x
|
20551
|
+
})
|
20552
|
+
}), self.operator[0] === "!");
|
20553
|
+
|
20554
|
+
return best_of(compressor, optimized, self);
|
20555
|
+
}
|
20381
20556
|
}
|
20382
20557
|
break;
|
20383
20558
|
case "&&":
|
@@ -20751,6 +20926,159 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20751
20926
|
}
|
20752
20927
|
}
|
20753
20928
|
}
|
20929
|
+
|
20930
|
+
// bitwise ops
|
20931
|
+
if (bitwise_binop.has(self.operator)) {
|
20932
|
+
// Use De Morgan's laws
|
20933
|
+
// z & (X | y)
|
20934
|
+
// => z & X (given y & z === 0)
|
20935
|
+
// => z & X | {y & z} (given y & z !== 0)
|
20936
|
+
let y, z, x_node, y_node, z_node = self.left;
|
20937
|
+
if (
|
20938
|
+
self.operator === "&"
|
20939
|
+
&& self.right instanceof AST_Binary
|
20940
|
+
&& self.right.operator === "|"
|
20941
|
+
&& typeof (z = self.left.evaluate(compressor)) === "number"
|
20942
|
+
) {
|
20943
|
+
if (typeof (y = self.right.right.evaluate(compressor)) === "number") {
|
20944
|
+
// z & (X | y)
|
20945
|
+
x_node = self.right.left;
|
20946
|
+
y_node = self.right.right;
|
20947
|
+
} else if (typeof (y = self.right.left.evaluate(compressor)) === "number") {
|
20948
|
+
// z & (y | X)
|
20949
|
+
x_node = self.right.right;
|
20950
|
+
y_node = self.right.left;
|
20951
|
+
}
|
20952
|
+
|
20953
|
+
if (x_node && y_node) {
|
20954
|
+
if ((y & z) === 0) {
|
20955
|
+
self = make_node(AST_Binary, self, {
|
20956
|
+
operator: self.operator,
|
20957
|
+
left: z_node,
|
20958
|
+
right: x_node
|
20959
|
+
});
|
20960
|
+
} else {
|
20961
|
+
const reordered_ops = make_node(AST_Binary, self, {
|
20962
|
+
operator: "|",
|
20963
|
+
left: make_node(AST_Binary, self, {
|
20964
|
+
operator: "&",
|
20965
|
+
left: x_node,
|
20966
|
+
right: z_node
|
20967
|
+
}),
|
20968
|
+
right: make_node_from_constant(y & z, y_node),
|
20969
|
+
});
|
20970
|
+
|
20971
|
+
self = best_of(compressor, self, reordered_ops);
|
20972
|
+
}
|
20973
|
+
}
|
20974
|
+
}
|
20975
|
+
|
20976
|
+
// x ^ x => 0
|
20977
|
+
// x | x => 0 | x
|
20978
|
+
// x & x => 0 | x
|
20979
|
+
const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
|
20980
|
+
if (same_operands) {
|
20981
|
+
if (self.operator === "^") {
|
20982
|
+
return make_node(AST_Number, self, { value: 0 });
|
20983
|
+
}
|
20984
|
+
if (self.operator === "|" || self.operator === "&") {
|
20985
|
+
self.left = make_node(AST_Number, self, { value: 0 });
|
20986
|
+
self.operator = "|";
|
20987
|
+
}
|
20988
|
+
}
|
20989
|
+
|
20990
|
+
|
20991
|
+
// Shifts that do nothing
|
20992
|
+
// {anything} >> 0 => {anything} | 0
|
20993
|
+
// {anything} << 0 => {anything} | 0
|
20994
|
+
if (
|
20995
|
+
(self.operator === "<<" || self.operator === ">>")
|
20996
|
+
&& self.right instanceof AST_Number && self.right.value === 0
|
20997
|
+
) {
|
20998
|
+
self.operator = "|";
|
20999
|
+
}
|
21000
|
+
|
21001
|
+
// Find useless to-bitwise conversions
|
21002
|
+
// {32 bit integer} | 0 => {32 bit integer}
|
21003
|
+
// {32 bit integer} ^ 0 => {32 bit integer}
|
21004
|
+
const zero_side = self.right instanceof AST_Number && self.right.value === 0 ? self.right
|
21005
|
+
: self.left instanceof AST_Number && self.left.value === 0 ? self.left
|
21006
|
+
: null;
|
21007
|
+
const non_zero_side = zero_side && (zero_side === self.right ? self.left : self.right);
|
21008
|
+
if (
|
21009
|
+
zero_side
|
21010
|
+
&& (self.operator === "|" || self.operator === "^")
|
21011
|
+
&& (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
|
21012
|
+
) {
|
21013
|
+
return non_zero_side;
|
21014
|
+
}
|
21015
|
+
|
21016
|
+
// {anything} & 0 => 0
|
21017
|
+
if (
|
21018
|
+
zero_side
|
21019
|
+
&& self.operator === "&"
|
21020
|
+
&& !non_zero_side.has_side_effects(compressor)
|
21021
|
+
) {
|
21022
|
+
return zero_side;
|
21023
|
+
}
|
21024
|
+
|
21025
|
+
const is_full_mask = (node) =>
|
21026
|
+
node instanceof AST_Number && node.value === -1
|
21027
|
+
||
|
21028
|
+
node instanceof AST_UnaryPrefix && (
|
21029
|
+
node.operator === "-"
|
21030
|
+
&& node.expression instanceof AST_Number
|
21031
|
+
&& node.expression.value === 1
|
21032
|
+
|| node.operator === "~"
|
21033
|
+
&& node.expression instanceof AST_Number
|
21034
|
+
&& node.expression.value === 0);
|
21035
|
+
|
21036
|
+
const full_mask = is_full_mask(self.right) ? self.right
|
21037
|
+
: is_full_mask(self.left) ? self.left
|
21038
|
+
: null;
|
21039
|
+
const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
|
21040
|
+
|
21041
|
+
switch (self.operator) {
|
21042
|
+
case "|":
|
21043
|
+
// {anything} | -1 => -1
|
21044
|
+
if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
|
21045
|
+
return full_mask;
|
21046
|
+
}
|
21047
|
+
|
21048
|
+
break;
|
21049
|
+
case "&":
|
21050
|
+
// {32 bit integer} & -1 => {32 bit integer}
|
21051
|
+
if (
|
21052
|
+
full_mask
|
21053
|
+
&& (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
|
21054
|
+
) {
|
21055
|
+
return non_full_mask_side;
|
21056
|
+
}
|
21057
|
+
|
21058
|
+
break;
|
21059
|
+
case "^":
|
21060
|
+
// {anything} ^ -1 => ~{anything}
|
21061
|
+
if (full_mask) {
|
21062
|
+
return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
|
21063
|
+
}
|
21064
|
+
|
21065
|
+
// ~x ^ ~y => x ^ y
|
21066
|
+
if (
|
21067
|
+
self.left instanceof AST_UnaryPrefix
|
21068
|
+
&& self.left.operator === "~"
|
21069
|
+
&& self.right instanceof AST_UnaryPrefix
|
21070
|
+
&& self.right.operator === "~"
|
21071
|
+
) {
|
21072
|
+
self = make_node(AST_Binary, self, {
|
21073
|
+
operator: "^",
|
21074
|
+
left: self.left.expression,
|
21075
|
+
right: self.right.expression
|
21076
|
+
});
|
21077
|
+
}
|
21078
|
+
|
21079
|
+
break;
|
21080
|
+
}
|
21081
|
+
}
|
20754
21082
|
}
|
20755
21083
|
// x && (y && z) ==> x && y && z
|
20756
21084
|
// x || (y || z) ==> x || y || z
|
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,159 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
2578
2687
|
}
|
2579
2688
|
}
|
2580
2689
|
}
|
2690
|
+
|
2691
|
+
// bitwise ops
|
2692
|
+
if (bitwise_binop.has(self.operator)) {
|
2693
|
+
// Use De Morgan's laws
|
2694
|
+
// z & (X | y)
|
2695
|
+
// => z & X (given y & z === 0)
|
2696
|
+
// => z & X | {y & z} (given y & z !== 0)
|
2697
|
+
let y, z, x_node, y_node, z_node = self.left;
|
2698
|
+
if (
|
2699
|
+
self.operator === "&"
|
2700
|
+
&& self.right instanceof AST_Binary
|
2701
|
+
&& self.right.operator === "|"
|
2702
|
+
&& typeof (z = self.left.evaluate(compressor)) === "number"
|
2703
|
+
) {
|
2704
|
+
if (typeof (y = self.right.right.evaluate(compressor)) === "number") {
|
2705
|
+
// z & (X | y)
|
2706
|
+
x_node = self.right.left;
|
2707
|
+
y_node = self.right.right;
|
2708
|
+
} else if (typeof (y = self.right.left.evaluate(compressor)) === "number") {
|
2709
|
+
// z & (y | X)
|
2710
|
+
x_node = self.right.right;
|
2711
|
+
y_node = self.right.left;
|
2712
|
+
}
|
2713
|
+
|
2714
|
+
if (x_node && y_node) {
|
2715
|
+
if ((y & z) === 0) {
|
2716
|
+
self = make_node(AST_Binary, self, {
|
2717
|
+
operator: self.operator,
|
2718
|
+
left: z_node,
|
2719
|
+
right: x_node
|
2720
|
+
});
|
2721
|
+
} else {
|
2722
|
+
const reordered_ops = make_node(AST_Binary, self, {
|
2723
|
+
operator: "|",
|
2724
|
+
left: make_node(AST_Binary, self, {
|
2725
|
+
operator: "&",
|
2726
|
+
left: x_node,
|
2727
|
+
right: z_node
|
2728
|
+
}),
|
2729
|
+
right: make_node_from_constant(y & z, y_node),
|
2730
|
+
});
|
2731
|
+
|
2732
|
+
self = best_of(compressor, self, reordered_ops);
|
2733
|
+
}
|
2734
|
+
}
|
2735
|
+
}
|
2736
|
+
|
2737
|
+
// x ^ x => 0
|
2738
|
+
// x | x => 0 | x
|
2739
|
+
// x & x => 0 | x
|
2740
|
+
const same_operands = self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor);
|
2741
|
+
if (same_operands) {
|
2742
|
+
if (self.operator === "^") {
|
2743
|
+
return make_node(AST_Number, self, { value: 0 });
|
2744
|
+
}
|
2745
|
+
if (self.operator === "|" || self.operator === "&") {
|
2746
|
+
self.left = make_node(AST_Number, self, { value: 0 });
|
2747
|
+
self.operator = "|";
|
2748
|
+
}
|
2749
|
+
}
|
2750
|
+
|
2751
|
+
|
2752
|
+
// Shifts that do nothing
|
2753
|
+
// {anything} >> 0 => {anything} | 0
|
2754
|
+
// {anything} << 0 => {anything} | 0
|
2755
|
+
if (
|
2756
|
+
(self.operator === "<<" || self.operator === ">>")
|
2757
|
+
&& self.right instanceof AST_Number && self.right.value === 0
|
2758
|
+
) {
|
2759
|
+
self.operator = "|";
|
2760
|
+
}
|
2761
|
+
|
2762
|
+
// Find useless to-bitwise conversions
|
2763
|
+
// {32 bit integer} | 0 => {32 bit integer}
|
2764
|
+
// {32 bit integer} ^ 0 => {32 bit integer}
|
2765
|
+
const zero_side = self.right instanceof AST_Number && self.right.value === 0 ? self.right
|
2766
|
+
: self.left instanceof AST_Number && self.left.value === 0 ? self.left
|
2767
|
+
: null;
|
2768
|
+
const non_zero_side = zero_side && (zero_side === self.right ? self.left : self.right);
|
2769
|
+
if (
|
2770
|
+
zero_side
|
2771
|
+
&& (self.operator === "|" || self.operator === "^")
|
2772
|
+
&& (non_zero_side.is_32_bit_integer() || compressor.in_32_bit_context())
|
2773
|
+
) {
|
2774
|
+
return non_zero_side;
|
2775
|
+
}
|
2776
|
+
|
2777
|
+
// {anything} & 0 => 0
|
2778
|
+
if (
|
2779
|
+
zero_side
|
2780
|
+
&& self.operator === "&"
|
2781
|
+
&& !non_zero_side.has_side_effects(compressor)
|
2782
|
+
) {
|
2783
|
+
return zero_side;
|
2784
|
+
}
|
2785
|
+
|
2786
|
+
const is_full_mask = (node) =>
|
2787
|
+
node instanceof AST_Number && node.value === -1
|
2788
|
+
||
|
2789
|
+
node instanceof AST_UnaryPrefix && (
|
2790
|
+
node.operator === "-"
|
2791
|
+
&& node.expression instanceof AST_Number
|
2792
|
+
&& node.expression.value === 1
|
2793
|
+
|| node.operator === "~"
|
2794
|
+
&& node.expression instanceof AST_Number
|
2795
|
+
&& node.expression.value === 0);
|
2796
|
+
|
2797
|
+
const full_mask = is_full_mask(self.right) ? self.right
|
2798
|
+
: is_full_mask(self.left) ? self.left
|
2799
|
+
: null;
|
2800
|
+
const non_full_mask_side = full_mask && (full_mask === self.right ? self.left : self.right);
|
2801
|
+
|
2802
|
+
switch (self.operator) {
|
2803
|
+
case "|":
|
2804
|
+
// {anything} | -1 => -1
|
2805
|
+
if (full_mask && !non_full_mask_side.has_side_effects(compressor)) {
|
2806
|
+
return full_mask;
|
2807
|
+
}
|
2808
|
+
|
2809
|
+
break;
|
2810
|
+
case "&":
|
2811
|
+
// {32 bit integer} & -1 => {32 bit integer}
|
2812
|
+
if (
|
2813
|
+
full_mask
|
2814
|
+
&& (non_full_mask_side.is_32_bit_integer() || compressor.in_32_bit_context())
|
2815
|
+
) {
|
2816
|
+
return non_full_mask_side;
|
2817
|
+
}
|
2818
|
+
|
2819
|
+
break;
|
2820
|
+
case "^":
|
2821
|
+
// {anything} ^ -1 => ~{anything}
|
2822
|
+
if (full_mask) {
|
2823
|
+
return non_full_mask_side.bitwise_negate(compressor.in_32_bit_context());
|
2824
|
+
}
|
2825
|
+
|
2826
|
+
// ~x ^ ~y => x ^ y
|
2827
|
+
if (
|
2828
|
+
self.left instanceof AST_UnaryPrefix
|
2829
|
+
&& self.left.operator === "~"
|
2830
|
+
&& self.right instanceof AST_UnaryPrefix
|
2831
|
+
&& self.right.operator === "~"
|
2832
|
+
) {
|
2833
|
+
self = make_node(AST_Binary, self, {
|
2834
|
+
operator: "^",
|
2835
|
+
left: self.left.expression,
|
2836
|
+
right: self.right.expression
|
2837
|
+
});
|
2838
|
+
}
|
2839
|
+
|
2840
|
+
break;
|
2841
|
+
}
|
2842
|
+
}
|
2581
2843
|
}
|
2582
2844
|
// x && (y && z) ==> x && y && z
|
2583
2845
|
// x || (y || z) ==> x || y || z
|
@@ -133,6 +133,7 @@ import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } fr
|
|
133
133
|
export const is_undeclared_ref = (node) =>
|
134
134
|
node instanceof AST_SymbolRef && node.definition().undeclared;
|
135
135
|
|
136
|
+
export const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
|
136
137
|
export const lazy_op = makePredicate("&& || ??");
|
137
138
|
export const unary_side_effects = makePredicate("delete ++ --");
|
138
139
|
|
@@ -193,6 +194,24 @@ export const unary_side_effects = makePredicate("delete ++ --");
|
|
193
194
|
node.DEFMETHOD("is_number", func);
|
194
195
|
});
|
195
196
|
|
197
|
+
// methods to determine if an expression is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size
|
198
|
+
(function(def_is_32_bit_integer) {
|
199
|
+
def_is_32_bit_integer(AST_Node, return_false);
|
200
|
+
def_is_32_bit_integer(AST_Number, function() {
|
201
|
+
return this.value === (this.value | 0);
|
202
|
+
});
|
203
|
+
def_is_32_bit_integer(AST_UnaryPrefix, function() {
|
204
|
+
return this.operator == "~" ? this.expression.is_number()
|
205
|
+
: this.operator === "+" ? this.expression.is_32_bit_integer()
|
206
|
+
: false;
|
207
|
+
});
|
208
|
+
def_is_32_bit_integer(AST_Binary, function() {
|
209
|
+
return bitwise_binop.has(this.operator);
|
210
|
+
});
|
211
|
+
}(function (node, func) {
|
212
|
+
node.DEFMETHOD("is_32_bit_integer", func);
|
213
|
+
}));
|
214
|
+
|
196
215
|
// methods to determine if an expression has a string result type
|
197
216
|
(function(def_is_string) {
|
198
217
|
def_is_string(AST_Node, return_false);
|
@@ -807,6 +826,37 @@ export function is_lhs(node, parent) {
|
|
807
826
|
});
|
808
827
|
});
|
809
828
|
|
829
|
+
(function (def_bitwise_negate) {
|
830
|
+
function basic_negation(exp) {
|
831
|
+
return make_node(AST_UnaryPrefix, exp, {
|
832
|
+
operator: "~",
|
833
|
+
expression: exp
|
834
|
+
});
|
835
|
+
}
|
836
|
+
|
837
|
+
def_bitwise_negate(AST_Node, function() {
|
838
|
+
return basic_negation(this);
|
839
|
+
});
|
840
|
+
|
841
|
+
def_bitwise_negate(AST_Number, function() {
|
842
|
+
const neg = ~this.value;
|
843
|
+
if (neg.toString().length > this.value.toString().length) {
|
844
|
+
return basic_negation(this);
|
845
|
+
}
|
846
|
+
return make_node(AST_Number, this, { value: neg });
|
847
|
+
});
|
848
|
+
|
849
|
+
def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
|
850
|
+
if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
|
851
|
+
return this.expression;
|
852
|
+
} else {
|
853
|
+
return basic_negation(this);
|
854
|
+
}
|
855
|
+
});
|
856
|
+
})(function (node, func) {
|
857
|
+
node.DEFMETHOD("bitwise_negate", func);
|
858
|
+
});
|
859
|
+
|
810
860
|
// Is the callee of this function pure?
|
811
861
|
var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
|
812
862
|
AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
|
package/lib/mozilla-ast.js
CHANGED
@@ -618,9 +618,7 @@ import { is_basic_identifier_string } from "./parse.js";
|
|
618
618
|
start: my_start_token(M),
|
619
619
|
end: my_end_token(M),
|
620
620
|
exported_definition: from_moz(M.declaration),
|
621
|
-
exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(
|
622
|
-
return from_moz(specifier);
|
623
|
-
}) : null,
|
621
|
+
exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(from_moz) : null,
|
624
622
|
module_name: from_moz(M.source),
|
625
623
|
assert_clause: assert_clause_from_moz(M.assertions)
|
626
624
|
});
|
package/lib/size.js
CHANGED
@@ -406,8 +406,8 @@ AST_Class.prototype._size = function () {
|
|
406
406
|
};
|
407
407
|
|
408
408
|
AST_ClassStaticBlock.prototype._size = function () {
|
409
|
-
// "
|
410
|
-
return
|
409
|
+
// "static{}" + semicolons
|
410
|
+
return 8 + list_overhead(this.body);
|
411
411
|
};
|
412
412
|
|
413
413
|
AST_ClassProperty.prototype._size = function () {
|