Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6982a0554c | ||
|
|
fa3250199a | ||
|
|
06b9894c19 | ||
|
|
9f9db504d7 | ||
|
|
82ae95c334 | ||
|
|
9a5e2052c4 | ||
|
|
b1410be443 | ||
|
|
12985d86c2 | ||
|
|
49bfc6b555 | ||
|
|
d1c6bb8c7c | ||
|
|
5c169615a8 | ||
|
|
73d77f4f64 | ||
|
|
ccf0e2ef4f | ||
|
|
20ca0f5906 | ||
|
|
b29d435bb5 |
@@ -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.
|
||||||
|
|
||||||
|
|||||||
22
lib/ast.js
22
lib/ast.js
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
221
lib/compress.js
221
lib/compress.js
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
107
lib/output.js
107
lib/output.js
@@ -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);
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
36
lib/scope.js
36
lib/scope.js
@@ -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);
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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]",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ if_else_empty: {
|
|||||||
if ({} ? a : b); else {}
|
if ({} ? a : b); else {}
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
!{} ? b : a;
|
({}), a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 */});",
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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
143
test/mocha/sourcemaps.js
Normal 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"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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 = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"mangle": false,
|
"mangle": false,
|
||||||
"output": {
|
"output": {
|
||||||
"beautify": true,
|
"beautify": true,
|
||||||
"bracketize": true
|
"braces": true
|
||||||
},
|
},
|
||||||
"rename": true
|
"rename": true
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user