Compare commits

..

15 Commits

Author SHA1 Message Date
Alex Lam S.L
6982a0554c v3.3.17 2018-03-31 04:13:45 +00:00
Alex Lam S.L
fa3250199a mangle unused nested AST_SymbolCatch correctly (#3038)
fixes #3035
2018-03-30 16:23:09 +09:00
Alex Lam S.L
06b9894c19 handle modifications to this correctly (#3036)
fixes #3032
2018-03-30 15:07:36 +09:00
Alex Lam S.L
9f9db504d7 improve test for #3023 (#3031) 2018-03-29 23:36:40 +09:00
Alex Lam S.L
82ae95c334 improve source map granularity (#3030)
fixes #3023
2018-03-29 14:47:55 +09:00
Fábio Santos
9a5e2052c4 fix extra regex slash when going through mozilla AST I/O (#3025)
This relates to #1929, but in the context of mozilla AST input/output.
2018-03-27 03:22:01 +09:00
Alex Lam S.L
b1410be443 speed up has_parens() (#3014) 2018-03-24 04:05:28 +08:00
Alex Lam S.L
12985d86c2 fix corner case in hoist_props (#3022)
fixes #3021
2018-03-23 07:27:35 +08:00
Alex Lam S.L
49bfc6b555 improve performance (#3020)
- replace `find_if()` with `all()` wherever possible
- move ESTree-specific logic out of `figure_out_scope()`
2018-03-23 03:43:52 +08:00
Alex Lam S.L
d1c6bb8c7c fix nested inline within loop (#3019)
fixes #3018
2018-03-23 02:31:59 +08:00
Alex Lam S.L
5c169615a8 fix corner case in inline (#3017)
fixes #3016
2018-03-22 23:46:26 +08:00
Alex Lam S.L
73d77f4f64 v3.3.16 2018-03-19 06:53:51 +00:00
Alex Lam S.L
ccf0e2ef4f extend fuzzy RHS folding (#3006)
- `a = []; if (1) x();` => `if (a = []) x();`
2018-03-17 03:10:21 +08:00
Alex Lam S.L
20ca0f5906 improve truthy compression (#3009) 2018-03-16 06:12:59 +08:00
Alex Lam S.L
b29d435bb5 refactor brackets to braces (#3005) 2018-03-15 15:46:45 +08:00
31 changed files with 868 additions and 369 deletions

View File

@@ -824,7 +824,7 @@ can pass additional arguments that control the code output:
when you want to generate minified code, in order to specify additional when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it. arguments, so you can use `-b beautify=false` to override it.
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`, - `braces` (default `false`) -- always insert braces in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single `do`, `while` or `with` statements, even if their body is a single
statement. statement.

View File

@@ -165,7 +165,7 @@ function walk_body(node, visitor) {
}; };
var AST_Block = DEFNODE("Block", "body", { var AST_Block = DEFNODE("Block", "body", {
$documentation: "A body of statements (usually bracketed)", $documentation: "A body of statements (usually braced)",
$propdoc: { $propdoc: {
body: "[AST_Statement*] an array of statements" body: "[AST_Statement*] an array of statements"
}, },
@@ -916,5 +916,25 @@ TreeWalker.prototype = {
|| node instanceof AST_Break && x instanceof AST_Switch) || node instanceof AST_Break && x instanceof AST_Switch)
return x; return x;
} }
},
in_boolean_context: function() {
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
} }
}; };

View File

@@ -147,27 +147,6 @@ merge(Compressor.prototype, {
return true; return true;
return false; return false;
}, },
in_boolean_context: function() {
if (!this.option("booleans")) return false;
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
},
compress: function(node) { compress: function(node) {
if (this.option("expression")) { if (this.option("expression")) {
node.process_expression(true); node.process_expression(true);
@@ -980,7 +959,7 @@ merge(Compressor.prototype, {
var args; var args;
var candidates = []; var candidates = [];
var stat_index = statements.length; var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) { var scanner = new TreeTransformer(function(node) {
if (abort) return node; if (abort) return node;
// Skip nodes before `candidate` as quickly as possible // Skip nodes before `candidate` as quickly as possible
if (!hit) { if (!hit) {
@@ -1019,7 +998,7 @@ merge(Compressor.prototype, {
if (can_replace if (can_replace
&& !(node instanceof AST_SymbolDeclaration) && !(node instanceof AST_SymbolDeclaration)
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node)) && (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
|| scan_rhs && (hit_rhs = rhs.equivalent_to(node)))) { || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) { if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true; abort = true;
return node; return node;
@@ -1073,6 +1052,7 @@ merge(Compressor.prototype, {
&& (side_effects || node.expression.may_throw_on_access(compressor)) && (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef || node instanceof AST_SymbolRef
&& (symbol_in_lvalues(node) || side_effects && may_modify(node)) && (symbol_in_lvalues(node) || side_effects && may_modify(node))
|| node instanceof AST_This && symbol_in_lvalues(node)
|| node instanceof AST_VarDef && node.value || node instanceof AST_VarDef && node.value
&& (node.name.name in lvalues || side_effects && may_modify(node.name)) && (node.name.name in lvalues || side_effects && may_modify(node.name))
|| (sym = is_lhs(node.left, node)) || (sym = is_lhs(node.left, node))
@@ -1388,12 +1368,16 @@ merge(Compressor.prototype, {
} }
function foldable(expr) { function foldable(expr) {
if (expr.is_constant()) return true; if (expr instanceof AST_SymbolRef) {
if (expr instanceof AST_Array) return false; var value = expr.evaluate(compressor);
if (expr instanceof AST_Function) return false; if (value === expr) return rhs_exact_match;
if (expr instanceof AST_Object) return false; return rhs_fuzzy_match(value, rhs_exact_match);
if (expr instanceof AST_RegExp) return false; }
if (expr instanceof AST_Symbol) return true; if (expr instanceof AST_This) return rhs_exact_match;
if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
if (expr.is_constant()) {
return rhs_fuzzy_match(expr.evaluate(compressor), rhs_exact_match);
}
if (!(lhs instanceof AST_SymbolRef)) return false; if (!(lhs instanceof AST_SymbolRef)) return false;
if (expr.has_side_effects(compressor)) return false; if (expr.has_side_effects(compressor)) return false;
var circular; var circular;
@@ -1404,7 +1388,25 @@ merge(Compressor.prototype, {
circular = true; circular = true;
} }
})); }));
return !circular; return !circular && rhs_exact_match;
}
function rhs_exact_match(node) {
return rhs.equivalent_to(node);
}
function rhs_fuzzy_match(value, fallback) {
return function(node, tw) {
if (tw.in_boolean_context()) {
if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
return true;
}
if (node.is_constant()) {
return !node.evaluate(compressor) == !value;
}
}
return fallback(node);
};
} }
function get_lvalues(expr) { function get_lvalues(expr) {
@@ -2041,6 +2043,28 @@ merge(Compressor.prototype, {
&& !node.expression.has_side_effects(compressor); && !node.expression.has_side_effects(compressor);
} }
// is_truthy()
// return true if `!!node === true`
(function(def) {
def(AST_Node, return_false);
def(AST_Array, return_true);
def(AST_Assign, function() {
return this.operator == "=" && this.right.is_truthy();
});
def(AST_Lambda, return_true);
def(AST_Object, return_true);
def(AST_RegExp, return_true);
def(AST_Sequence, function() {
return this.tail_node().is_truthy();
});
def(AST_SymbolRef, function() {
var fixed = this.fixed_value();
return fixed && fixed.is_truthy();
});
})(function(node, func) {
node.DEFMETHOD("is_truthy", func);
});
// may_throw_on_access() // may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor` // returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) { (function(def) {
@@ -3502,8 +3526,9 @@ merge(Compressor.prototype, {
var defs = []; var defs = [];
vars.each(function(def, name){ vars.each(function(def, name){
if (self instanceof AST_Lambda if (self instanceof AST_Lambda
&& find_if(function(x){ return x.name == def.name.name }, && !all(self.argnames, function(argname) {
self.argnames)) { return argname.name != name;
})) {
vars.del(name); vars.del(name);
} else { } else {
def = def.clone(); def = def.clone();
@@ -3600,8 +3625,9 @@ merge(Compressor.prototype, {
var sym = node.name, def, value; var sym = node.name, def, value;
if (sym.scope === self if (sym.scope === self
&& (def = sym.definition()).escaped != 1 && (def = sym.definition()).escaped != 1
&& !def.single_use && !def.assignments
&& !def.direct_access && !def.direct_access
&& !def.single_use
&& !top_retain(def) && !top_retain(def)
&& (value = sym.fixed_value()) === node.value && (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) { && value instanceof AST_Object) {
@@ -3821,7 +3847,7 @@ merge(Compressor.prototype, {
OPT(AST_Do, function(self, compressor){ OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self; if (!compressor.option("loops")) return self;
var cond = self.condition.tail_node().evaluate(compressor); var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!(cond instanceof AST_Node)) { if (!(cond instanceof AST_Node)) {
if (cond) return make_node(AST_For, self, { if (cond) return make_node(AST_For, self, {
body: make_node(AST_BlockStatement, self.body, { body: make_node(AST_BlockStatement, self.body, {
@@ -3943,9 +3969,11 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig); self.condition = best_of_expression(self.condition.transform(compressor), orig);
} }
} }
if (compressor.option("dead_code")) { if (cond instanceof AST_Node) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor); cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!cond) { }
if (!cond) {
if (compressor.option("dead_code")) {
var body = []; var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body); extract_declarations_from_unreachable_code(compressor, self.body, body);
if (self.init instanceof AST_Statement) { if (self.init instanceof AST_Statement) {
@@ -3960,6 +3988,16 @@ merge(Compressor.prototype, {
})); }));
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
} }
} else if (self.condition && !(cond instanceof AST_Node)) {
self.body = make_node(AST_BlockStatement, self.body, {
body: [
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}),
self.body
]
});
self.condition = null;
} }
} }
return if_break_in_loop(self, compressor); return if_break_in_loop(self, compressor);
@@ -3980,7 +4018,9 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig); self.condition = best_of_expression(self.condition.transform(compressor), orig);
} }
if (compressor.option("dead_code")) { if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor); if (cond instanceof AST_Node) {
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
}
if (!cond) { if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start); compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
var body = []; var body = [];
@@ -4736,8 +4776,10 @@ merge(Compressor.prototype, {
var var_def = stat.definitions[j]; var var_def = stat.definitions[j];
var name = var_def.name; var name = var_def.name;
append_var(decls, expressions, name, var_def.value); append_var(decls, expressions, name, var_def.value);
if (in_loop) { if (in_loop && all(fn.argnames, function(argname) {
var def = name.definition(); return argname.name != name.name;
})) {
var def = fn.variables.get(name.name);
var sym = make_node(AST_SymbolRef, name, name); var sym = make_node(AST_SymbolRef, name, name);
def.references.push(sym); def.references.push(sym);
expressions.splice(pos++, 0, make_node(AST_Assign, var_def, { expressions.splice(pos++, 0, make_node(AST_Assign, var_def, {
@@ -4864,8 +4906,10 @@ merge(Compressor.prototype, {
return make_node(AST_Undefined, self).optimize(compressor); return make_node(AST_Undefined, self).optimize(compressor);
} }
} }
if (compressor.in_boolean_context()) { if (compressor.option("booleans")) {
switch (self.operator) { if (self.operator == "!" && e.is_truthy()) {
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
} else if (compressor.in_boolean_context()) switch (self.operator) {
case "!": case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") { if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context // !!foo ==> foo, if we're in boolean context
@@ -5051,7 +5095,7 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (self.operator == "+" && compressor.in_boolean_context()) { if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") { if (ll && typeof ll == "string") {
@@ -5106,7 +5150,7 @@ merge(Compressor.prototype, {
if (compressor.option("evaluate")) { if (compressor.option("evaluate")) {
switch (self.operator) { switch (self.operator) {
case "&&": case "&&":
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor); var ll = fuzzy_eval(self.left);
if (!ll) { if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start); compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
@@ -5116,7 +5160,7 @@ merge(Compressor.prototype, {
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
if (compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
@@ -5125,7 +5169,8 @@ merge(Compressor.prototype, {
} else self.falsy = true; } else self.falsy = true;
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent(); var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) { if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start); compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
@@ -5141,7 +5186,7 @@ merge(Compressor.prototype, {
} }
break; break;
case "||": case "||":
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor); var ll = fuzzy_eval(self.left);
if (!ll) { if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start); compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
@@ -5152,12 +5197,13 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
var parent = compressor.parent(); var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) { if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start); compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
if (compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
@@ -5379,6 +5425,13 @@ merge(Compressor.prototype, {
return best_of(compressor, ev, self); return best_of(compressor, ev, self);
} }
return self; return self;
function fuzzy_eval(node) {
if (node.truthy) return true;
if (node.falsy) return false;
if (node.is_truthy()) return true;
return node.evaluate(compressor);
}
}); });
function recursive_ref(compressor, def) { function recursive_ref(compressor, def) {
@@ -5674,15 +5727,13 @@ merge(Compressor.prototype, {
expressions.push(self); expressions.push(self);
return make_sequence(self, expressions); return make_sequence(self, expressions);
} }
var cond = self.condition.evaluate(compressor); var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (cond !== self.condition) { if (!cond) {
if (cond) { compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
compressor.warn("Condition always true [{file}:{line},{col}]", self.start); return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent); } else if (!(cond instanceof AST_Node)) {
} else { compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
compressor.warn("Condition always false [{file}:{line},{col}]", self.start); return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
}
} }
var negated = cond.negate(compressor, first_in_statement(compressor)); var negated = cond.negate(compressor, first_in_statement(compressor));
if (best_of(compressor, cond, negated) === negated) { if (best_of(compressor, cond, negated) === negated) {
@@ -5790,7 +5841,7 @@ merge(Compressor.prototype, {
right: alternative right: alternative
}).optimize(compressor); }).optimize(compressor);
} }
var in_bool = compressor.in_boolean_context(); var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (is_true(self.consequent)) { if (is_true(self.consequent)) {
if (is_false(self.alternative)) { if (is_false(self.alternative)) {
// c ? true : false ---> !!c // c ? true : false ---> !!c
@@ -5888,32 +5939,29 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Boolean, function(self, compressor){ OPT(AST_Boolean, function(self, compressor){
if (!compressor.option("booleans")) return self;
if (compressor.in_boolean_context()) return make_node(AST_Number, self, { if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value value: +self.value
}); });
if (compressor.option("booleans")) { var p = compressor.parent();
var p = compressor.parent(); if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
if (p instanceof AST_Binary && (p.operator == "==" compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
|| p.operator == "!=")) { operator : p.operator,
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { value : self.value,
operator : p.operator, file : p.start.file,
value : self.value, line : p.start.line,
file : p.start.file, col : p.start.col,
line : p.start.line, });
col : p.start.col, return make_node(AST_Number, self, {
}); value: +self.value
return make_node(AST_Number, self, {
value: +self.value
});
}
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
}); });
} }
return self; return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
});
}); });
OPT(AST_Sub, function(self, compressor){ OPT(AST_Sub, function(self, compressor){
@@ -6122,19 +6170,6 @@ merge(Compressor.prototype, {
return self; return self;
}); });
function literals_in_boolean_context(self, compressor) {
if (compressor.in_boolean_context()) {
return best_of(compressor, self, make_sequence(self, [
self,
make_node(AST_True, self)
]).optimize(compressor));
}
return self;
};
OPT(AST_Array, literals_in_boolean_context);
OPT(AST_Object, literals_in_boolean_context);
OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){ OPT(AST_Return, function(self, compressor){
if (self.value && is_undefined(self.value, compressor)) { if (self.value && is_undefined(self.value, compressor)) {
self.value = null; self.value = null;

View File

@@ -180,6 +180,17 @@
end : my_end_token(M) end : my_end_token(M)
}; };
if (val === null) return new AST_Null(args); if (val === null) return new AST_Null(args);
var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags);
args.value.raw_source = rx.pattern;
return new AST_RegExp(args);
} else if (rx) {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
return new AST_RegExp(args);
}
switch (typeof val) { switch (typeof val) {
case "string": case "string":
args.value = val; args.value = val;
@@ -189,16 +200,6 @@
return new AST_Number(args); return new AST_Number(args);
case "boolean": case "boolean":
return new (val ? AST_True : AST_False)(args); return new (val ? AST_True : AST_False)(args);
default:
var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags).toString();
} else {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
}
return new AST_RegExp(args);
} }
}, },
Identifier: function(M) { Identifier: function(M) {
@@ -410,14 +411,15 @@
}); });
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var value = M.value; var flags = M.value.toString().match(/[gimuy]*$/)[0];
var value = "/" + M.value.raw_source + "/" + flags;
return { return {
type: "Literal", type: "Literal",
value: value, value: value,
raw: value.toString(), raw: value,
regex: { regex: {
pattern: value.source, pattern: M.value.raw_source,
flags: value.toString().match(/[gimuy]*$/)[0] flags: flags
} }
}; };
}); });
@@ -564,6 +566,21 @@
FROM_MOZ_STACK = []; FROM_MOZ_STACK = [];
var ast = from_moz(node); var ast = from_moz(node);
FROM_MOZ_STACK = save_stack; FROM_MOZ_STACK = save_stack;
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_LabelRef) {
for (var level = 0, parent; parent = this.parent(level); level++) {
if (parent instanceof AST_Scope) break;
if (parent instanceof AST_LabeledStatement && parent.label.name == node.name) {
node.thedef = parent.label;
break;
}
}
if (!node.thedef) {
var s = node.start;
js_error("Undefined label " + node.name, s.file, s.line, s.col, s.pos);
}
}
}));
return ast; return ast;
}; };

View File

@@ -56,7 +56,7 @@ function OutputStream(options) {
options = defaults(options, { options = defaults(options, {
ascii_only : false, ascii_only : false,
beautify : false, beautify : false,
bracketize : false, braces : false,
comments : false, comments : false,
ie8 : false, ie8 : false,
indent_level : 4, indent_level : 4,
@@ -576,7 +576,7 @@ function OutputStream(options) {
indentation : function() { return indentation }, indentation : function() { return indentation },
current_width : function() { return current_col - indentation }, current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width }, should_break : function() { return options.width && this.current_width() >= options.width },
has_parens : function() { return OUTPUT.slice(-1) == "(" }, has_parens : function() { return OUTPUT[OUTPUT.length - 1] == "(" },
newline : newline, newline : newline,
print : print, print : print,
space : space, space : space,
@@ -886,22 +886,22 @@ function OutputStream(options) {
self.body.print(output); self.body.print(output);
output.semicolon(); output.semicolon();
}); });
function print_bracketed_empty(self, output) { function print_braced_empty(self, output) {
output.print("{"); output.print("{");
output.with_indent(output.next_indent(), function() { output.with_indent(output.next_indent(), function() {
output.append_comments(self, true); output.append_comments(self, true);
}); });
output.print("}"); output.print("}");
} }
function print_bracketed(self, output, allow_directives) { function print_braced(self, output, allow_directives) {
if (self.body.length > 0) { if (self.body.length > 0) {
output.with_block(function() { output.with_block(function() {
display_body(self.body, false, output, allow_directives); display_body(self.body, false, output, allow_directives);
}); });
} else print_bracketed_empty(self, output); } else print_braced_empty(self, output);
}; };
DEFPRINT(AST_BlockStatement, function(self, output){ DEFPRINT(AST_BlockStatement, function(self, output){
print_bracketed(self, output); print_braced(self, output);
}); });
DEFPRINT(AST_EmptyStatement, function(self, output){ DEFPRINT(AST_EmptyStatement, function(self, output){
output.semicolon(); output.semicolon();
@@ -996,7 +996,7 @@ function OutputStream(options) {
}); });
}); });
output.space(); output.space();
print_bracketed(self, output, true); print_braced(self, output, true);
}); });
DEFPRINT(AST_Lambda, function(self, output){ DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output); self._do_print(output);
@@ -1037,7 +1037,7 @@ function OutputStream(options) {
/* -----[ if ]----- */ /* -----[ if ]----- */
function make_then(self, output) { function make_then(self, output) {
var b = self.body; var b = self.body;
if (output.option("bracketize") if (output.option("braces")
|| output.option("ie8") && b instanceof AST_Do) || output.option("ie8") && b instanceof AST_Do)
return make_block(b, output); return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single // The squeezer replaces "block"-s that contain only a single
@@ -1046,7 +1046,7 @@ function OutputStream(options) {
// IF having an ELSE clause where the THEN clause ends in an // IF having an ELSE clause where the THEN clause ends in an
// IF *without* an ELSE block (then the outer ELSE would refer // IF *without* an ELSE block (then the outer ELSE would refer
// to the inner IF). This function checks for this case and // to the inner IF). This function checks for this case and
// adds the block brackets if needed. // adds the block braces if needed.
if (!b) return output.force_semicolon(); if (!b) return output.force_semicolon();
while (true) { while (true) {
if (b instanceof AST_If) { if (b instanceof AST_If) {
@@ -1093,7 +1093,7 @@ function OutputStream(options) {
}); });
output.space(); output.space();
var last = self.body.length - 1; var last = self.body.length - 1;
if (last < 0) print_bracketed_empty(self, output); if (last < 0) print_braced_empty(self, output);
else output.with_block(function(){ else output.with_block(function(){
self.body.forEach(function(branch, i){ self.body.forEach(function(branch, i){
output.indent(true); output.indent(true);
@@ -1127,7 +1127,7 @@ function OutputStream(options) {
DEFPRINT(AST_Try, function(self, output){ DEFPRINT(AST_Try, function(self, output){
output.print("try"); output.print("try");
output.space(); output.space();
print_bracketed(self, output); print_braced(self, output);
if (self.bcatch) { if (self.bcatch) {
output.space(); output.space();
self.bcatch.print(output); self.bcatch.print(output);
@@ -1144,12 +1144,12 @@ function OutputStream(options) {
self.argname.print(output); self.argname.print(output);
}); });
output.space(); output.space();
print_bracketed(self, output); print_braced(self, output);
}); });
DEFPRINT(AST_Finally, function(self, output){ DEFPRINT(AST_Finally, function(self, output){
output.print("finally"); output.print("finally");
output.space(); output.space();
print_bracketed(self, output); print_braced(self, output);
}); });
/* -----[ var/const ]----- */ /* -----[ var/const ]----- */
@@ -1348,7 +1348,7 @@ function OutputStream(options) {
}); });
output.newline(); output.newline();
}); });
else print_bracketed_empty(self, output); else print_braced_empty(self, output);
}); });
function print_property_name(key, quote, output) { function print_property_name(key, quote, output) {
@@ -1420,7 +1420,7 @@ function OutputStream(options) {
}); });
function force_statement(stat, output) { function force_statement(stat, output) {
if (output.option("bracketize")) { if (output.option("braces")) {
make_block(stat, output); make_block(stat, output);
} else { } else {
if (!stat || stat instanceof AST_EmptyStatement) if (!stat || stat instanceof AST_EmptyStatement)
@@ -1484,47 +1484,52 @@ function OutputStream(options) {
/* -----[ source map generators ]----- */ /* -----[ source map generators ]----- */
function DEFMAP(nodetype, generator) { function DEFMAP(nodetype, generator) {
nodetype.DEFMETHOD("add_source_map", function(stream){ nodetype.forEach(function(nodetype) {
generator(this, stream); nodetype.DEFMETHOD("add_source_map", generator);
}); });
}; }
// We could easily add info for ALL nodes, but it seems to me that DEFMAP([
// would be quite wasteful, hence this noop in the base class. // We could easily add info for ALL nodes, but it seems to me that
DEFMAP(AST_Node, noop); // would be quite wasteful, hence this noop in the base class.
AST_Node,
function basic_sourcemap_gen(self, output) { // since the label symbol will mark it
output.add_mapping(self.start); AST_LabeledStatement,
}; AST_Toplevel,
], noop);
// XXX: I'm not exactly sure if we need it for all of these nodes, // XXX: I'm not exactly sure if we need it for all of these nodes,
// or if we should add even more. // or if we should add even more.
DEFMAP([
DEFMAP(AST_Directive, basic_sourcemap_gen); AST_Array,
DEFMAP(AST_Debugger, basic_sourcemap_gen); AST_BlockStatement,
DEFMAP(AST_Symbol, basic_sourcemap_gen); AST_Catch,
DEFMAP(AST_Jump, basic_sourcemap_gen); AST_Constant,
DEFMAP(AST_StatementWithBody, basic_sourcemap_gen); AST_Debugger,
DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it AST_Definitions,
DEFMAP(AST_Lambda, basic_sourcemap_gen); AST_Directive,
DEFMAP(AST_Switch, basic_sourcemap_gen); AST_Finally,
DEFMAP(AST_SwitchBranch, basic_sourcemap_gen); AST_Jump,
DEFMAP(AST_BlockStatement, basic_sourcemap_gen); AST_Lambda,
DEFMAP(AST_Toplevel, noop); AST_New,
DEFMAP(AST_New, basic_sourcemap_gen); AST_Object,
DEFMAP(AST_Try, basic_sourcemap_gen); AST_StatementWithBody,
DEFMAP(AST_Catch, basic_sourcemap_gen); AST_Symbol,
DEFMAP(AST_Finally, basic_sourcemap_gen); AST_Switch,
DEFMAP(AST_Definitions, basic_sourcemap_gen); AST_SwitchBranch,
DEFMAP(AST_Constant, basic_sourcemap_gen); AST_Try,
DEFMAP(AST_ObjectSetter, function(self, output){ ], function(output) {
output.add_mapping(self.start, self.key.name); output.add_mapping(this.start);
});
DEFMAP(AST_ObjectGetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectProperty, function(self, output){
output.add_mapping(self.start, self.key);
}); });
DEFMAP([
AST_ObjectGetter,
AST_ObjectSetter,
], function(output) {
output.add_mapping(this.start, this.key.name);
});
DEFMAP([ AST_ObjectProperty ], function(output) {
output.add_mapping(this.start, this.key);
});
})(); })();

View File

@@ -969,7 +969,9 @@ function parse($TEXT, options) {
function labeled_statement() { function labeled_statement() {
var label = as_symbol(AST_Label); var label = as_symbol(AST_Label);
if (find_if(function(l){ return l.name == label.name }, S.labels)) { if (!all(S.labels, function(l) {
return l.name != label.name;
})) {
// ECMA-262, 12.12: An ECMAScript program is considered // ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a // syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a // LabelledStatement that is enclosed by a

View File

@@ -100,7 +100,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
var self = this; var self = this;
var scope = self.parent_scope = null; var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null; var defun = null;
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Catch) { if (node instanceof AST_Catch) {
@@ -115,25 +114,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.init_scope_vars(scope); node.init_scope_vars(scope);
var save_scope = scope; var save_scope = scope;
var save_defun = defun; var save_defun = defun;
var save_labels = labels;
defun = scope = node; defun = scope = node;
labels = new Dictionary();
descend(); descend();
scope = save_scope; scope = save_scope;
defun = save_defun; defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) { if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) for (var s = scope; s; s = s.parent_scope)
s.uses_with = true; s.uses_with = true;
@@ -171,15 +157,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
else if (node instanceof AST_SymbolCatch) { else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun; scope.def_variable(node).defun = defun;
} }
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
}); });
self.walk(tw); self.walk(tw);
@@ -457,16 +434,19 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var redef = def.redefined(); var redef = def.redefined();
if (redef) { if (redef) {
redefined.push(def); redefined.push(def);
def.references.forEach(function(ref) { reference(node.argname);
ref.thedef = redef; def.references.forEach(reference);
ref.reference(options);
ref.thedef = def;
});
} }
descend(); descend();
if (!redef) mangle(def); if (!redef) mangle(def);
return true; return true;
} }
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
}); });
this.walk(tw); this.walk(tw);
redefined.forEach(mangle); redefined.forEach(mangle);

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.3.15", "version": "3.3.17",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -4976,7 +4976,7 @@ collapse_rhs_array: {
expect_stdout: "false false false" expect_stdout: "false false false"
} }
collapse_rhs_boolean: { collapse_rhs_boolean_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
} }
@@ -5001,6 +5001,66 @@ collapse_rhs_boolean: {
expect_stdout: "true true true" expect_stdout: "true true true"
} }
collapse_rhs_boolean_2: {
options = {
collapse_vars: true,
}
input: {
var a;
(function f1() {
a = function() {};
if (/foo/)
console.log(typeof a);
})();
console.log(function f2() {
a = [];
return !1;
}());
}
expect: {
var a;
(function f1() {
if (a = function() {})
console.log(typeof a);
})();
console.log(function f2() {
return !(a = []);
}());
}
expect_stdout: [
"function",
"false",
]
}
collapse_rhs_boolean_3: {
options = {
booleans: true,
collapse_vars: true,
conditionals: true,
}
input: {
var a, f, g, h, i, n, s, t, x, y;
if (x()) {
n = a;
} else if (y()) {
n = f();
} else if (s) {
i = false;
n = g(true);
} else if (t) {
i = false;
n = h(true);
} else {
n = [];
}
}
expect: {
var a, f, g, h, i, n, s, t, x, y;
n = x() ? a : y() ? f() : s ? g(!(i = !1)) : t ? h(!(i = !1)) : [];
}
}
collapse_rhs_function: { collapse_rhs_function: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -5243,3 +5303,27 @@ issue_2974: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_3032: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
console.log({
f: function() {
this.a = 42;
return [ this.a, !1 ];
}
}.f()[0]);
}
expect: {
console.log({
f: function() {
this.a = 42;
return [ this.a, !1 ];
}
}.f()[0]);
}
expect_stdout: "42"
}

