support BigInt literals (#4583)
This commit is contained in:
17
lib/ast.js
17
lib/ast.js
@@ -249,7 +249,7 @@ var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_s
|
|||||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||||
functions: "[Object/S] like `variables`, but only lists function declarations",
|
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||||
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
||||||
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
variables: "[Object/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",
|
||||||
},
|
},
|
||||||
clone: function(deep) {
|
clone: function(deep) {
|
||||||
var node = this._clone(deep);
|
var node = this._clone(deep);
|
||||||
@@ -472,7 +472,7 @@ var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
|
|||||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||||
$documentation: "The toplevel scope",
|
$documentation: "The toplevel scope",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
globals: "[Object/S] a map of name ---> SymbolDef for all undeclared names",
|
||||||
},
|
},
|
||||||
wrap: function(name) {
|
wrap: function(name) {
|
||||||
var body = this.body;
|
var body = this.body;
|
||||||
@@ -1440,6 +1440,19 @@ var AST_Number = DEFNODE("Number", "value", {
|
|||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (typeof this.value != "number") throw new Error("value must be number");
|
if (typeof this.value != "number") throw new Error("value must be number");
|
||||||
|
if (!isFinite(this.value)) throw new Error("value must be finite");
|
||||||
|
if (this.value < 0) throw new Error("value cannot be negative");
|
||||||
|
},
|
||||||
|
}, AST_Constant);
|
||||||
|
|
||||||
|
var AST_BigInt = DEFNODE("BigInt", "value", {
|
||||||
|
$documentation: "A BigInt literal",
|
||||||
|
$propdoc: {
|
||||||
|
value: "[string] the numeric representation",
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (typeof this.value != "string") throw new Error("value must be string");
|
||||||
|
if (this.value[0] == "-") throw new Error("value cannot be negative");
|
||||||
},
|
},
|
||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
|
|||||||
292
lib/compress.js
292
lib/compress.js
@@ -729,7 +729,7 @@ merge(Compressor.prototype, {
|
|||||||
if (aborts) push(tw);
|
if (aborts) push(tw);
|
||||||
reset_variables(tw, compressor, fn);
|
reset_variables(tw, compressor, fn);
|
||||||
// Virtually turn IIFE parameters into variable definitions:
|
// Virtually turn IIFE parameters into variable definitions:
|
||||||
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
|
// (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
|
||||||
// So existing transformation rules can work on them.
|
// So existing transformation rules can work on them.
|
||||||
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
||||||
fn.argnames.forEach(function(arg, i) {
|
fn.argnames.forEach(function(arg, i) {
|
||||||
@@ -2742,7 +2742,7 @@ merge(Compressor.prototype, {
|
|||||||
var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
|
var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
|
||||||
//---
|
//---
|
||||||
// pretty silly case, but:
|
// pretty silly case, but:
|
||||||
// if (foo()) return; return; => foo(); return;
|
// if (foo()) return; return; ---> foo(); return;
|
||||||
if (!value && !stat.alternative
|
if (!value && !stat.alternative
|
||||||
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
|
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
@@ -2752,7 +2752,7 @@ merge(Compressor.prototype, {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//---
|
//---
|
||||||
// if (foo()) return x; return y; => return foo() ? x : y;
|
// if (foo()) return x; return y; ---> return foo() ? x : y;
|
||||||
if (!stat.alternative && next instanceof AST_Return) {
|
if (!stat.alternative && next instanceof AST_Return) {
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
@@ -2762,7 +2762,7 @@ merge(Compressor.prototype, {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//---
|
//---
|
||||||
// if (foo()) return x; [ return ; ] => return foo() ? x : undefined;
|
// if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
|
||||||
if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
|
if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
@@ -2773,7 +2773,7 @@ merge(Compressor.prototype, {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//---
|
//---
|
||||||
// if (a) return b; if (c) return d; e; => return a ? b : c ? d : void e;
|
// if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
|
||||||
//
|
//
|
||||||
// if sequences is not enabled, this can lead to an endless loop (issue #866).
|
// if sequences is not enabled, this can lead to an endless loop (issue #866).
|
||||||
// however, with sequences on this helps producing slightly better output for
|
// however, with sequences on this helps producing slightly better output for
|
||||||
@@ -3970,6 +3970,7 @@ merge(Compressor.prototype, {
|
|||||||
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
|
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
|
||||||
});
|
});
|
||||||
def(AST_Accessor, return_this);
|
def(AST_Accessor, return_this);
|
||||||
|
def(AST_BigInt, return_this);
|
||||||
def(AST_Node, return_this);
|
def(AST_Node, return_this);
|
||||||
def(AST_Constant, function() {
|
def(AST_Constant, function() {
|
||||||
return this.value;
|
return this.value;
|
||||||
@@ -4172,7 +4173,7 @@ merge(Compressor.prototype, {
|
|||||||
&& typeof result == "number"
|
&& typeof result == "number"
|
||||||
&& (this.operator == "+" || this.operator == "-")) {
|
&& (this.operator == "+" || this.operator == "-")) {
|
||||||
var digits = Math.max(0, decimals(left), decimals(right));
|
var digits = Math.max(0, decimals(left), decimals(right));
|
||||||
// 53-bit significand => 15.95 decimal places
|
// 53-bit significand ---> 15.95 decimal places
|
||||||
if (digits < 16) return +result.toFixed(digits);
|
if (digits < 16) return +result.toFixed(digits);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -7944,6 +7945,7 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
if (is_undeclared_ref(exp)) switch (exp.name) {
|
if (is_undeclared_ref(exp)) switch (exp.name) {
|
||||||
case "Array":
|
case "Array":
|
||||||
|
// Array(n) ---> [ , , ... , ]
|
||||||
if (self.args.length == 1) {
|
if (self.args.length == 1) {
|
||||||
var first = self.args[0];
|
var first = self.args[0];
|
||||||
if (first instanceof AST_Number) try {
|
if (first instanceof AST_Number) try {
|
||||||
@@ -7951,9 +7953,7 @@ merge(Compressor.prototype, {
|
|||||||
if (length > 6) break;
|
if (length > 6) break;
|
||||||
var elements = Array(length);
|
var elements = Array(length);
|
||||||
for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
|
for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
|
||||||
return make_node(AST_Array, self, {
|
return make_node(AST_Array, self, { elements: elements });
|
||||||
elements: elements
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
|
AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
|
||||||
length: length,
|
length: length,
|
||||||
@@ -7965,81 +7965,90 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
|
if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
|
||||||
}
|
}
|
||||||
return make_node(AST_Array, self, {
|
// Array(...) ---> [ ... ]
|
||||||
elements: self.args
|
return make_node(AST_Array, self, { elements: self.args });
|
||||||
});
|
|
||||||
case "Object":
|
case "Object":
|
||||||
if (self.args.length == 0) {
|
// Object() ---> {}
|
||||||
return make_node(AST_Object, self, {
|
if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
|
||||||
properties: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "String":
|
case "String":
|
||||||
if (self.args.length == 0) return make_node(AST_String, self, {
|
// String() ---> ""
|
||||||
value: ""
|
if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
|
||||||
});
|
// String(x) ---> "" + x
|
||||||
if (self.args.length <= 1) return make_node(AST_Binary, self, {
|
if (self.args.length == 1) return make_node(AST_Binary, self, {
|
||||||
left: self.args[0],
|
|
||||||
operator: "+",
|
operator: "+",
|
||||||
right: make_node(AST_String, self, { value: "" })
|
left: make_node(AST_String, self, { value: "" }),
|
||||||
|
right: self.args[0],
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
break;
|
break;
|
||||||
case "Number":
|
case "Number":
|
||||||
if (self.args.length == 0) return make_node(AST_Number, self, {
|
// Number() ---> 0
|
||||||
value: 0
|
if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
|
||||||
});
|
// Number(x) ---> +("" + x)
|
||||||
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
||||||
expression: self.args[0],
|
operator: "+",
|
||||||
operator: "+"
|
expression: make_node(AST_Binary, self, {
|
||||||
}).optimize(compressor);
|
operator: "+",
|
||||||
case "Boolean":
|
left: make_node(AST_String, self, { value: "" }),
|
||||||
if (self.args.length == 0) return make_node(AST_False, self);
|
right: self.args[0],
|
||||||
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
}),
|
||||||
expression: make_node(AST_UnaryPrefix, self, {
|
}).optimize(compressor);
|
||||||
expression: self.args[0],
|
break;
|
||||||
operator: "!"
|
case "Boolean":
|
||||||
|
// Boolean() ---> false
|
||||||
|
if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
|
||||||
|
// Boolean(x) ---> !!x
|
||||||
|
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
||||||
|
operator: "!",
|
||||||
|
expression: make_node(AST_UnaryPrefix, self, {
|
||||||
|
operator: "!",
|
||||||
|
expression: self.args[0],
|
||||||
}),
|
}),
|
||||||
operator: "!"
|
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
break;
|
break;
|
||||||
case "RegExp":
|
case "RegExp":
|
||||||
|
// attempt to convert RegExp(...) to literal
|
||||||
var params = [];
|
var params = [];
|
||||||
if (all(self.args, function(arg) {
|
if (all(self.args, function(arg) {
|
||||||
var value = arg.evaluate(compressor);
|
var value = arg.evaluate(compressor);
|
||||||
params.unshift(value);
|
params.unshift(value);
|
||||||
return arg !== value;
|
return arg !== value;
|
||||||
})) {
|
})) try {
|
||||||
try {
|
return best_of(compressor, self, make_node(AST_RegExp, self, {
|
||||||
return best_of(compressor, self, make_node(AST_RegExp, self, {
|
value: RegExp.apply(RegExp, params),
|
||||||
value: RegExp.apply(RegExp, params),
|
}));
|
||||||
}));
|
} catch (ex) {
|
||||||
} catch (ex) {
|
AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
|
||||||
AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
|
expr: self,
|
||||||
expr: self,
|
file: self.start.file,
|
||||||
file: self.start.file,
|
line: self.start.line,
|
||||||
line: self.start.line,
|
col: self.start.col,
|
||||||
col: self.start.col,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else if (exp instanceof AST_Dot) switch(exp.property) {
|
} else if (exp instanceof AST_Dot) switch (exp.property) {
|
||||||
case "toString":
|
case "toString":
|
||||||
|
// x.toString() ---> "" + x
|
||||||
if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) {
|
if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) {
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
left: make_node(AST_String, self, { value: "" }),
|
|
||||||
operator: "+",
|
operator: "+",
|
||||||
right: exp.expression
|
left: make_node(AST_String, self, { value: "" }),
|
||||||
|
right: exp.expression,
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "join":
|
case "join":
|
||||||
if (exp.expression instanceof AST_Array) EXIT: {
|
if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
|
||||||
var separator;
|
var separator = self.args[0];
|
||||||
if (self.args.length > 0) {
|
// [].join() ---> ""
|
||||||
separator = self.args[0].evaluate(compressor);
|
// [].join(x) ---> (x, "")
|
||||||
if (separator === self.args[0]) break EXIT; // not a constant
|
if (exp.expression.elements.length == 0) return separator ? make_sequence(self, [
|
||||||
|
separator,
|
||||||
|
make_node(AST_String, self, { value: "" }),
|
||||||
|
]).optimize(compressor) : make_node(AST_String, self, { value: "" });
|
||||||
|
if (separator) {
|
||||||
|
separator = separator.evaluate(compressor);
|
||||||
|
if (separator instanceof AST_Node) break EXIT; // not a constant
|
||||||
}
|
}
|
||||||
var elements = [];
|
var elements = [];
|
||||||
var consts = [];
|
var consts = [];
|
||||||
@@ -8050,45 +8059,46 @@ merge(Compressor.prototype, {
|
|||||||
} else {
|
} else {
|
||||||
if (consts.length > 0) {
|
if (consts.length > 0) {
|
||||||
elements.push(make_node(AST_String, self, {
|
elements.push(make_node(AST_String, self, {
|
||||||
value: consts.join(separator)
|
value: consts.join(separator),
|
||||||
}));
|
}));
|
||||||
consts.length = 0;
|
consts.length = 0;
|
||||||
}
|
}
|
||||||
elements.push(el);
|
elements.push(el);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (consts.length > 0) {
|
if (consts.length > 0) elements.push(make_node(AST_String, self, {
|
||||||
elements.push(make_node(AST_String, self, {
|
value: consts.join(separator),
|
||||||
value: consts.join(separator)
|
}));
|
||||||
}));
|
// [ x ].join() ---> "" + x
|
||||||
}
|
// [ x ].join(".") ---> "" + x
|
||||||
if (elements.length == 0) return make_node(AST_String, self, { value: "" });
|
// [ 1, 2, 3 ].join() ---> "1,2,3"
|
||||||
|
// [ 1, 2, 3 ].join(".") ---> "1.2.3"
|
||||||
if (elements.length == 1) {
|
if (elements.length == 1) {
|
||||||
if (elements[0].is_string(compressor)) {
|
if (elements[0].is_string(compressor)) return elements[0];
|
||||||
return elements[0];
|
|
||||||
}
|
|
||||||
return make_node(AST_Binary, elements[0], {
|
return make_node(AST_Binary, elements[0], {
|
||||||
operator : "+",
|
operator: "+",
|
||||||
left : make_node(AST_String, self, { value: "" }),
|
left: make_node(AST_String, self, { value: "" }),
|
||||||
right : elements[0]
|
right: elements[0],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
|
||||||
if (separator == "") {
|
if (separator == "") {
|
||||||
var first;
|
var first;
|
||||||
if (elements[0].is_string(compressor)
|
if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
|
||||||
|| elements[1].is_string(compressor)) {
|
|
||||||
first = elements.shift();
|
first = elements.shift();
|
||||||
} else {
|
} else {
|
||||||
first = make_node(AST_String, self, { value: "" });
|
first = make_node(AST_String, self, { value: "" });
|
||||||
}
|
}
|
||||||
return elements.reduce(function(prev, el) {
|
return elements.reduce(function(prev, el) {
|
||||||
return make_node(AST_Binary, el, {
|
return make_node(AST_Binary, el, {
|
||||||
operator : "+",
|
operator: "+",
|
||||||
left : prev,
|
left: prev,
|
||||||
right : el
|
right: el,
|
||||||
});
|
});
|
||||||
}, first).optimize(compressor);
|
}, first).optimize(compressor);
|
||||||
}
|
}
|
||||||
|
// [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
|
||||||
|
// [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
|
||||||
// need this awkward cloning to not affect original element
|
// need this awkward cloning to not affect original element
|
||||||
// best_of will decide which one to get through.
|
// best_of will decide which one to get through.
|
||||||
var node = self.clone();
|
var node = self.clone();
|
||||||
@@ -8152,7 +8162,7 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("unsafe_Function")
|
if (compressor.option("unsafe_Function")
|
||||||
&& is_undeclared_ref(exp)
|
&& is_undeclared_ref(exp)
|
||||||
&& exp.name == "Function") {
|
&& exp.name == "Function") {
|
||||||
// new Function() => function(){}
|
// new Function() ---> function(){}
|
||||||
if (self.args.length == 0) return make_node(AST_Function, self, {
|
if (self.args.length == 0) return make_node(AST_Function, self, {
|
||||||
argnames: [],
|
argnames: [],
|
||||||
body: []
|
body: []
|
||||||
@@ -8780,8 +8790,8 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
// (a = b, x && a = c) => a = x ? c : b
|
// (a = b, x && a = c) ---> a = x ? c : b
|
||||||
// (a = b, x || a = c) => a = x ? b : c
|
// (a = b, x || a = c) ---> a = x ? b : c
|
||||||
function to_conditional_assignment(compressor, def, value, node) {
|
function to_conditional_assignment(compressor, def, value, node) {
|
||||||
if (!(node instanceof AST_Binary)) return;
|
if (!(node instanceof AST_Binary)) return;
|
||||||
if (!lazy_op[node.operator]) return;
|
if (!lazy_op[node.operator]) return;
|
||||||
@@ -8912,7 +8922,7 @@ merge(Compressor.prototype, {
|
|||||||
} else if (compressor.in_boolean_context()) switch (op) {
|
} else if (compressor.in_boolean_context()) switch (op) {
|
||||||
case "!":
|
case "!":
|
||||||
if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
|
if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
|
||||||
// !!foo => foo, if we're in boolean context
|
// !!foo ---> foo, if we're in boolean context
|
||||||
return exp.expression;
|
return exp.expression;
|
||||||
}
|
}
|
||||||
if (exp instanceof AST_Binary) {
|
if (exp instanceof AST_Binary) {
|
||||||
@@ -9077,8 +9087,8 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (compressor.option("assignments") && lazy_op[self.operator]) {
|
if (compressor.option("assignments") && lazy_op[self.operator]) {
|
||||||
var assign = self.right;
|
var assign = self.right;
|
||||||
// a || (a = x) => a = a || x
|
// a || (a = x) ---> a = a || x
|
||||||
// a && (a = x) => a = a && x
|
// a && (a = x) ---> a = a && x
|
||||||
if (self.left instanceof AST_SymbolRef
|
if (self.left instanceof AST_SymbolRef
|
||||||
&& assign instanceof AST_Assign
|
&& assign instanceof AST_Assign
|
||||||
&& assign.operator == "="
|
&& assign.operator == "="
|
||||||
@@ -9108,11 +9118,11 @@ merge(Compressor.prototype, {
|
|||||||
// XXX: intentionally falling down to the next case
|
// XXX: intentionally falling down to the next case
|
||||||
case "==":
|
case "==":
|
||||||
case "!=":
|
case "!=":
|
||||||
// void 0 == x => null == x
|
// void 0 == x ---> null == x
|
||||||
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
|
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
|
||||||
self.left = make_node(AST_Null, self.left);
|
self.left = make_node(AST_Null, self.left);
|
||||||
}
|
}
|
||||||
// "undefined" == typeof x => undefined === x
|
// "undefined" == typeof x ---> undefined === x
|
||||||
else if (compressor.option("typeofs")
|
else if (compressor.option("typeofs")
|
||||||
&& self.left instanceof AST_String
|
&& self.left instanceof AST_String
|
||||||
&& self.left.value == "undefined"
|
&& self.left.value == "undefined"
|
||||||
@@ -9126,7 +9136,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.operator.length == 2) self.operator += "=";
|
if (self.operator.length == 2) self.operator += "=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// obj !== obj => false
|
// obj !== obj ---> false
|
||||||
else if (self.left instanceof AST_SymbolRef
|
else if (self.left instanceof AST_SymbolRef
|
||||||
&& self.right instanceof AST_SymbolRef
|
&& self.right instanceof AST_SymbolRef
|
||||||
&& self.left.definition() === self.right.definition()
|
&& self.left.definition() === self.right.definition()
|
||||||
@@ -9136,8 +9146,8 @@ merge(Compressor.prototype, {
|
|||||||
break;
|
break;
|
||||||
case "&&":
|
case "&&":
|
||||||
case "||":
|
case "||":
|
||||||
// void 0 !== x && null !== x => null != x
|
// void 0 !== x && null !== x ---> null != x
|
||||||
// void 0 === x || null === x => null == x
|
// void 0 === x || null === x ---> null == x
|
||||||
var lhs = self.left;
|
var lhs = self.left;
|
||||||
if (lhs.operator == self.operator) {
|
if (lhs.operator == self.operator) {
|
||||||
lhs = lhs.right;
|
lhs = lhs.right;
|
||||||
@@ -9214,8 +9224,8 @@ merge(Compressor.prototype, {
|
|||||||
case ">=": reverse("<="); break;
|
case ">=": reverse("<="); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// x && (y && z) => x && y && z
|
// x && (y && z) ---> x && y && z
|
||||||
// x || (y || z) => x || y || z
|
// x || (y || z) ---> x || y || z
|
||||||
if (compressor.option("conditionals")
|
if (compressor.option("conditionals")
|
||||||
&& lazy_op[self.operator]
|
&& lazy_op[self.operator]
|
||||||
&& self.right instanceof AST_Binary
|
&& self.right instanceof AST_Binary
|
||||||
@@ -9223,19 +9233,19 @@ merge(Compressor.prototype, {
|
|||||||
swap_chain();
|
swap_chain();
|
||||||
}
|
}
|
||||||
if (compressor.option("strings") && self.operator == "+") {
|
if (compressor.option("strings") && self.operator == "+") {
|
||||||
// "foo" + 42 + "" => "foo" + 42
|
// "foo" + 42 + "" ---> "foo" + 42
|
||||||
if (self.right instanceof AST_String
|
if (self.right instanceof AST_String
|
||||||
&& self.right.value == ""
|
&& self.right.value == ""
|
||||||
&& self.left.is_string(compressor)) {
|
&& self.left.is_string(compressor)) {
|
||||||
return self.left.optimize(compressor);
|
return self.left.optimize(compressor);
|
||||||
}
|
}
|
||||||
// "" + ("foo" + 42) => "foo" + 42
|
// "" + ("foo" + 42) ---> "foo" + 42
|
||||||
if (self.left instanceof AST_String
|
if (self.left instanceof AST_String
|
||||||
&& self.left.value == ""
|
&& self.left.value == ""
|
||||||
&& self.right.is_string(compressor)) {
|
&& self.right.is_string(compressor)) {
|
||||||
return self.right.optimize(compressor);
|
return self.right.optimize(compressor);
|
||||||
}
|
}
|
||||||
// "" + 42 + "foo" => 42 + "foo"
|
// "" + 42 + "foo" ---> 42 + "foo"
|
||||||
if (self.left instanceof AST_Binary
|
if (self.left instanceof AST_Binary
|
||||||
&& self.left.operator == "+"
|
&& self.left.operator == "+"
|
||||||
&& self.left.left instanceof AST_String
|
&& self.left.left instanceof AST_String
|
||||||
@@ -9244,8 +9254,8 @@ merge(Compressor.prototype, {
|
|||||||
self.left = self.left.right;
|
self.left = self.left.right;
|
||||||
return self.optimize(compressor);
|
return self.optimize(compressor);
|
||||||
}
|
}
|
||||||
// "x" + (y + "z") => "x" + y + "z"
|
// "x" + (y + "z") ---> "x" + y + "z"
|
||||||
// x + ("y" + z) => x + "y" + z
|
// x + ("y" + z) ---> x + "y" + z
|
||||||
if (self.right instanceof AST_Binary
|
if (self.right instanceof AST_Binary
|
||||||
&& self.operator == self.right.operator
|
&& self.operator == self.right.operator
|
||||||
&& (self.left.is_string(compressor) && self.right.is_string(compressor)
|
&& (self.left.is_string(compressor) && self.right.is_string(compressor)
|
||||||
@@ -9281,7 +9291,7 @@ merge(Compressor.prototype, {
|
|||||||
return self.left.optimize(compressor);
|
return self.left.optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// (x || false) && y => x ? y : false
|
// (x || false) && y ---> x ? y : false
|
||||||
if (self.left.operator == "||") {
|
if (self.left.operator == "||") {
|
||||||
var lr = self.left.right.evaluate(compressor, true);
|
var lr = self.left.right.evaluate(compressor, true);
|
||||||
if (!lr) return make_node(AST_Conditional, self, {
|
if (!lr) return make_node(AST_Conditional, self, {
|
||||||
@@ -9315,7 +9325,7 @@ merge(Compressor.prototype, {
|
|||||||
]).optimize(compressor);
|
]).optimize(compressor);
|
||||||
} else self.truthy = true;
|
} else self.truthy = true;
|
||||||
}
|
}
|
||||||
// x && true || y => x ? true : y
|
// x && true || y ---> x ? true : y
|
||||||
if (self.left.operator == "&&") {
|
if (self.left.operator == "&&") {
|
||||||
var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true);
|
var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true);
|
||||||
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
|
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
|
||||||
@@ -9326,7 +9336,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "+":
|
case "+":
|
||||||
// "foo" + ("bar" + x) => "foobar" + x
|
// "foo" + ("bar" + x) ---> "foobar" + x
|
||||||
if (self.left instanceof AST_Constant
|
if (self.left instanceof AST_Constant
|
||||||
&& self.right instanceof AST_Binary
|
&& self.right instanceof AST_Binary
|
||||||
&& self.right.operator == "+"
|
&& self.right.operator == "+"
|
||||||
@@ -9342,7 +9352,7 @@ merge(Compressor.prototype, {
|
|||||||
right: self.right.right
|
right: self.right.right
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// (x + "foo") + "bar" => x + "foobar"
|
// (x + "foo") + "bar" ---> x + "foobar"
|
||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
&& self.left instanceof AST_Binary
|
&& self.left instanceof AST_Binary
|
||||||
&& self.left.operator == "+"
|
&& self.left.operator == "+"
|
||||||
@@ -9358,7 +9368,7 @@ merge(Compressor.prototype, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// a + -b => a - b
|
// a + -b ---> a - b
|
||||||
if (self.right instanceof AST_UnaryPrefix
|
if (self.right instanceof AST_UnaryPrefix
|
||||||
&& self.right.operator == "-"
|
&& self.right.operator == "-"
|
||||||
&& self.left.is_number(compressor)) {
|
&& self.left.is_number(compressor)) {
|
||||||
@@ -9369,7 +9379,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// -a + b => b - a
|
// -a + b ---> b - a
|
||||||
if (self.left instanceof AST_UnaryPrefix
|
if (self.left instanceof AST_UnaryPrefix
|
||||||
&& self.left.operator == "-"
|
&& self.left.operator == "-"
|
||||||
&& reversible()
|
&& reversible()
|
||||||
@@ -9381,7 +9391,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// (a + b) + 3 => 3 + (a + b)
|
// (a + b) + 3 ---> 3 + (a + b)
|
||||||
if (compressor.option("unsafe_math")
|
if (compressor.option("unsafe_math")
|
||||||
&& self.left instanceof AST_Binary
|
&& self.left instanceof AST_Binary
|
||||||
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
|
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
|
||||||
@@ -9402,7 +9412,7 @@ merge(Compressor.prototype, {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "-":
|
case "-":
|
||||||
// a - -b => a + b
|
// a - -b ---> a + b
|
||||||
if (self.right instanceof AST_UnaryPrefix
|
if (self.right instanceof AST_UnaryPrefix
|
||||||
&& self.right.operator == "-"
|
&& self.right.operator == "-"
|
||||||
&& self.left.is_number(compressor)
|
&& self.left.is_number(compressor)
|
||||||
@@ -9417,8 +9427,8 @@ merge(Compressor.prototype, {
|
|||||||
case "*":
|
case "*":
|
||||||
case "/":
|
case "/":
|
||||||
associative = compressor.option("unsafe_math");
|
associative = compressor.option("unsafe_math");
|
||||||
// +a - b => a - b
|
// +a - b ---> a - b
|
||||||
// a - +b => a - b
|
// a - +b ---> a - b
|
||||||
if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
|
if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
|
||||||
var node = self[operand];
|
var node = self[operand];
|
||||||
if (node instanceof AST_UnaryPrefix && node.operator == "+") {
|
if (node instanceof AST_UnaryPrefix && node.operator == "+") {
|
||||||
@@ -9431,7 +9441,7 @@ merge(Compressor.prototype, {
|
|||||||
case "&":
|
case "&":
|
||||||
case "|":
|
case "|":
|
||||||
case "^":
|
case "^":
|
||||||
// a + +b => +b + a
|
// a + +b ---> +b + a
|
||||||
if (self.operator != "-"
|
if (self.operator != "-"
|
||||||
&& self.operator != "/"
|
&& self.operator != "/"
|
||||||
&& (self.left.is_boolean(compressor) || self.left.is_number(compressor))
|
&& (self.left.is_boolean(compressor) || self.left.is_number(compressor))
|
||||||
@@ -9453,7 +9463,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!associative || !self.is_number(compressor)) break;
|
if (!associative || !self.is_number(compressor)) break;
|
||||||
// a + (b + c) => (a + b) + c
|
// a + (b + c) ---> (a + b) + c
|
||||||
if (self.right instanceof AST_Binary
|
if (self.right instanceof AST_Binary
|
||||||
&& self.right.operator != "%"
|
&& self.right.operator != "%"
|
||||||
&& PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
|
&& PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
|
||||||
@@ -9485,8 +9495,8 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// (2 * n) * 3 => 6 * n
|
// (2 * n) * 3 ---> 6 * n
|
||||||
// (n + 2) + 3 => n + 5
|
// (n + 2) + 3 ---> n + 5
|
||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
&& self.left instanceof AST_Binary
|
&& self.left instanceof AST_Binary
|
||||||
&& self.left.operator != "%"
|
&& self.left.operator != "%"
|
||||||
@@ -9511,7 +9521,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
|
if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
|
||||||
if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
|
if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
|
||||||
// 0 + n => n
|
// 0 + n ---> n
|
||||||
case "+":
|
case "+":
|
||||||
if (self.left.value == 0) {
|
if (self.left.value == 0) {
|
||||||
if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
|
if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
|
||||||
@@ -9521,7 +9531,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
|
if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// 1 * n => n
|
// 1 * n ---> n
|
||||||
case "*":
|
case "*":
|
||||||
if (self.left.value == 1) {
|
if (self.left.value == 1) {
|
||||||
return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
|
return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
|
||||||
@@ -9532,7 +9542,7 @@ merge(Compressor.prototype, {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
|
if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
|
||||||
// n + 0 => n
|
// n + 0 ---> n
|
||||||
case "+":
|
case "+":
|
||||||
if (self.right.value == 0) {
|
if (self.right.value == 0) {
|
||||||
if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
|
if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
|
||||||
@@ -9542,7 +9552,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
|
if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// n - 0 => n
|
// n - 0 ---> n
|
||||||
case "-":
|
case "-":
|
||||||
if (self.right.value == 0) {
|
if (self.right.value == 0) {
|
||||||
return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
|
return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
|
||||||
@@ -9551,7 +9561,7 @@ merge(Compressor.prototype, {
|
|||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// n / 1 => n
|
// n / 1 ---> n
|
||||||
case "/":
|
case "/":
|
||||||
if (self.right.value == 1) {
|
if (self.right.value == 1) {
|
||||||
return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
|
return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
|
||||||
@@ -9674,16 +9684,16 @@ merge(Compressor.prototype, {
|
|||||||
function is_indexOf_match_pattern() {
|
function is_indexOf_match_pattern() {
|
||||||
switch (self.operator) {
|
switch (self.operator) {
|
||||||
case "<=":
|
case "<=":
|
||||||
// 0 <= array.indexOf(string) => !!~array.indexOf(string)
|
// 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
|
||||||
return indexRight && self.left instanceof AST_Number && self.left.value == 0;
|
return indexRight && self.left instanceof AST_Number && self.left.value == 0;
|
||||||
case "<":
|
case "<":
|
||||||
// array.indexOf(string) < 0 => !~array.indexOf(string)
|
// array.indexOf(string) < 0 ---> !~array.indexOf(string)
|
||||||
if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
|
if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
|
||||||
// -1 < array.indexOf(string) => !!~array.indexOf(string)
|
// -1 < array.indexOf(string) ---> !!~array.indexOf(string)
|
||||||
case "==":
|
case "==":
|
||||||
case "!=":
|
case "!=":
|
||||||
// -1 == array.indexOf(string) => !~array.indexOf(string)
|
// -1 == array.indexOf(string) ---> !~array.indexOf(string)
|
||||||
// -1 != array.indexOf(string) => !!~array.indexOf(string)
|
// -1 != array.indexOf(string) ---> !!~array.indexOf(string)
|
||||||
if (!indexRight) return false;
|
if (!indexRight) return false;
|
||||||
return self.left instanceof AST_Number && self.left.value == -1
|
return self.left instanceof AST_Number && self.left.value == -1
|
||||||
|| self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
|
|| self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
|
||||||
@@ -10037,7 +10047,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.right.left instanceof AST_SymbolRef
|
if (self.right.left instanceof AST_SymbolRef
|
||||||
&& self.right.left.name == self.left.name
|
&& self.right.left.name == self.left.name
|
||||||
&& ASSIGN_OPS[self.right.operator]) {
|
&& ASSIGN_OPS[self.right.operator]) {
|
||||||
// x = x - 2 => x -= 2
|
// x = x - 2 ---> x -= 2
|
||||||
return make_node(AST_Assign, self, {
|
return make_node(AST_Assign, self, {
|
||||||
operator: self.right.operator + "=",
|
operator: self.right.operator + "=",
|
||||||
left: self.left,
|
left: self.left,
|
||||||
@@ -10048,7 +10058,7 @@ merge(Compressor.prototype, {
|
|||||||
&& self.right.right.name == self.left.name
|
&& self.right.right.name == self.left.name
|
||||||
&& ASSIGN_OPS_COMMUTATIVE[self.right.operator]
|
&& ASSIGN_OPS_COMMUTATIVE[self.right.operator]
|
||||||
&& !self.right.left.has_side_effects(compressor)) {
|
&& !self.right.left.has_side_effects(compressor)) {
|
||||||
// x = 2 & x => x &= 2
|
// x = 2 & x ---> x &= 2
|
||||||
return make_node(AST_Assign, self, {
|
return make_node(AST_Assign, self, {
|
||||||
operator: self.right.operator + "=",
|
operator: self.right.operator + "=",
|
||||||
left: self.left,
|
left: self.left,
|
||||||
@@ -10122,13 +10132,13 @@ merge(Compressor.prototype, {
|
|||||||
var consequent = self.consequent;
|
var consequent = self.consequent;
|
||||||
var alternative = self.alternative;
|
var alternative = self.alternative;
|
||||||
if (repeatable(compressor, condition)) {
|
if (repeatable(compressor, condition)) {
|
||||||
// x ? x : y => x || y
|
// x ? x : y ---> x || y
|
||||||
if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
|
if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: condition,
|
left: condition,
|
||||||
right: alternative,
|
right: alternative,
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
// x ? y : x => x && y
|
// x ? y : x ---> x && y
|
||||||
if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
|
if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: condition,
|
left: condition,
|
||||||
@@ -10162,17 +10172,17 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// x ? y : y => x, y
|
// x ? y : y ---> x, y
|
||||||
if (consequent.equivalent_to(alternative)) return make_sequence(self, [
|
if (consequent.equivalent_to(alternative)) return make_sequence(self, [
|
||||||
condition,
|
condition,
|
||||||
consequent
|
consequent
|
||||||
]).optimize(compressor);
|
]).optimize(compressor);
|
||||||
// x ? y.p : z.p => (x ? y : z).p
|
// x ? y.p : z.p ---> (x ? y : z).p
|
||||||
// x ? y(a) : z(a) => (x ? y : z)(a)
|
// x ? y(a) : z(a) ---> (x ? y : z)(a)
|
||||||
// x ? y.f(a) : z.f(a) => (x ? y : z).f(a)
|
// x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
|
||||||
var combined = combine_tail(consequent, alternative, true);
|
var combined = combine_tail(consequent, alternative, true);
|
||||||
if (combined) return combined;
|
if (combined) return combined;
|
||||||
// x ? y(a) : y(b) => y(x ? a : b)
|
// x ? y(a) : y(b) ---> y(x ? a : b)
|
||||||
var arg_index;
|
var arg_index;
|
||||||
if (consequent instanceof AST_Call
|
if (consequent instanceof AST_Call
|
||||||
&& alternative.TYPE == consequent.TYPE
|
&& alternative.TYPE == consequent.TYPE
|
||||||
@@ -10195,7 +10205,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
// x ? (y ? a : b) : b => x && y ? a : b
|
// x ? (y ? a : b) : b ---> x && y ? a : b
|
||||||
if (consequent instanceof AST_Conditional
|
if (consequent instanceof AST_Conditional
|
||||||
&& consequent.alternative.equivalent_to(alternative)) {
|
&& consequent.alternative.equivalent_to(alternative)) {
|
||||||
return make_node(AST_Conditional, self, {
|
return make_node(AST_Conditional, self, {
|
||||||
@@ -10208,7 +10218,7 @@ merge(Compressor.prototype, {
|
|||||||
alternative: alternative
|
alternative: alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// x ? (y ? a : b) : a => !x || y ? a : b
|
// x ? (y ? a : b) : a ---> !x || y ? a : b
|
||||||
if (consequent instanceof AST_Conditional
|
if (consequent instanceof AST_Conditional
|
||||||
&& consequent.consequent.equivalent_to(alternative)) {
|
&& consequent.consequent.equivalent_to(alternative)) {
|
||||||
return make_node(AST_Conditional, self, {
|
return make_node(AST_Conditional, self, {
|
||||||
@@ -10221,7 +10231,7 @@ merge(Compressor.prototype, {
|
|||||||
alternative: consequent.alternative
|
alternative: consequent.alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// x ? a : (y ? a : b) => x || y ? a : b
|
// x ? a : (y ? a : b) ---> x || y ? a : b
|
||||||
if (alternative instanceof AST_Conditional
|
if (alternative instanceof AST_Conditional
|
||||||
&& consequent.equivalent_to(alternative.consequent)) {
|
&& consequent.equivalent_to(alternative.consequent)) {
|
||||||
return make_node(AST_Conditional, self, {
|
return make_node(AST_Conditional, self, {
|
||||||
@@ -10234,7 +10244,7 @@ merge(Compressor.prototype, {
|
|||||||
alternative: alternative.alternative
|
alternative: alternative.alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// x ? b : (y ? a : b) => !x && y ? a : b
|
// x ? b : (y ? a : b) ---> !x && y ? a : b
|
||||||
if (alternative instanceof AST_Conditional
|
if (alternative instanceof AST_Conditional
|
||||||
&& consequent.equivalent_to(alternative.alternative)) {
|
&& consequent.equivalent_to(alternative.alternative)) {
|
||||||
return make_node(AST_Conditional, self, {
|
return make_node(AST_Conditional, self, {
|
||||||
@@ -10247,7 +10257,7 @@ merge(Compressor.prototype, {
|
|||||||
alternative: consequent
|
alternative: consequent
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// x ? (a, c) : (b, c) => x ? a : b, c
|
// x ? (a, c) : (b, c) ---> x ? a : b, c
|
||||||
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
|
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
|
||||||
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
|
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
|
||||||
return make_sequence(self, [
|
return make_sequence(self, [
|
||||||
@@ -10259,7 +10269,7 @@ merge(Compressor.prototype, {
|
|||||||
consequent.tail_node()
|
consequent.tail_node()
|
||||||
]).optimize(compressor);
|
]).optimize(compressor);
|
||||||
}
|
}
|
||||||
// x ? y && a : a => (!x || y) && a
|
// x ? y && a : a ---> (!x || y) && a
|
||||||
if (consequent instanceof AST_Binary
|
if (consequent instanceof AST_Binary
|
||||||
&& consequent.operator == "&&"
|
&& consequent.operator == "&&"
|
||||||
&& consequent.right.equivalent_to(alternative)) {
|
&& consequent.right.equivalent_to(alternative)) {
|
||||||
@@ -10273,7 +10283,7 @@ merge(Compressor.prototype, {
|
|||||||
right: alternative
|
right: alternative
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
// x ? y || a : a => x && y || a
|
// x ? y || a : a ---> x && y || a
|
||||||
if (consequent instanceof AST_Binary
|
if (consequent instanceof AST_Binary
|
||||||
&& consequent.operator == "||"
|
&& consequent.operator == "||"
|
||||||
&& consequent.right.equivalent_to(alternative)) {
|
&& consequent.right.equivalent_to(alternative)) {
|
||||||
@@ -10287,7 +10297,7 @@ merge(Compressor.prototype, {
|
|||||||
right: alternative
|
right: alternative
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
// x ? a : y && a => (x || y) && a
|
// x ? a : y && a ---> (x || y) && a
|
||||||
if (alternative instanceof AST_Binary
|
if (alternative instanceof AST_Binary
|
||||||
&& alternative.operator == "&&"
|
&& alternative.operator == "&&"
|
||||||
&& alternative.right.equivalent_to(consequent)) {
|
&& alternative.right.equivalent_to(consequent)) {
|
||||||
@@ -10301,7 +10311,7 @@ merge(Compressor.prototype, {
|
|||||||
right: consequent
|
right: consequent
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
// x ? a : y || a => !x && y || a
|
// x ? a : y || a ---> !x && y || a
|
||||||
if (alternative instanceof AST_Binary
|
if (alternative instanceof AST_Binary
|
||||||
&& alternative.operator == "||"
|
&& alternative.operator == "||"
|
||||||
&& alternative.right.equivalent_to(consequent)) {
|
&& alternative.right.equivalent_to(consequent)) {
|
||||||
@@ -10318,10 +10328,10 @@ merge(Compressor.prototype, {
|
|||||||
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
||||||
if (is_true(consequent)) {
|
if (is_true(consequent)) {
|
||||||
if (is_false(alternative)) {
|
if (is_false(alternative)) {
|
||||||
// c ? true : false => !!c
|
// c ? true : false ---> !!c
|
||||||
return booleanize(condition);
|
return booleanize(condition);
|
||||||
}
|
}
|
||||||
// c ? true : x => !!c || x
|
// c ? true : x ---> !!c || x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: booleanize(condition),
|
left: booleanize(condition),
|
||||||
@@ -10330,10 +10340,10 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (is_false(consequent)) {
|
if (is_false(consequent)) {
|
||||||
if (is_true(alternative)) {
|
if (is_true(alternative)) {
|
||||||
// c ? false : true => !c
|
// c ? false : true ---> !c
|
||||||
return booleanize(condition.negate(compressor));
|
return booleanize(condition.negate(compressor));
|
||||||
}
|
}
|
||||||
// c ? false : x => !c && x
|
// c ? false : x ---> !c && x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: booleanize(condition.negate(compressor)),
|
left: booleanize(condition.negate(compressor)),
|
||||||
@@ -10341,7 +10351,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_true(alternative)) {
|
if (is_true(alternative)) {
|
||||||
// c ? x : true => !c || x
|
// c ? x : true ---> !c || x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: booleanize(condition.negate(compressor)),
|
left: booleanize(condition.negate(compressor)),
|
||||||
@@ -10349,7 +10359,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_false(alternative)) {
|
if (is_false(alternative)) {
|
||||||
// c ? x : false => !!c && x
|
// c ? x : false ---> !!c && x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: booleanize(condition),
|
left: booleanize(condition),
|
||||||
|
|||||||
@@ -689,32 +689,32 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
PARENS(AST_Sequence, function(output) {
|
PARENS(AST_Sequence, function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
// [ 1, (2, 3), 4 ] ---> [ 1, 3, 4 ]
|
||||||
return p instanceof AST_Array
|
return p instanceof AST_Array
|
||||||
// () => (foo, bar)
|
// () ---> (foo, bar)
|
||||||
|| is_arrow(p) && p.value === this
|
|| is_arrow(p) && p.value === this
|
||||||
// await (foo, bar)
|
// await (foo, bar)
|
||||||
|| p instanceof AST_Await
|
|| p instanceof AST_Await
|
||||||
// 1 + (2, 3) + 4 ==> 8
|
// 1 + (2, 3) + 4 ---> 8
|
||||||
|| p instanceof AST_Binary
|
|| p instanceof AST_Binary
|
||||||
// new (foo, bar) or foo(1, (2, 3), 4)
|
// new (foo, bar) or foo(1, (2, 3), 4)
|
||||||
|| p instanceof AST_Call
|
|| p instanceof AST_Call
|
||||||
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||||
// ==> 20 (side effect, set a := 10 and b := 20)
|
// ---> 20 (side effect, set a := 10 and b := 20)
|
||||||
|| p instanceof AST_Conditional
|
|| p instanceof AST_Conditional
|
||||||
// [ a = (1, 2) ] = [] ==> a == 2
|
// [ a = (1, 2) ] = [] ---> a == 2
|
||||||
|| p instanceof AST_DefaultValue
|
|| p instanceof AST_DefaultValue
|
||||||
// { [(1, 2)]: 3 }[2] ==> 3
|
// { [(1, 2)]: 3 }[2] ---> 3
|
||||||
// { foo: (1, 2) }.foo ==> 2
|
// { foo: (1, 2) }.foo ---> 2
|
||||||
|| p instanceof AST_DestructuredKeyVal
|
|| p instanceof AST_DestructuredKeyVal
|
||||||
|| p instanceof AST_ObjectProperty
|
|| p instanceof AST_ObjectProperty
|
||||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2
|
||||||
|| p instanceof AST_PropAccess && p.expression === this
|
|| p instanceof AST_PropAccess && p.expression === this
|
||||||
// ...(foo, bar, baz)
|
// ...(foo, bar, baz)
|
||||||
|| p instanceof AST_Spread
|
|| p instanceof AST_Spread
|
||||||
// !(foo, bar, baz)
|
// !(foo, bar, baz)
|
||||||
|| p instanceof AST_Unary
|
|| p instanceof AST_Unary
|
||||||
// var a = (1, 2), b = a + a; ==> b == 4
|
// var a = (1, 2), b = a + a; ---> b == 4
|
||||||
|| p instanceof AST_VarDef;
|
|| p instanceof AST_VarDef;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -777,14 +777,10 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Number, function(output) {
|
PARENS(AST_Number, function(output) {
|
||||||
|
if (!output.option("galio")) return false;
|
||||||
|
// https://github.com/mishoo/UglifyJS/pull/1009
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_PropAccess && p.expression === this) {
|
return p instanceof AST_PropAccess && p.expression === this && /^0/.test(make_num(this.value));
|
||||||
var value = this.value;
|
|
||||||
// https://github.com/mishoo/UglifyJS/issues/115
|
|
||||||
return value < 0
|
|
||||||
// https://github.com/mishoo/UglifyJS/pull/1009
|
|
||||||
|| output.option("galio") && /^0/.test(make_num(value));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function needs_parens_assign_cond(self, output) {
|
function needs_parens_assign_cond(self, output) {
|
||||||
@@ -807,8 +803,8 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
PARENS(AST_Assign, function(output) {
|
PARENS(AST_Assign, function(output) {
|
||||||
if (needs_parens_assign_cond(this, output)) return true;
|
if (needs_parens_assign_cond(this, output)) return true;
|
||||||
// v8 parser bug => workaround
|
// v8 parser bug ---> workaround
|
||||||
// f([1], [a] = []) => f([1], ([a] = []))
|
// f([1], [a] = []) ---> f([1], ([a] = []))
|
||||||
if (output.option("v8")) return this.left instanceof AST_Destructured;
|
if (output.option("v8")) return this.left instanceof AST_Destructured;
|
||||||
// ({ p: a } = o);
|
// ({ p: a } = o);
|
||||||
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
|
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
|
||||||
|
|||||||
22
lib/parse.js
22
lib/parse.js
@@ -280,9 +280,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function read_while(pred) {
|
function read_while(pred) {
|
||||||
var ret = "", ch, i = 0;
|
var ret = "", ch;
|
||||||
while ((ch = peek()) && pred(ch, i++))
|
while ((ch = peek()) && pred(ch)) ret += next();
|
||||||
ret += next();
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,16 +291,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
|
|
||||||
function read_num(prefix) {
|
function read_num(prefix) {
|
||||||
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
|
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
|
||||||
var num = read_while(function(ch, i) {
|
var num = read_while(function(ch) {
|
||||||
var code = ch.charCodeAt(0);
|
var code = ch.charCodeAt(0);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 120: case 88: // xX
|
case 120: case 88: // xX
|
||||||
return has_x ? false : (has_x = true);
|
return has_x ? false : (has_x = true);
|
||||||
case 101: case 69: // eE
|
case 101: case 69: // eE
|
||||||
return has_x ? true : has_e ? false : (has_e = after_e = true);
|
return has_x ? true : has_e ? false : (has_e = after_e = true);
|
||||||
case 45: // -
|
case 43: case 45: // +-
|
||||||
return after_e || (i == 0 && !prefix);
|
|
||||||
case 43: // +
|
|
||||||
return after_e;
|
return after_e;
|
||||||
case (after_e = false, 46): // .
|
case (after_e = false, 46): // .
|
||||||
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
|
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
|
||||||
@@ -315,8 +312,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
|
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
|
||||||
}
|
}
|
||||||
var valid = parse_js_number(num);
|
var valid = parse_js_number(num);
|
||||||
if (!isNaN(valid)) return token("num", valid);
|
if (isNaN(valid)) parse_error("Invalid syntax: " + num);
|
||||||
parse_error("Invalid syntax: " + num);
|
if (has_dot || has_e || peek() != "n") return token("num", valid);
|
||||||
|
return token("bigint", num.toLowerCase() + next());
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_escaped_char(in_string) {
|
function read_escaped_char(in_string) {
|
||||||
@@ -635,7 +633,7 @@ var PRECEDENCE = function(a, ret) {
|
|||||||
["*", "/", "%"]
|
["*", "/", "%"]
|
||||||
], {});
|
], {});
|
||||||
|
|
||||||
var ATOMIC_START_TOKEN = makePredicate("atom num regexp string");
|
var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string");
|
||||||
|
|
||||||
/* -----[ Parser ]----- */
|
/* -----[ Parser ]----- */
|
||||||
|
|
||||||
@@ -783,6 +781,7 @@ function parse($TEXT, options) {
|
|||||||
semicolon();
|
semicolon();
|
||||||
return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
|
return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
|
||||||
case "num":
|
case "num":
|
||||||
|
case "bigint":
|
||||||
case "regexp":
|
case "regexp":
|
||||||
case "operator":
|
case "operator":
|
||||||
case "atom":
|
case "atom":
|
||||||
@@ -1361,6 +1360,9 @@ function parse($TEXT, options) {
|
|||||||
case "num":
|
case "num":
|
||||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||||
break;
|
break;
|
||||||
|
case "bigint":
|
||||||
|
ret = new AST_BigInt({ start: tok, end: tok, value: tok.value });
|
||||||
|
break;
|
||||||
case "string":
|
case "string":
|
||||||
ret = new AST_String({
|
ret = new AST_String({
|
||||||
start : tok,
|
start : tok,
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ function mangle_properties(ast, options) {
|
|||||||
var mangled = cache.get(name);
|
var mangled = cache.get(name);
|
||||||
if (!mangled) {
|
if (!mangled) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
|
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo ---> o._$foo$NNN_.
|
||||||
var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
|
var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
|
||||||
if (can_mangle(debug_mangled)) mangled = debug_mangled;
|
if (can_mangle(debug_mangled)) mangled = debug_mangled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ holes_and_undefined: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constant_join: {
|
constant_join_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
side_effects: true,
|
||||||
strings: true,
|
strings: true,
|
||||||
unsafe: true,
|
unsafe: true,
|
||||||
}
|
}
|
||||||
@@ -57,7 +58,7 @@ constant_join: {
|
|||||||
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
|
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
|
||||||
var c6 = [ "1,2,,,foo,bar", baz() ].join();
|
var c6 = [ "1,2,,,foo,bar", baz() ].join();
|
||||||
var d = "foo-3bar-baz";
|
var d = "foo-3bar-baz";
|
||||||
var e = [].join(foo + bar);
|
var e = (foo, bar, "");
|
||||||
var f = "";
|
var f = "";
|
||||||
var g = "";
|
var g = "";
|
||||||
}
|
}
|
||||||
|
|||||||
46
test/compress/bigint.js
Normal file
46
test/compress/bigint.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
arithmetic: {
|
||||||
|
input: {
|
||||||
|
console.log(((1n + 0x2n) * (0o3n - -4n)) >> (5n - 6n));
|
||||||
|
}
|
||||||
|
expect_exact: "console.log((1n+0x2n)*(0o3n- -4n)>>5n-6n);"
|
||||||
|
expect_stdout: "42n"
|
||||||
|
node_version: ">=10"
|
||||||
|
}
|
||||||
|
|
||||||
|
minus_dot: {
|
||||||
|
input: {
|
||||||
|
console.log(typeof -42n.toString(), typeof (-42n).toString());
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(typeof-42n.toString(),typeof(-42n).toString());"
|
||||||
|
expect_stdout: "number string"
|
||||||
|
node_version: ">=10"
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log((0xDEAD_BEEFn).toString(16));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(0xdeadbeefn.toString(16));
|
||||||
|
}
|
||||||
|
expect_stdout: "deadbeef"
|
||||||
|
node_version: ">=10"
|
||||||
|
}
|
||||||
|
|
||||||
|
Number: {
|
||||||
|
options = {
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(Number(-0xfeed_dead_beef_badn));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(+("" + -0xfeed_dead_beef_badn));
|
||||||
|
}
|
||||||
|
expect_stdout: "-1148098955808013200"
|
||||||
|
node_version: ">=10"
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ issue_269_1: {
|
|||||||
expect: {
|
expect: {
|
||||||
var x = {};
|
var x = {};
|
||||||
console.log(
|
console.log(
|
||||||
x + "", +x, !!x,
|
"" + x, +("" + x), !!x,
|
||||||
"", 0, false
|
"", 0, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ evaluate_3: {
|
|||||||
console.log(1 + Number(x) + 2);
|
console.log(1 + Number(x) + 2);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log(+x + 3);
|
console.log(+("" + x) + 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ var SUPPORT = function(matrix) {
|
|||||||
}({
|
}({
|
||||||
arrow: "a => 0;",
|
arrow: "a => 0;",
|
||||||
async: "async function f(){}",
|
async: "async function f(){}",
|
||||||
|
bigint: "42n",
|
||||||
catch_omit_var: "try {} catch {}",
|
catch_omit_var: "try {} catch {}",
|
||||||
computed_key: "({[0]: 0});",
|
computed_key: "({[0]: 0});",
|
||||||
const_block: "var a; { const a = 0; }",
|
const_block: "var a; { const a = 0; }",
|
||||||
@@ -188,6 +189,12 @@ var VALUES = [
|
|||||||
'"function"',
|
'"function"',
|
||||||
"this",
|
"this",
|
||||||
];
|
];
|
||||||
|
if (SUPPORT.bigint) VALUES = VALUES.concat([
|
||||||
|
"!0o644n",
|
||||||
|
"([3n][0] > 2)",
|
||||||
|
"(-42n).toString()",
|
||||||
|
"Number(0XDEADn << 16n | 0xbeefn)",
|
||||||
|
]);
|
||||||
|
|
||||||
var BINARY_OPS = [
|
var BINARY_OPS = [
|
||||||
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
|
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
|
||||||
|
|||||||
Reference in New Issue
Block a user