terser 5.27.1 → 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 +7 -0
- package/dist/bundle.min.js +350 -14
- package/lib/compress/common.js +3 -0
- package/lib/compress/drop-unused.js +14 -5
- 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
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v5.28.0
|
4
|
+
- Optimise redundant or shrinkable bitwise operations (`|`, `^`, `&`, `>>`, `<<`)
|
5
|
+
- Evaluate some `BigInt` math operations
|
6
|
+
|
7
|
+
## v5.27.2
|
8
|
+
- Recognise `this` as a reference to the surrounding class in `drop_unused`. Closes #1472
|
9
|
+
|
3
10
|
## v5.27.1
|
4
11
|
- Fixed case where `collapse_vars` inlines `await` expressions into non-async functions.
|
5
12
|
|
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
|
}
|
@@ -15137,6 +15206,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
15137
15206
|
return node.expression;
|
15138
15207
|
}
|
15139
15208
|
};
|
15209
|
+
var this_def = null;
|
15140
15210
|
var in_use_ids = new Map();
|
15141
15211
|
var fixed_ids = new Map();
|
15142
15212
|
if (self instanceof AST_Toplevel && compressor.top_retain) {
|
@@ -15148,6 +15218,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
15148
15218
|
}
|
15149
15219
|
var var_defs_by_id = new Map();
|
15150
15220
|
var initializations = new Map();
|
15221
|
+
|
15151
15222
|
// pass 1: find out which symbols are directly used in
|
15152
15223
|
// this scope (not in nested scopes).
|
15153
15224
|
var scope = this;
|
@@ -15160,11 +15231,6 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
15160
15231
|
});
|
15161
15232
|
}
|
15162
15233
|
if (node === self) return;
|
15163
|
-
if (node instanceof AST_Class) {
|
15164
|
-
if (node.has_side_effects(compressor)) {
|
15165
|
-
node.visit_nondeferred_class_parts(tw);
|
15166
|
-
}
|
15167
|
-
}
|
15168
15234
|
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
15169
15235
|
var node_def = node.name.definition();
|
15170
15236
|
const in_export = tw.parent() instanceof AST_Export;
|
@@ -15174,11 +15240,22 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
15174
15240
|
}
|
15175
15241
|
}
|
15176
15242
|
|
15243
|
+
if (node instanceof AST_DefClass && node.has_side_effects(compressor)) {
|
15244
|
+
const save_this_def = this_def;
|
15245
|
+
this_def = node_def;
|
15246
|
+
node.visit_nondeferred_class_parts(tw);
|
15247
|
+
this_def = save_this_def;
|
15248
|
+
}
|
15249
|
+
|
15177
15250
|
map_add(initializations, node_def.id, node);
|
15178
15251
|
return true; // don't go in nested scopes
|
15179
15252
|
}
|
15180
15253
|
// In the root scope, we drop things. In inner scopes, we just check for uses.
|
15181
15254
|
const in_root_scope = scope === self;
|
15255
|
+
if (node instanceof AST_This && this_def && in_root_scope) {
|
15256
|
+
in_use_ids.set(this_def.id, this_def);
|
15257
|
+
return true;
|
15258
|
+
}
|
15182
15259
|
if (node instanceof AST_SymbolFunarg && in_root_scope) {
|
15183
15260
|
map_add(var_defs_by_id, node.definition().id, node);
|
15184
15261
|
}
|
@@ -18538,6 +18615,33 @@ class Compressor extends TreeWalker {
|
|
18538
18615
|
}
|
18539
18616
|
}
|
18540
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
|
+
|
18541
18645
|
get_toplevel() {
|
18542
18646
|
return this._toplevel;
|
18543
18647
|
}
|
@@ -20236,9 +20340,40 @@ def_optimize(AST_UnaryPrefix, function(self, compressor) {
|
|
20236
20340
|
right: e.right
|
20237
20341
|
});
|
20238
20342
|
}
|
20239
|
-
|
20240
|
-
if (
|
20241
|
-
|
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
|
+
) {
|
20242
20377
|
var ev = self.evaluate(compressor);
|
20243
20378
|
if (ev !== self) {
|
20244
20379
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
@@ -20329,6 +20464,7 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20329
20464
|
self.left.equivalent_to(self.right)) {
|
20330
20465
|
self.operator = self.operator.substr(0, 2);
|
20331
20466
|
}
|
20467
|
+
|
20332
20468
|
// XXX: intentionally falling down to the next case
|
20333
20469
|
case "==":
|
20334
20470
|
case "!=":
|
@@ -20370,6 +20506,55 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20370
20506
|
&& self.left.definition() === self.right.definition()
|
20371
20507
|
&& is_object(self.left.fixed_value())) {
|
20372
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
|
+
}
|
20373
20558
|
}
|
20374
20559
|
break;
|
20375
20560
|
case "&&":
|
@@ -20743,6 +20928,157 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
20743
20928
|
}
|
20744
20929
|
}
|
20745
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
|
+
}
|
20746
21082
|
}
|
20747
21083
|
// x && (y && z) ==> x && y && z
|
20748
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":
|
@@ -70,6 +70,7 @@ import {
|
|
70
70
|
AST_SymbolFunarg,
|
71
71
|
AST_SymbolRef,
|
72
72
|
AST_SymbolVar,
|
73
|
+
AST_This,
|
73
74
|
AST_Toplevel,
|
74
75
|
AST_Unary,
|
75
76
|
AST_Var,
|
@@ -126,6 +127,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
126
127
|
return node.expression;
|
127
128
|
}
|
128
129
|
};
|
130
|
+
var this_def = null;
|
129
131
|
var in_use_ids = new Map();
|
130
132
|
var fixed_ids = new Map();
|
131
133
|
if (self instanceof AST_Toplevel && compressor.top_retain) {
|
@@ -137,6 +139,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
137
139
|
}
|
138
140
|
var var_defs_by_id = new Map();
|
139
141
|
var initializations = new Map();
|
142
|
+
|
140
143
|
// pass 1: find out which symbols are directly used in
|
141
144
|
// this scope (not in nested scopes).
|
142
145
|
var scope = this;
|
@@ -149,11 +152,6 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
149
152
|
});
|
150
153
|
}
|
151
154
|
if (node === self) return;
|
152
|
-
if (node instanceof AST_Class) {
|
153
|
-
if (node.has_side_effects(compressor)) {
|
154
|
-
node.visit_nondeferred_class_parts(tw);
|
155
|
-
}
|
156
|
-
}
|
157
155
|
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
158
156
|
var node_def = node.name.definition();
|
159
157
|
const in_export = tw.parent() instanceof AST_Export;
|
@@ -163,11 +161,22 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
163
161
|
}
|
164
162
|
}
|
165
163
|
|
164
|
+
if (node instanceof AST_DefClass && node.has_side_effects(compressor)) {
|
165
|
+
const save_this_def = this_def;
|
166
|
+
this_def = node_def;
|
167
|
+
node.visit_nondeferred_class_parts(tw);
|
168
|
+
this_def = save_this_def;
|
169
|
+
}
|
170
|
+
|
166
171
|
map_add(initializations, node_def.id, node);
|
167
172
|
return true; // don't go in nested scopes
|
168
173
|
}
|
169
174
|
// In the root scope, we drop things. In inner scopes, we just check for uses.
|
170
175
|
const in_root_scope = scope === self;
|
176
|
+
if (node instanceof AST_This && this_def && in_root_scope) {
|
177
|
+
in_use_ids.set(this_def.id, this_def);
|
178
|
+
return true;
|
179
|
+
}
|
171
180
|
if (node instanceof AST_SymbolFunarg && in_root_scope) {
|
172
181
|
map_add(var_defs_by_id, node.definition().id, node);
|
173
182
|
}
|
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 () {
|