View File

@@ -703,10 +703,11 @@ ternary_boolean_alternative: {
trivial_boolean_ternary_expressions : { trivial_boolean_ternary_expressions : {
options = { options = {
booleans: true,
conditionals: true, conditionals: true,
evaluate : true, evaluate: true,
booleans : true side_effects: true,
}; }
input: { input: {
f('foo' in m ? true : false); f('foo' in m ? true : false);
f('foo' in m ? false : true); f('foo' in m ? false : true);

View File

@@ -745,7 +745,7 @@ in_boolean_context: {
!b("foo"), !b("foo"),
!b([1, 2]), !b([1, 2]),
!b(/foo/), !b(/foo/),
![1, foo()], (foo(), !1),
(foo(), !1) (foo(), !1)
); );
} }
@@ -1566,3 +1566,43 @@ issue_2968: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
truthy_conditionals: {
options = {
conditionals: true,
evaluate: true,
}
input: {
if (a = {}) x();
(b = /foo/) && y();
(c = function() {}) || z();
}
expect: {
a = {}, x();
b = /foo/, y();
c = function() {};
}
}
truthy_loops: {
options = {
evaluate: true,
loops: true,
}
input: {
while ([]) x();
do {
y();
} while(a = {});
}
expect: {
for (;;) {
[];
x();
}
for (;;) {
y();
a = {};
}
}
}

View File

@@ -2051,3 +2051,189 @@ drop_lone_use_strict: {
} }
} }
} }
issue_3016_1: {
options = {
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
(function(a) {
return a[b];
var a;
})(3);
} while (0);
console.log(b);
}
expect: {
var b = 1;
do {
a = 3,
a[b];
} while(0);
var a;
console.log(b);
}
expect_stdout: "1"
}
issue_3016_2: {
options = {
dead_code: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
(function(a) {
return a[b];
try {
a = 2;
} catch (a) {
var a;
}
})(3);
} while (0);
console.log(b);
}
expect: {
var b = 1;
do {
a = 3,
a[b];
} while(0);
var a;
console.log(b);
}
expect_stdout: "1"
}
issue_3016_2_ie8: {
options = {
dead_code: true,
ie8: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
(function(a) {
return a[b];
try {
a = 2;
} catch (a) {
var a;
}
})(3);
} while (0);
console.log(b);
}
expect: {
var b = 1;
do {
a = 3,
a[b];
} while(0);
var a;
console.log(b);
}
expect_stdout: "1"
}
issue_3016_3: {
options = {
dead_code: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
console.log(function() {
return a ? "FAIL" : a = "PASS";
try {
a = 2;
} catch (a) {
var a;
}
}());
} while (b--);
}
expect: {
var b = 1;
do {
console.log((a = void 0, a ? "FAIL" : a = "PASS"));
} while(b--);
var a;
}
expect_stdout: [
"PASS",
"PASS",
]
}
issue_3016_3_ie8: {
options = {
dead_code: true,
ie8: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
console.log(function() {
return a ? "FAIL" : a = "PASS";
try {
a = 2;
} catch (a) {
var a;
}
}());
} while (b--);
}
expect: {
var b = 1;
do {
console.log((a = void 0, a ? "FAIL" : a = "PASS"));
} while(b--);
var a;
}
expect_stdout: [
"PASS",
"PASS",
]
}
issue_3018: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 1, c = "PASS";
do {
(function() {
(function(a) {
a = 0 != (a && (c = "FAIL"));
})();
})();
} while (b--);
console.log(c);
}
expect: {
var b = 1, c = "PASS";
do {
a = void 0,
a = 0 != (a && (c = "FAIL"));
} while (b--);
var a;
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -686,3 +686,33 @@ undefined_key: {
} }
expect_stdout: "3" expect_stdout: "3"
} }
issue_3021: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
var a = 1, b = 2;
(function() {
b = a;
if (a++ + b--)
return 1;
return;
var b = {};
})();
console.log(a, b);
}
expect: {
var a = 1, b = 2;
(function() {
b = a;
if (a++ + b--)
return 1;
return;
var b = {};
})();
console.log(a, b);
}
expect_stdout: "2 2"
}

View File

@@ -464,3 +464,77 @@ issue_2976_2: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3035: {
mangle = {
ie8: false,
}
input: {
var c = "FAIL";
(function(a) {
try {
throw 1;
} catch (b) {
try {
throw 0;
} catch (a) {
b && (c = "PASS");
}
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function(o) {
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (o) {
t && (c = "PASS");
}
}
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3035_ie8: {
mangle = {
ie8: true,
}
input: {
var c = "FAIL";
(function(a) {
try {
throw 1;
} catch (b) {
try {
throw 0;
} catch (a) {
b && (c = "PASS");
}
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function(t) {
try {
throw 1;
} catch (o) {
try {
throw 0;
} catch (t) {
o && (c = "PASS");
}
}
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -175,8 +175,8 @@ should_warn: {
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]",
"WARN: Condition always true [test/compress/issue-1261.js:141,8]", "WARN: Condition always true [test/compress/issue-1261.js:141,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Condition always true [test/compress/issue-1261.js:143,8]", "WARN: Condition always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]",
"WARN: Condition always false [test/compress/issue-1261.js:144,8]", "WARN: Condition always false [test/compress/issue-1261.js:144,8]",
] ]

View File

@@ -294,10 +294,10 @@ issue_186_beautify_ie8: {
] ]
} }
issue_186_bracketize: { issue_186_braces: {
beautify = { beautify = {
beautify: false, beautify: false,
bracketize: true, braces: true,
ie8: false, ie8: false,
} }
input: { input: {
@@ -314,10 +314,10 @@ issue_186_bracketize: {
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}' expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
} }
issue_186_bracketize_ie8: { issue_186_braces_ie8: {
beautify = { beautify = {
beautify: false, beautify: false,
bracketize: true, braces: true,
ie8: true, ie8: true,
} }
input: { input: {
@@ -334,10 +334,10 @@ issue_186_bracketize_ie8: {
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}' expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
} }
issue_186_beautify_bracketize: { issue_186_beautify_braces: {
beautify = { beautify = {
beautify: true, beautify: true,
bracketize: true, braces: true,
ie8: false, ie8: false,
} }
input: { input: {
@@ -366,10 +366,10 @@ issue_186_beautify_bracketize: {
] ]
} }
issue_186_beautify_bracketize_ie8: { issue_186_beautify_braces_ie8: {
beautify = { beautify = {
beautify: true, beautify: true,
bracketize: true, braces: true,
ie8: true, ie8: true,
} }
input: { input: {

View File

@@ -67,7 +67,7 @@ negate_iife_3_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false); (function(){ return true })() ? console.log(true) : console.log(false);
} }
expect: { expect: {
console.log(true); true, console.log(true);
} }
expect_stdout: true expect_stdout: true
} }
@@ -110,7 +110,7 @@ negate_iife_3_off_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false); (function(){ return true })() ? console.log(true) : console.log(false);
} }
expect: { expect: {
console.log(true); true, console.log(true);
} }
expect_stdout: true expect_stdout: true
} }

View File

@@ -55,7 +55,7 @@ reduce_vars: {
console.log(a - 5); console.log(a - 5);
eval("console.log(a);"); eval("console.log(a);");
})(eval); })(eval);
"yes"; true, "yes";
console.log(A + 1); console.log(A + 1);
} }
expect_stdout: true expect_stdout: true
@@ -147,7 +147,7 @@ modified: {
} }
function f4() { function f4() {
var b = 2, c = 3; var b = 2, c = 3;
b = c; 1, b = c;
console.log(1 + b); console.log(1 + b);
console.log(b + c); console.log(b + c);
console.log(1 + c); console.log(1 + c);
@@ -715,10 +715,12 @@ passes: {
passes: 2, passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
sequences: true,
side_effects: true,
unused: true, unused: true,
} }
input: { input: {
function f() { (function() {
var a = 1, b = 2, c = 3; var a = 1, b = 2, c = 3;
if (a) { if (a) {
b = c; b = c;
@@ -729,17 +731,22 @@ passes: {
console.log(b + c); console.log(b + c);
console.log(a + c); console.log(a + c);
console.log(a + b + c); console.log(a + b + c);
} })();
} }
expect: { expect: {
function f() { (function() {
3; console.log(4),
console.log(4); console.log(6),
console.log(6); console.log(4),
console.log(4);
console.log(7); console.log(7);
} })();
} }
expect_stdout: [
"4",
"6",
"4",
"7",
]
} }
iife: { iife: {

View File

@@ -59,7 +59,7 @@ if_else_empty: {
if ({} ? a : b); else {} if ({} ? a : b); else {}
} }
expect: { expect: {
!{} ? b : a; ({}), a;
} }
} }

View File

@@ -164,13 +164,13 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should work with `--beautify bracketize`", function (done) { it("Should work with `--beautify braces`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize'; var command = uglifyjscmd + ' test/input/issue-1482/input.js -b braces';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js")); assert.strictEqual(stdout, read("test/input/issue-1482/braces.js"));
done(); done();
}); });
}); });

View File

@@ -139,7 +139,7 @@ describe("Comment", function() {
assert.strictEqual(result.code, code); assert.strictEqual(result.code, code);
}); });
it("Should retain comments within brackets", function() { it("Should retain comments within braces", function() {
var code = [ var code = [
"{/* foo */}", "{/* foo */}",
"a({/* foo */});", "a({/* foo */});",

View File

@@ -186,99 +186,6 @@ describe("minify", function() {
}); });
}); });
describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), {
sourceMap: {
content: read("./test/input/issue-1236/simple.js.map"),
filename: "simple.min.js",
includeSources: true
}
});
var map = JSON.parse(result.map);
assert.equal(map.file, 'simple.min.js');
assert.equal(map.sourcesContent.length, 1);
assert.equal(map.sourcesContent[0],
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
});
it("Should process inline source map", function() {
var code = Uglify.minify(read("./test/input/issue-520/input.js"), {
compress: { toplevel: true },
sourceMap: {
content: "inline",
url: "inline"
}
}).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
});
it("Should warn for missing inline source map", function() {
var warn_function = Uglify.AST_Node.warn_function;
var warnings = [];
Uglify.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), {
mangle: false,
sourceMap: {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
Uglify.AST_Node.warn_function = warn_function;
}
});
it("Should fail with multiple input and inline source map", function() {
var result = Uglify.minify([
read("./test/input/issue-520/input.js"),
read("./test/input/issue-520/output.js")
], {
sourceMap: {
content: "inline",
url: "inline"
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
});
});
describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', {
sourceMap: {
url: "inline"
}
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
});
describe("#__PURE__", function() { describe("#__PURE__", function() {
it("should drop #__PURE__ hint after use", function() { it("should drop #__PURE__ hint after use", function() {
var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', { var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', {
@@ -312,6 +219,15 @@ describe("minify", function() {
assert.strictEqual(err.line, 1); assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12); assert.strictEqual(err.col, 12);
}); });
it("should reject duplicated label name", function() {
var result = Uglify.minify("L:{L:{}}");
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Label L defined twice");
assert.strictEqual(err.filename, "0");
assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 4);
});
}); });
describe("global_defs", function() { describe("global_defs", function() {

View File

@@ -17,7 +17,7 @@ describe("test/benchmark.js", function() {
this.timeout(10 * 60 * 1000); this.timeout(10 * 60 * 1000);
[ [
"-b", "-b",
"-b bracketize", "-b braces",
"-m", "-m",
"-mc passes=3", "-mc passes=3",
"-mc passes=3,toplevel", "-mc passes=3,toplevel",

143
test/mocha/sourcemaps.js Normal file
View File

@@ -0,0 +1,143 @@
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var Uglify = require("../../");
function read(path) {
return readFileSync(path, "utf8");
}
function source_map(code) {
return JSON.parse(Uglify.minify(code, {
compress: false,
mangle: false,
sourceMap: true,
}).map);
}
describe("sourcemaps", function() {
it("Should give correct version", function() {
var map = source_map("var x = 1 + 1;");
assert.strictEqual(map.version, 3);
assert.deepEqual(map.names, [ "x" ]);
});
it("Should give correct names", function() {
var map = source_map([
"({",
" get enabled() {",
" return 3;",
" },",
" set enabled(x) {",
" ;",
" }",
"});",
].join("\n"));
assert.deepEqual(map.names, [ "enabled", "x" ]);
});
it("Should mark array/object literals", function() {
var result = Uglify.minify([
"var obj = {};",
"obj.wat([]);",
].join("\n"), {
sourceMap: true,
toplevel: true,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "({}).wat([]);");
assert.strictEqual(result.map, '{"version":3,"sources":["0"],"names":["wat"],"mappings":"CAAU,IACNA,IAAI"}');
});
describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), {
sourceMap: {
content: read("./test/input/issue-1236/simple.js.map"),
filename: "simple.min.js",
includeSources: true
}
});
var map = JSON.parse(result.map);
assert.equal(map.file, 'simple.min.js');
assert.equal(map.sourcesContent.length, 1);
assert.equal(map.sourcesContent[0],
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
});
it("Should process inline source map", function() {
var code = Uglify.minify(read("./test/input/issue-520/input.js"), {
compress: { toplevel: true },
sourceMap: {
content: "inline",
url: "inline"
}
}).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
});
it("Should warn for missing inline source map", function() {
var warn_function = Uglify.AST_Node.warn_function;
var warnings = [];
Uglify.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), {
mangle: false,
sourceMap: {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
Uglify.AST_Node.warn_function = warn_function;
}
});
it("Should fail with multiple input and inline source map", function() {
var result = Uglify.minify([
read("./test/input/issue-520/input.js"),
read("./test/input/issue-520/output.js")
], {
sourceMap: {
content: "inline",
url: "inline"
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
});
});
describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', {
sourceMap: {
url: "inline"
}
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
});
});

View File

@@ -23,6 +23,15 @@ describe("spidermonkey export/import sanity test", function() {
}); });
}); });
it("should not add unnecessary escape slashes to regexps", function() {
var input = "/[\\\\/]/;";
var ast = uglify.parse(input).to_mozilla_ast();
assert.equal(
uglify.AST_Node.from_mozilla_ast(ast).print_to_string(),
input
);
});
it("Should judge between directives and strings correctly on import", function() { it("Should judge between directives and strings correctly on import", function() {
var tests = [ var tests = [
{ {

View File

@@ -11,7 +11,7 @@ function try_beautify(code) {
mangle: false, mangle: false,
output: { output: {
beautify: true, beautify: true,
bracketize: true braces: true
} }
}); });
if (beautified.error) { if (beautified.error) {

View File

@@ -22,9 +22,6 @@ if (failures) {
var mocha_tests = require("./mocha.js"); var mocha_tests = require("./mocha.js");
mocha_tests(); mocha_tests();
var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests();
/* -----[ utils ]----- */ /* -----[ utils ]----- */
function tmpl() { function tmpl() {
@@ -49,16 +46,9 @@ function log_test(name) {
} }
function find_test_files(dir) { function find_test_files(dir) {
var files = fs.readdirSync(dir).filter(function(name){ return fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name); return /\.js$/i.test(name);
}); });
if (process.argv.length > 2) {
var x = process.argv.slice(2);
files = files.filter(function(f){
return x.indexOf(f) >= 0;
});
}
return files;
} }
function test_directory(dir) { function test_directory(dir) {

View File

@@ -1,40 +0,0 @@
var UglifyJS = require("..");
var ok = require("assert");
module.exports = function () {
console.log("--- Sourcemaps tests");
var basic = source_map([
'var x = 1 + 1;'
].join('\n'));
ok.equal(basic.version, 3);
ok.deepEqual(basic.names, ['x']);
var issue836 = source_map([
"({",
" get enabled() {",
" return 3;",
" },",
" set enabled(x) {",
" ;",
" }",
"});",
].join("\n"));
ok.deepEqual(issue836.names, ['enabled', 'x']);
}
function source_map(js) {
return JSON.parse(UglifyJS.minify(js, {
compress: false,
mangle: false,
sourceMap: true
}).map);
}
// Run standalone
if (module.parent === null) {
module.exports();
}

View File

@@ -975,7 +975,7 @@ function try_beautify(code, result, printfn) {
mangle: false, mangle: false,
output: { output: {
beautify: true, beautify: true,
bracketize: true, braces: true,
}, },
}); });
if (beautified.error) { if (beautified.error) {

View File

@@ -4,7 +4,7 @@
"mangle": false, "mangle": false,
"output": { "output": {
"beautify": true, "beautify": true,
"bracketize": true "braces": true
}, },
"rename": true "rename": true
}, },