Compare commits

...

27 Commits

Author SHA1 Message Date
Alex Lam S.L
36a430cd1e v3.7.6 2020-01-19 11:02:58 +00:00
Alex Lam S.L
41a6eb892a fix corner case in evaluate (#3685)
fixes #3684
2020-01-16 01:51:37 +08:00
Alex Lam S.L
91d87ae663 fix corner case in unsafe_math (#3683)
fixes #3682
2020-01-15 04:05:58 +08:00
Alex Lam S.L
5beb7e4797 v3.7.5 2020-01-12 11:12:11 +08:00
Alex Lam S.L
46caaa82ba enhance collapse_vars (#3680)
closes #3679
2020-01-10 04:28:43 +08:00
Alex Lam S.L
5d258259a4 introduce --output-opts CLI option (#3678)
closes #3675
2020-01-08 20:44:03 +08:00
Alex Lam S.L
14c35739dd fix corner case in unsafe_math (#3677)
fixes #3676
2020-01-08 10:28:10 +08:00
Alex Lam S.L
f5ceff6e4b fix corner case in unused (#3674)
fixes #3673
2020-01-07 20:06:25 +08:00
Alex Lam S.L
4d6771b9b1 fix corner case in collapse_vars (#3672)
fixes #3671
2020-01-07 19:34:16 +08:00
Alex Lam S.L
d17191111a v3.7.4 2020-01-07 07:59:54 +08:00
Alex Lam S.L
0ff607cb80 improve ufuzz false positive detection (#3670) 2020-01-06 11:26:15 +08:00
Alex Lam S.L
1988495d71 fix corner case in conditionals (#3669)
fixes #3668
2020-01-04 09:24:28 +08:00
Alex Lam S.L
fdc10086da fix corner case in reduce_vars (#3667)
fixes #3666
2020-01-03 19:28:47 +08:00
Alex Lam S.L
746f5f6c62 fix corner case in unused (#3665)
fixes #3664
2020-01-01 20:24:30 +08:00
Alex Lam S.L
d83d3d741a enhance unused (#3662) 2019-12-31 23:39:24 +08:00
Alex Lam S.L
99ac73a635 enhance booleans (#3661) 2019-12-31 13:10:05 +08:00
Alex Lam S.L
a2e4c2fd97 enhance evaluate (#3660) 2019-12-31 11:51:21 +08:00
Alex Lam S.L
94785e8e14 fix corner case in booleans (#3659)
fixes #3658
2019-12-31 09:57:35 +08:00
Alex Lam S.L
4dbdac9c31 enhance booleans (#3657) 2019-12-30 22:41:11 +08:00
Alex Lam S.L
78c8efd851 fix corner case in evaluate (#3656)
fixes #3655
2019-12-29 21:16:53 +08:00
Alex Lam S.L
af310ba2d0 fix corner case in evaluate (#3654)
fixes #3653
2019-12-29 02:50:57 +00:00
Alex Lam S.L
2f3930d1b9 fix corner case in collapse_vars (#3652)
fixes #3651
2019-12-29 00:57:59 +00:00
Alex Lam S.L
d1a78920d9 workaround firefox asm.js quirks (#3650)
fixes #3636
2019-12-28 23:14:53 +00:00
Alex Lam S.L
d9cd3d33c8 enhance evaluate (#3649) 2019-12-28 20:26:15 +00:00
Alex Lam S.L
22b47cdd63 improve unicode handling (#3648) 2019-12-28 18:06:51 +00:00
Alex Lam S.L
4cf612dc9f increase mocha default timeout (#3647)
closes #3640
2019-12-28 02:32:22 +00:00
Alex Lam S.L
a19d31dd33 fix corner case in unsafe (#3646) 2019-12-27 14:24:54 +00:00
27 changed files with 1513 additions and 444 deletions

View File

@@ -87,6 +87,7 @@ a double dash to prevent input files being used as option arguments:
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may
want to disable `negate_iife` under
compressor options.
-O, --output-opts [options] Specify output options (`beautify` disabled by default).
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
`spidermonkey` to write UglifyJS or SpiderMonkey AST
as JSON to STDOUT respectively.

View File

@@ -36,6 +36,7 @@ program.option("-c, --compress [options]", "Enable compressor/specify compressor
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
program.option("-O, --output-opts [options]", "Output options (beautify disabled).", parse_js());
program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file.");
@@ -59,7 +60,7 @@ if (program.configFile) {
if (options.mangle && options.mangle.properties && options.mangle.properties.regex) {
options.mangle.properties.regex = UglifyJS.parse(options.mangle.properties.regex, {
expression: true
}).getValue();
}).value;
}
}
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
@@ -93,6 +94,10 @@ if (program.beautify) {
options.output.beautify = true;
}
}
if (program.outputOpts) {
if (program.beautify) fatal("--beautify cannot be used with --output-opts");
options.output = typeof program.outputOpts == "object" ? program.outputOpts : {};
}
if (program.comments) {
if (typeof options.output != "object") options.output = {};
options.output.comments = typeof program.comments == "string" ? program.comments : "some";
@@ -370,7 +375,7 @@ function parse_js(flag) {
if (!(node instanceof UglifyJS.AST_Sequence)) throw node;
function to_string(value) {
return value instanceof UglifyJS.AST_Constant ? value.getValue() : value.print_to_string({
return value instanceof UglifyJS.AST_Constant ? value.value : value.print_to_string({
quote_keys: true
});
}

View File

@@ -618,7 +618,7 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
getProperty: function() {
var p = this.property;
if (p instanceof AST_Constant) {
return p.getValue();
return p.value;
}
if (p instanceof AST_UnaryPrefix
&& p.operator == "void"
@@ -824,9 +824,6 @@ var AST_This = DEFNODE("This", null, {
var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants",
getValue: function() {
return this.value;
}
});
var AST_String = DEFNODE("String", "value quote", {
@@ -967,11 +964,13 @@ TreeWalker.prototype = {
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
if (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_Return && p.in_bool
|| p instanceof AST_Sequence && p.tail_node() !== self
|| p instanceof AST_SimpleStatement
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}

View File

@@ -209,6 +209,7 @@ merge(Compressor.prototype, {
if (is_scope) {
node.hoist_properties(this);
node.hoist_declarations(this);
node.process_boolean_returns(this);
}
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
@@ -241,7 +242,7 @@ merge(Compressor.prototype, {
return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
});
AST_Scope.DEFMETHOD("process_expression", function(insert, compressor) {
AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
var self = this;
var tt = new TreeTransformer(function(node) {
if (insert && node instanceof AST_SimpleStatement) {
@@ -250,13 +251,7 @@ merge(Compressor.prototype, {
});
}
if (!insert && node instanceof AST_Return) {
if (compressor) {
var value = node.value && node.value.drop_side_effect_free(compressor, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
}
return make_node(AST_SimpleStatement, node, {
return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_UnaryPrefix, node, {
operator: "void",
expression: make_node(AST_Number, node, {
@@ -361,6 +356,7 @@ merge(Compressor.prototype, {
function reset_def(tw, compressor, def) {
def.assignments = 0;
def.bool_fn = 0;
def.chained = false;
def.cross_loop = false;
def.direct_access = false;
@@ -596,14 +592,22 @@ merge(Compressor.prototype, {
def(AST_Call, function(tw, descend) {
tw.find_parent(AST_Scope).may_call_this();
var exp = this.expression;
if (!(exp instanceof AST_SymbolRef)) return;
var def = exp.definition();
if (!(def.fixed instanceof AST_Defun)) return;
var defun = mark_defun(tw, def);
if (!defun) return;
descend();
defun.walk(tw);
return true;
if (exp instanceof AST_SymbolRef) {
var def = exp.definition();
if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
if (!(def.fixed instanceof AST_Defun)) return;
var defun = mark_defun(tw, def);
if (!defun) return;
descend();
defun.walk(tw);
return true;
} else if (this.TYPE == "Call"
&& exp instanceof AST_Assign
&& exp.operator == "="
&& exp.left instanceof AST_SymbolRef
&& tw.in_boolean_context()) {
exp.left.definition().bool_fn++;
}
});
def(AST_Case, function(tw) {
push(tw);
@@ -1352,7 +1356,10 @@ merge(Compressor.prototype, {
return node.operator != "=" && lhs.equivalent_to(node.left);
}
if (node instanceof AST_Call) {
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
if (!(lhs instanceof AST_PropAccess)) return false;
if (!lhs.equivalent_to(node.expression)) return false;
var rhs = get_rvalue(candidate);
return !(rhs instanceof AST_Function && !rhs.contains_this());
}
if (node instanceof AST_Debugger) return true;
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
@@ -1363,7 +1370,7 @@ merge(Compressor.prototype, {
if (replace_all) return false;
return node instanceof AST_SymbolRef
&& !node.is_declared(compressor)
&& !(parent instanceof AST_Assign && parent.left === node);
&& !(parent instanceof AST_Assign && parent.operator == "=" && parent.left === node);
}
function in_conditional(node, parent) {
@@ -2017,9 +2024,10 @@ merge(Compressor.prototype, {
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
// if (foo()) return; return; => foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
@@ -2029,8 +2037,8 @@ merge(Compressor.prototype, {
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
// if (foo()) return x; return y; => return foo() ? x : y;
if ((in_bool || value) && !stat.alternative && next instanceof AST_Return) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
@@ -2039,21 +2047,18 @@ merge(Compressor.prototype, {
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (value && !stat.alternative
&& (!next && in_lambda && multiple_if_returns
|| next instanceof AST_Return)) {
// if (foo()) return x; [ return ; ] => return foo() ? x : undefined;
if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
stat.alternative = make_node(AST_Return, stat, {
value: null
});
statements.splice(i, 1, stat.transform(compressor));
if (next) statements.splice(j, 1);
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
// if (a) return b; if (c) return d; e; => return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
@@ -2480,6 +2485,61 @@ merge(Compressor.prototype, {
node.DEFMETHOD("is_truthy", func);
});
// is_negative_zero()
// return true if the node may represent -0
(function(def) {
def(AST_Node, return_true);
def(AST_Array, return_false);
function binary(op, left, right) {
switch (op) {
case "-":
return left.is_negative_zero()
&& (!(right instanceof AST_Constant) || right.value == 0);
case "&&":
case "||":
return left.is_negative_zero() || right.is_negative_zero();
case "*":
case "/":
return true;
case "%":
return left.is_negative_zero();
default:
return false;
}
}
def(AST_Assign, function() {
var op = this.operator;
if (op == "=") return this.right.is_negative_zero();
return binary(op.slice(0, -1), this.left, this.right);
});
def(AST_Binary, function() {
return binary(this.operator, this.left, this.right);
});
def(AST_Constant, function() {
return this.value == 0 && 1 / this.value < 0;
});
def(AST_Lambda, return_false);
def(AST_Object, return_false);
def(AST_RegExp, return_false);
def(AST_Sequence, function() {
return this.tail_node().is_negative_zero();
});
def(AST_SymbolRef, function() {
var fixed = this.fixed_value();
if (!fixed) return true;
this.is_negative_zero = return_true;
var result = fixed.is_negative_zero();
delete this.is_negative_zero;
return result;
});
def(AST_UnaryPrefix, function() {
return this.operator == "+" && this.expression.is_negative_zero()
|| this.operator == "-";
});
})(function(node, func) {
node.DEFMETHOD("is_negative_zero", func);
});
// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
@@ -2872,9 +2932,11 @@ merge(Compressor.prototype, {
}
function convert_to_predicate(obj) {
var map = Object.create(null);
Object.keys(obj).forEach(function(key) {
obj[key] = makePredicate(obj[key]);
map[key] = makePredicate(obj[key]);
});
return map;
}
AST_Lambda.DEFMETHOD("first_statement", function() {
@@ -2897,7 +2959,7 @@ merge(Compressor.prototype, {
"toString",
"valueOf",
];
var native_fns = {
var native_fns = convert_to_predicate({
Array: [
"indexOf",
"join",
@@ -2933,9 +2995,8 @@ merge(Compressor.prototype, {
"toUpperCase",
"trim",
].concat(object_fns),
};
convert_to_predicate(native_fns);
var static_fns = {
});
var static_fns = convert_to_predicate({
Array: [
"isArray",
],
@@ -2975,8 +3036,7 @@ merge(Compressor.prototype, {
String: [
"fromCharCode",
],
};
convert_to_predicate(static_fns);
});
// methods to evaluate a constant expression
(function(def) {
@@ -3014,7 +3074,7 @@ merge(Compressor.prototype, {
def(AST_Lambda, return_this);
def(AST_Node, return_this);
def(AST_Constant, function() {
return this.getValue();
return this.value;
});
def(AST_Function, function(compressor) {
if (compressor.option("unsafe")) {
@@ -3199,7 +3259,7 @@ merge(Compressor.prototype, {
Object: Object,
String: String,
};
var static_values = {
var static_values = convert_to_predicate({
Math: [
"E",
"LN10",
@@ -3217,8 +3277,7 @@ merge(Compressor.prototype, {
"NEGATIVE_INFINITY",
"POSITIVE_INFINITY",
],
};
convert_to_predicate(static_values);
});
var regexp_props = makePredicate("global ignoreCase multiline source");
def(AST_PropAccess, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
@@ -3636,7 +3695,7 @@ merge(Compressor.prototype, {
return !this.is_declared(compressor);
});
def(AST_Try, function(compressor) {
return this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)
return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
|| this.bfinally && this.bfinally.may_throw(compressor);
});
def(AST_Unary, function(compressor) {
@@ -3878,7 +3937,7 @@ merge(Compressor.prototype, {
});
return true;
}
return scan_ref_scoped(node, descend);
return scan_ref_scoped(node, descend, true);
});
self.walk(tw);
// pass 2: for every used symbol we need to walk its
@@ -3912,6 +3971,11 @@ merge(Compressor.prototype, {
if (node instanceof AST_Assign) {
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
value = get_rhs(node);
if (node.write_only === true) {
value = value.drop_side_effect_free(compressor) || make_node(AST_Number, node, {
value: 0
});
}
}
} else if (!in_use) {
value = make_node(AST_Number, node, {
@@ -4181,7 +4245,7 @@ merge(Compressor.prototype, {
return rhs.right.has_side_effects(compressor) ? rhs : rhs.right;
}
function scan_ref_scoped(node, descend) {
function scan_ref_scoped(node, descend, init) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
props.forEach(function(prop) {
@@ -4190,7 +4254,11 @@ merge(Compressor.prototype, {
if (node instanceof AST_Assign) {
if (node.write_only === "p" && node.right.may_throw_on_access(compressor)) return;
var right = get_rhs(node);
right.walk(tw);
if (init && node.write_only === true && node_def.scope === self && !right.has_side_effects(compressor)) {
initializations.add(node_def.id, right);
} else {
right.walk(tw);
}
if (node.left === sym) {
if (!node_def.chained && sym.fixed_value(true) === right) {
fixed_ids[node_def.id] = node;
@@ -4349,6 +4417,96 @@ merge(Compressor.prototype, {
self.body = dirs.concat(hoisted, self.body);
});
function scan_local_returns(fn, transform) {
fn.walk(new TreeWalker(function(node) {
if (node instanceof AST_Return) {
transform(node);
return true;
}
if (node instanceof AST_Scope && node !== fn) return true;
}));
}
function map_bool_returns(fn) {
var map = Object.create(null);
scan_local_returns(fn, function(node) {
var value = node.value;
if (value) value = value.tail_node();
if (value instanceof AST_SymbolRef) {
var id = value.definition().id;
map[id] = (map[id] || 0) + 1;
}
});
return map;
}
function all_bool(def, bool_returns, compressor) {
return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length
&& !compressor.exposed(def);
}
function process_boolean_returns(fn, compressor) {
scan_local_returns(fn, function(node) {
node.in_bool = true;
var value = node.value;
if (value) {
var ev = value.is_truthy() || value.tail_node().evaluate(compressor);
if (!ev) {
value = value.drop_side_effect_free(compressor);
if (node.value !== value) node.value = value ? make_sequence(node.value, [
value,
make_node(AST_Number, node.value, {
value: 0
})
]) : null;
} else if (ev && !(ev instanceof AST_Node)) {
value = value.drop_side_effect_free(compressor);
if (node.value !== value) node.value = value ? make_sequence(node.value, [
value,
make_node(AST_Number, node.value, {
value: 1
})
]) : make_node(AST_Number, node.value, {
value: 1
});
}
}
});
}
AST_Scope.DEFMETHOD("process_boolean_returns", noop);
AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
if (!compressor.option("booleans")) return;
var bool_returns = map_bool_returns(this);
if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
process_boolean_returns(this, compressor);
});
AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
if (!compressor.option("booleans")) return;
var bool_returns = map_bool_returns(this);
if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
var parent = compressor.parent();
if (parent instanceof AST_Assign) {
if (parent.operator != "=") return;
var sym = parent.left;
if (!(sym instanceof AST_SymbolRef)) return;
if (!all_bool(sym.definition(), bool_returns, compressor)) return;
} else if (parent instanceof AST_Call && parent.expression !== this) {
var exp = parent.expression;
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
if (!(exp instanceof AST_Lambda)) return;
if (exp.uses_arguments || exp.pinned()) return;
var sym = exp.argnames[parent.args.indexOf(this)];
if (sym && !all_bool(sym.definition(), bool_returns, compressor)) return;
} else if (parent.TYPE == "Call") {
compressor.pop();
var in_bool = compressor.in_boolean_context();
compressor.push(this);
if (!in_bool) return;
} else return;
process_boolean_returns(this, compressor);
});
AST_Scope.DEFMETHOD("var_names", function() {
var var_names = this._var_names;
if (!var_names) {
@@ -4561,7 +4719,12 @@ merge(Compressor.prototype, {
}
if (exp instanceof AST_Function && (!exp.name || !exp.name.definition().references.length)) {
var node = this.clone();
exp.process_expression(false, compressor);
exp.process_expression(false, function(node) {
var value = node.value && node.value.drop_side_effect_free(compressor, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
});
exp.walk(new TreeWalker(function(node) {
if (node instanceof AST_Return && node.value) {
node.value = node.value.drop_side_effect_free(compressor);
@@ -4878,7 +5041,7 @@ merge(Compressor.prototype, {
var sym = condition.right.expression;
if (!is_undeclared_ref(sym)) return;
var body;
var undef = condition.left.getValue() == "undefined";
var undef = condition.left.value == "undefined";
switch (condition.operator) {
case "==":
body = undef ? alternative : consequent;
@@ -5006,13 +5169,17 @@ merge(Compressor.prototype, {
if (self.body instanceof AST_Exit
&& self.alternative instanceof AST_Exit
&& self.body.TYPE == self.alternative.TYPE) {
return make_node(self.body.CTOR, self, {
var exit = make_node(self.body.CTOR, self, {
value: make_node(AST_Conditional, self, {
condition : self.condition,
consequent : self.body.value || make_node(AST_Undefined, self.body),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
}).transform(compressor)
}).optimize(compressor);
consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
})
});
if (exit instanceof AST_Return) {
exit.in_bool = self.body.in_bool || self.alternative.in_bool;
}
return exit;
}
if (self.body instanceof AST_If
&& !self.body.alternative
@@ -5314,7 +5481,7 @@ merge(Compressor.prototype, {
if (self.args.length == 1) {
var first = self.args[0];
if (first instanceof AST_Number) try {
var length = first.getValue();
var length = first.value;
if (length > 6) break;
var elements = Array(length);
for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
@@ -5927,7 +6094,7 @@ merge(Compressor.prototype, {
} else if (compressor.in_boolean_context()) switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context
// !!foo => foo, if we're in boolean context
return e.expression;
}
if (e instanceof AST_Binary) {
@@ -6072,7 +6239,7 @@ merge(Compressor.prototype, {
// "undefined" == typeof x => undefined === x
else if (compressor.option("typeofs")
&& self.left instanceof AST_String
&& self.left.getValue() == "undefined"
&& self.left.value == "undefined"
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "typeof") {
var expr = self.right.expression;
@@ -6144,7 +6311,7 @@ merge(Compressor.prototype, {
}
break;
case "==":
if (self.left instanceof AST_String && self.left.getValue() == "" && self.right.is_string(compressor)) {
if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: self.right
@@ -6152,7 +6319,7 @@ merge(Compressor.prototype, {
}
break;
case "!=":
if (self.left instanceof AST_String && self.left.getValue() == "" && self.right.is_string(compressor)) {
if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
return self.right.optimize(compressor);
}
break;
@@ -6173,19 +6340,19 @@ merge(Compressor.prototype, {
}
if (self.operator == "+") {
if (self.right instanceof AST_String
&& self.right.getValue() == ""
&& self.right.value == ""
&& self.left.is_string(compressor)) {
return self.left.optimize(compressor);
}
if (self.left instanceof AST_String
&& self.left.getValue() == ""
&& self.left.value == ""
&& self.right.is_string(compressor)) {
return self.right.optimize(compressor);
}
if (self.left instanceof AST_Binary
&& self.left.operator == "+"
&& self.left.left instanceof AST_String
&& self.left.left.getValue() == ""
&& self.left.left.value == ""
&& self.right.is_string(compressor)) {
self.left = self.left.right;
return self.optimize(compressor);
@@ -6218,9 +6385,9 @@ merge(Compressor.prototype, {
return self.left.optimize(compressor);
}
}
// x || false && y ---> x ? y : false
// (x || false) && y => x ? y : false
if (self.left.operator == "||") {
var lr = self.left.right.evaluate(compressor);
var lr = self.left.right.tail_node().evaluate(compressor);
if (!lr) return make_node(AST_Conditional, self, {
condition: self.left.left,
consequent: self.right,
@@ -6252,8 +6419,9 @@ merge(Compressor.prototype, {
]).optimize(compressor);
} else self.truthy = true;
}
// x && true || y => x ? true : y
if (self.left.operator == "&&") {
var lr = self.left.right.evaluate(compressor);
var lr = self.left.right.is_truthy() || self.left.right.tail_node().evaluate(compressor);
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
condition: self.left.left,
consequent: self.left.right,
@@ -6271,7 +6439,7 @@ merge(Compressor.prototype, {
self = make_node(AST_Binary, self, {
operator: "+",
left: make_node(AST_String, self.left, {
value: "" + self.left.getValue() + self.right.left.getValue(),
value: "" + self.left.value + self.right.left.value,
start: self.left.start,
end: self.right.left.end
}),
@@ -6288,7 +6456,7 @@ merge(Compressor.prototype, {
operator: "+",
left: self.left.left,
right: make_node(AST_String, self.right, {
value: "" + self.left.right.getValue() + self.right.getValue(),
value: "" + self.left.right.value + self.right.value,
start: self.left.right.start,
end: self.right.end
})
@@ -6309,7 +6477,7 @@ merge(Compressor.prototype, {
operator: "+",
left: self.left.left,
right: make_node(AST_String, self.left.right, {
value: "" + self.left.right.getValue() + self.right.left.getValue(),
value: "" + self.left.right.value + self.right.left.value,
start: self.left.right.start,
end: self.right.left.end
})
@@ -6392,8 +6560,8 @@ merge(Compressor.prototype, {
// a + +b => +b + a
if (self.operator != "-"
&& self.operator != "/"
&& self.left.is_number(compressor)
&& self.right.is_number(compressor)
&& (self.left.is_boolean(compressor) || self.left.is_number(compressor))
&& (self.right.is_boolean(compressor) || self.right.is_number(compressor))
&& reversible()
&& !(self.left instanceof AST_Binary
&& self.left.operator != self.operator
@@ -6418,7 +6586,10 @@ merge(Compressor.prototype, {
&& self.right.is_number(compressor)
&& (self.operator != "+"
|| self.right.left.is_boolean(compressor)
|| self.right.left.is_number(compressor))) {
|| self.right.left.is_number(compressor))
&& (self.operator != "-" || !self.left.is_negative_zero())
&& (self.right.left.is_constant_expression()
|| !self.right.right.has_side_effects(compressor))) {
self = make_node(AST_Binary, self, {
operator: align(self.operator, self.right.operator),
left: make_node(AST_Binary, self.left, {
@@ -6451,11 +6622,67 @@ merge(Compressor.prototype, {
self = make_binary(self, self.left.operator, lhs, self.left.right);
} else if (self.left.right instanceof AST_Constant) {
var rhs = make_binary(self.left, align(self.left.operator, self.operator), self.left.right, self.right, self.left.right.start, self.right.end);
self = make_binary(self, self.left.operator, self.left.left, rhs);
if (self.left.operator != "-"
|| !self.right.value
|| rhs.evaluate(compressor)
|| !self.left.left.is_negative_zero()) {
self = make_binary(self, self.left.operator, self.left.left, rhs);
}
}
}
break;
}
if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
// 0 + n => n
case "+":
if (self.left.value == 0) {
if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
operator: "+",
expression: self.right
}).optimize(compressor);
if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
}
break;
// 1 * n => n
case "*":
if (self.left.value == 1) {
return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
operator: "+",
expression: self.right
}).optimize(compressor);
}
break;
}
if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
// n + 0 => n
case "+":
if (self.right.value == 0) {
if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
operator: "+",
expression: self.left
}).optimize(compressor);
if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
}
break;
// n - 0 => n
case "-":
if (self.right.value == 0) {
return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
operator: "+",
expression: self.left
}).optimize(compressor);
}
break;
// n / 1 => n
case "/":
if (self.right.value == 1) {
return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
operator: "+",
expression: self.left
}).optimize(compressor);
}
break;
}
}
if (compressor.option("typeofs")) switch (self.operator) {
case "&&":
@@ -6471,7 +6698,7 @@ merge(Compressor.prototype, {
&& indexRight
&& (self.operator == "==" || self.operator == "!=")
&& self.left instanceof AST_Number
&& self.left.getValue() == 0) {
&& self.left.value == 0) {
return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: self.right
@@ -6500,10 +6727,10 @@ merge(Compressor.prototype, {
return node.optimize(compressor);
}
}
// x && (y && z) ==> x && y && z
// x || (y || z) ==> x || y || z
// x + ("y" + z) ==> x + "y" + z
// "x" + (y + "z")==> "x" + y + "z"
// x && (y && z) => x && y && z
// x || (y || z) => x || y || z
// x + ("y" + z) => x + "y" + z
// "x" + (y + "z") => "x" + y + "z"
if (self.right instanceof AST_Binary
&& self.right.operator == self.operator
&& (lazy_op[self.operator]
@@ -6574,19 +6801,19 @@ merge(Compressor.prototype, {
switch (self.operator) {
case "<=":
// 0 <= array.indexOf(string) => !!~array.indexOf(string)
return indexRight && self.left instanceof AST_Number && self.left.getValue() == 0;
return indexRight && self.left instanceof AST_Number && self.left.value == 0;
case "<":
// array.indexOf(string) < 0 => !~array.indexOf(string)
if (indexLeft && self.right instanceof AST_Number && self.right.getValue() == 0) return true;
if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
// -1 < array.indexOf(string) => !!~array.indexOf(string)
case "==":
case "!=":
// -1 == array.indexOf(string) => !~array.indexOf(string)
// -1 != array.indexOf(string) => !!~array.indexOf(string)
if (!indexRight) return false;
return self.left instanceof AST_Number && self.left.getValue() == -1
return self.left instanceof AST_Number && self.left.value == -1
|| self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
&& self.left.expression instanceof AST_Number && self.left.expression.getValue() == 1;
&& self.left.expression instanceof AST_Number && self.left.expression.value == 1;
}
}
});
@@ -6621,24 +6848,28 @@ merge(Compressor.prototype, {
var def = self.definition();
var fixed = self.fixed_value();
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
if (single_use && fixed instanceof AST_Lambda) {
if (def.scope !== self.scope
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
single_use = false;
} else if (recursive_ref(compressor, def)) {
single_use = false;
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
do if (scope instanceof AST_Defun || scope instanceof AST_Function) {
scope.inlined = true;
} while (scope = scope.parent_scope);
if (single_use) {
if (fixed instanceof AST_Lambda) {
if (def.scope !== self.scope
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
single_use = false;
} else if (recursive_ref(compressor, def)) {
single_use = false;
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
do if (scope instanceof AST_Defun || scope instanceof AST_Function) {
scope.inlined = true;
} while (scope = scope.parent_scope);
}
}
if (single_use) fixed.parent_scope = self.scope;
} else if (!fixed || !fixed.is_constant_expression()) {
single_use = false;
}
if (single_use) fixed.parent_scope = self.scope;
}
if (single_use && fixed) {
if (single_use) {
def.single_use = false;
fixed._squeezed = true;
fixed.single_use = true;
@@ -6891,7 +7122,7 @@ merge(Compressor.prototype, {
if (self.right.left instanceof AST_SymbolRef
&& self.right.left.name == self.left.name
&& ASSIGN_OPS[self.right.operator]) {
// x = x - 2 ---> x -= 2
// x = x - 2 => x -= 2
self.operator = self.right.operator + "=";
self.right = self.right.right;
}
@@ -6899,7 +7130,7 @@ merge(Compressor.prototype, {
&& self.right.right.name == self.left.name
&& ASSIGN_OPS_COMMUTATIVE[self.right.operator]
&& !self.right.left.has_side_effects(compressor)) {
// x = 2 & x ---> x &= 2
// x = 2 & x => x &= 2
self.operator = self.right.operator + "=";
self.right = self.right.left;
}
@@ -6907,7 +7138,7 @@ merge(Compressor.prototype, {
if ((self.operator == "-=" || self.operator == "+="
&& (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
&& self.right instanceof AST_Number
&& self.right.getValue() === 1) {
&& self.right.value == 1) {
var op = self.operator.slice(0, -1);
return make_node(AST_UnaryPrefix, self, {
operator: op + op,
@@ -6968,7 +7199,7 @@ merge(Compressor.prototype, {
var condition = self.condition;
var consequent = self.consequent;
var alternative = self.alternative;
// x?x:y --> x||y
// x ? x : y => x || y
if (condition instanceof AST_SymbolRef
&& consequent instanceof AST_SymbolRef
&& condition.definition() === consequent.definition()) {
@@ -7005,7 +7236,7 @@ merge(Compressor.prototype, {
});
}
}
// x ? y : y --> x, y
// x ? y : y => x, y
if (consequent.equivalent_to(alternative)) return make_sequence(self, [
condition,
consequent
@@ -7014,7 +7245,7 @@ merge(Compressor.prototype, {
&& alternative.TYPE === consequent.TYPE
&& consequent.args.length == alternative.args.length) {
var arg_index = arg_diff();
// x ? y(a) : z(a) --> (x ? y : z)(a)
// x ? y(a) : z(a) => (x ? y : z)(a)
if (arg_index == -1
&& !(consequent.expression instanceof AST_PropAccess)
&& !(alternative.expression instanceof AST_PropAccess)) {
@@ -7026,7 +7257,7 @@ merge(Compressor.prototype, {
});
return node;
}
// x ? y(a) : y(b) --> y(x ? a : b)
// x ? y(a) : y(b) => y(x ? a : b)
if (arg_index >= 0
&& consequent.expression.equivalent_to(alternative.expression)
&& !condition.has_side_effects(compressor)
@@ -7040,7 +7271,7 @@ merge(Compressor.prototype, {
return node;
}
}
// x ? (y ? a : b) : b --> x && y ? a : b
// x ? (y ? a : b) : b => x && y ? a : b
if (consequent instanceof AST_Conditional
&& consequent.alternative.equivalent_to(alternative)) {
return make_node(AST_Conditional, self, {
@@ -7053,7 +7284,7 @@ merge(Compressor.prototype, {
alternative: alternative
});
}
// x ? a : (y ? a : b)--> x || y ? a : b
// x ? a : (y ? a : b) => x || y ? a : b
if (alternative instanceof AST_Conditional
&& consequent.equivalent_to(alternative.consequent)) {
return make_node(AST_Conditional, self, {
@@ -7066,7 +7297,7 @@ merge(Compressor.prototype, {
alternative: alternative.alternative
});
}
// x ? (y, w) : (z, w) --> x ? y : z, w
// x ? (y, w) : (z, w) => x ? y : z, w
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
return make_sequence(self, [
@@ -7078,7 +7309,7 @@ merge(Compressor.prototype, {
consequent.tail_node()
]).optimize(compressor);
}
// x ? y || z : z --> x && y || z
// x ? y || z : z => x && y || z
if (consequent instanceof AST_Binary
&& consequent.operator == "||"
&& consequent.right.equivalent_to(alternative)) {
@@ -7095,10 +7326,10 @@ merge(Compressor.prototype, {
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (is_true(consequent)) {
if (is_false(alternative)) {
// c ? true : false ---> !!c
// c ? true : false => !!c
return booleanize(condition);
}
// c ? true : x ---> !!c || x
// c ? true : x => !!c || x
return make_node(AST_Binary, self, {
operator: "||",
left: booleanize(condition),
@@ -7107,10 +7338,10 @@ merge(Compressor.prototype, {
}
if (is_false(consequent)) {
if (is_true(alternative)) {
// c ? false : true ---> !c
// c ? false : true => !c
return booleanize(condition.negate(compressor));
}
// c ? false : x ---> !c && x
// c ? false : x => !c && x
return make_node(AST_Binary, self, {
operator: "&&",
left: booleanize(condition.negate(compressor)),
@@ -7118,7 +7349,7 @@ merge(Compressor.prototype, {
});
}
if (is_true(alternative)) {
// c ? x : true ---> !c || x
// c ? x : true => !c || x
return make_node(AST_Binary, self, {
operator: "||",
left: booleanize(condition.negate(compressor)),
@@ -7126,7 +7357,7 @@ merge(Compressor.prototype, {
});
}
if (is_false(alternative)) {
// c ? x : false ---> !!c && x
// c ? x : false => !!c && x
return make_node(AST_Binary, self, {
operator: "&&",
left: booleanize(condition),
@@ -7150,22 +7381,25 @@ merge(Compressor.prototype, {
return node instanceof AST_True
|| in_bool
&& node instanceof AST_Constant
&& node.getValue()
&& node.value
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !node.expression.getValue());
&& !node.expression.value);
}
// AST_False or !1
// AST_False or !1 or void 0
function is_false(node) {
return node instanceof AST_False
|| in_bool
&& node instanceof AST_Constant
&& !node.getValue()
&& (node instanceof AST_Constant
&& !node.value
|| node instanceof AST_UnaryPrefix
&& node.operator == "void"
&& !node.expression.has_side_effects(compressor))
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& node.expression.getValue());
&& node.expression.value);
}
function arg_diff() {
@@ -7273,7 +7507,7 @@ merge(Compressor.prototype, {
&& is_arguments(def = expr.definition())
&& prop instanceof AST_Number
&& (fn = expr.scope) === find_lambda()) {
var index = prop.getValue();
var index = prop.value;
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
if (!def.deleted) def.deleted = [];
def.deleted[index] = true;
@@ -7319,7 +7553,7 @@ merge(Compressor.prototype, {
}
if (compressor.option("properties") && compressor.option("side_effects")
&& prop instanceof AST_Number && expr instanceof AST_Array) {
var index = prop.getValue();
var index = prop.value;
var elements = expr.elements;
var retValue = elements[index];
if (safe_to_flatten(retValue, compressor)) {

View File

@@ -119,15 +119,20 @@ function OutputStream(options) {
});
} : function(str) {
var s = "";
for (var i = 0; i < str.length; i++) {
if (is_surrogate_pair_head(str[i]) && !is_surrogate_pair_tail(str[i + 1])
|| is_surrogate_pair_tail(str[i]) && !is_surrogate_pair_head(str[i - 1])) {
s += "\\u" + str.charCodeAt(i).toString(16);
} else {
s += str[i];
for (var i = 0, j = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
if (is_surrogate_pair_head(code)) {
if (is_surrogate_pair_tail(str.charCodeAt(i + 1))) {
i++;
continue;
}
} else if (!is_surrogate_pair_tail(code)) {
continue;
}
s += str.slice(j, i) + "\\u" + code.toString(16);
j = i + 1;
}
return s;
return j == 0 ? str : s + str.slice(j);
};
function make_string(str, quote) {
@@ -777,7 +782,7 @@ function OutputStream(options) {
PARENS(AST_Number, function(output) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.getValue();
var value = this.value;
if (value < 0 || /^0/.test(make_num(value))) {
return true;
}
@@ -1207,7 +1212,7 @@ function OutputStream(options) {
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (expr instanceof AST_Number && expr.value >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
@@ -1331,21 +1336,21 @@ function OutputStream(options) {
output.print("this");
});
DEFPRINT(AST_Constant, function(self, output) {
output.print(self.getValue());
output.print(self.value);
});
DEFPRINT(AST_String, function(self, output) {
output.print_string(self.getValue(), self.quote);
output.print_string(self.value, self.quote);
});
DEFPRINT(AST_Number, function(self, output) {
if (use_asm && self.start && self.start.raw != null) {
output.print(self.start.raw);
} else {
output.print(make_num(self.getValue()));
output.print(make_num(self.value));
}
});
DEFPRINT(AST_RegExp, function(self, output) {
var regexp = self.getValue();
var regexp = self.value;
var str = regexp.toString();
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));

View File

@@ -133,14 +133,10 @@ function is_letter(code) {
}
function is_surrogate_pair_head(code) {
if (typeof code == "string")
code = code.charCodeAt(0);
return code >= 0xd800 && code <= 0xdbff;
}
function is_surrogate_pair_tail(code) {
if (typeof code == "string")
code = code.charCodeAt(0);
return code >= 0xdc00 && code <= 0xdfff;
}

View File

@@ -428,6 +428,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
if (options.cache && node instanceof AST_Toplevel) {
node.globals.each(mangle);
}
if (node instanceof AST_Defun && tw.has_directive("use asm")) {
var sym = new AST_SymbolRef(node.name);
sym.scope = node;
sym.reference(options);
}
node.variables.each(function(def) {
if (!defer_redef(def)) mangle(def);
});

View File

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

View File

@@ -166,3 +166,69 @@ asm_nested_functions: {
}
expect_exact: '0;function a(){"use asm";0.0}0;function b(){0;function c(){"use asm";0.0}0;function d(){0}0}0;'
}
issue_3636_1: {
mangle = {}
input: {
function n(stdlib, foreign, buffer) {
"use asm";
function add(x, y) {
x = x | 0;
y = y | 0;
return x + y | 0;
}
return {
add: add
};
}
console.log(new n().add("foo", 42));
}
expect: {
function n(o, e, u) {
"use asm";
function d(n, o) {
n = n | 0;
o = o | 0;
return n + o | 0;
}
return {
add: d
};
}
console.log(new n().add("foo", 42));
}
expect_stdout: "42"
}
issue_3636_2: {
mangle = {}
input: {
var n = function(stdlib, foreign, buffer) {
"use asm";
function add(x, y) {
x = x | 0;
y = y | 0;
return x + y | 0;
}
return {
add: add
};
};
console.log(new n().add("foo", 42));
}
expect: {
var n = function(n, o, e) {
"use asm";
function r(n, o) {
n = n | 0;
o = o | 0;
return n + o | 0;
}
return {
add: r
};
};
console.log(new n().add("foo", 42));
}
expect_stdout: "42"
}

View File

@@ -86,3 +86,48 @@ issue_3465_3: {
}
expect_stdout: "PASS"
}
issue_2737_2: {
options = {
booleans: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(bar) {
for (;bar();) break;
})(function qux() {
return console.log("PASS"), qux;
});
}
expect: {
(function(bar) {
for (;bar();) break;
})(function() {
return console.log("PASS"), 1;
});
}
expect_stdout: "PASS"
}
issue_3658: {
options = {
booleans: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function f() {
console || f();
return "PASS";
}());
}
expect: {
console.log(function f() {
console || f();
return "PASS";
}());
}
expect_stdout: "PASS"
}

View File

@@ -5863,8 +5863,8 @@ issue_2974: {
var c = 0;
(function(b) {
var a = 2;
for (; b.null = -4, c++, b.null && --a > 0;);
})(!0),
for (;c++, (!0).null && --a > 0;);
})(),
console.log(c);
}
expect_stdout: "1"
@@ -7422,3 +7422,72 @@ issue_3641: {
}
expect_stdout: "foo undefined"
}
issue_3651: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var a, b = "PASS";
try {
a = function() {
try {
var c = 1;
while (0 < --c);
} catch (e) {} finally {
throw 42;
}
}();
b = "FAIL";
a.p;
} catch (e) {
console.log(b);
}
}
expect: {
var a, b = "PASS";
try {
a = function() {
try {
var c = 1;
while (0 < --c);
} catch (e) {} finally {
throw 42;
}
}();
b = "FAIL";
a.p;
} catch (e) {
console.log(b);
}
}
expect_stdout: "PASS"
}
issue_3671: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
try {
a++;
A += 0;
a = 1 + a;
} catch (e) {
console.log(a);
}
}
expect: {
var a = 0;
try {
a++;
A += 0;
a = 1 + a;
} catch (e) {
console.log(a);
}
}
expect_stdout: "1"
}

View File

@@ -1578,3 +1578,34 @@ issue_3576: {
}
expect_stdout: "PASS"
}
issue_3668: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f() {
try {
var undefined = typeof f;
if (!f) return undefined;
return;
} catch (e) {
return "FAIL";
}
}
console.log(f());
}
expect: {
function f() {
try {
var undefined = typeof f;
return f ? void 0 : undefined;
} catch (e) {
return "FAIL";
}
}
console.log(f());
}
expect_stdout: "undefined"
}

View File

@@ -141,207 +141,6 @@ try_catch_finally: {
]
}
accessor: {
options = {
side_effects: true,
}
input: {
({
get a() {},
set a(v){
this.b = 2;
},
b: 1
});
}
expect: {}
}
issue_2233_1: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
Array.isArray;
Boolean;
console.log;
Date;
decodeURI;
decodeURIComponent;
encodeURI;
encodeURIComponent;
Error.name;
escape;
eval;
EvalError;
Function.length;
isFinite;
isNaN;
JSON;
Math.random;
Number.isNaN;
parseFloat;
parseInt;
RegExp;
Object.defineProperty;
String.fromCharCode;
RangeError;
ReferenceError;
SyntaxError;
TypeError;
unescape;
URIError;
}
expect: {}
expect_stdout: true
}
global_timeout_and_interval_symbols: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
// These global symbols do not exist in the test sandbox
// and must be tested separately.
clearInterval;
clearTimeout;
setInterval;
setTimeout;
}
expect: {}
}
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
var RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
}
}
}
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
UndeclaredGlobal;
}
}
global_fns: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Boolean(1, 2);
decodeURI(1, 2);
decodeURIComponent(1, 2);
Date(1, 2);
encodeURI(1, 2);
encodeURIComponent(1, 2);
Error(1, 2);
escape(1, 2);
EvalError(1, 2);
isFinite(1, 2);
isNaN(1, 2);
Number(1, 2);
Object(1, 2);
parseFloat(1, 2);
parseInt(1, 2);
RangeError(1, 2);
ReferenceError(1, 2);
String(1, 2);
SyntaxError(1, 2);
TypeError(1, 2);
unescape(1, 2);
URIError(1, 2);
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect: {
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect_stdout: [
"SyntaxError",
"SyntaxError",
"RangeError",
]
}
collapse_vars_assignment: {
options = {
collapse_vars: true,
@@ -863,23 +662,6 @@ issue_2749: {
expect_stdout: "PASS"
}
unsafe_builtin: {
options = {
side_effects: true,
unsafe: true,
}
input: {
(!w).constructor(x);
Math.abs(y);
[ 1, 2, z ].valueOf();
}
expect: {
w, x;
y;
z;
}
}
issue_2860_1: {
options = {
dead_code: true,
@@ -941,24 +723,6 @@ issue_2929: {
expect_stdout: "PASS"
}
unsafe_string_replace: {
options = {
side_effects: true,
unsafe: true,
}
input: {
"foo".replace("f", function() {
console.log("PASS");
});
}
expect: {
"foo".replace("f", function() {
console.log("PASS");
});
}
expect_stdout: "PASS"
}
issue_3402: {
options = {
dead_code: true,

View File

@@ -699,18 +699,6 @@ iife: {
}
}
drop_value: {
options = {
side_effects: true,
}
input: {
(1, [2, foo()], 3, {a:1, b:bar()});
}
expect: {
foo(), bar();
}
}
issue_1539: {
options = {
collapse_vars: true,
@@ -2266,3 +2254,124 @@ issue_3598: {
}
expect_stdout: "PASS"
}
self_assign: {
options = {
passes: 2,
side_effects: true,
unused: true,
}
input: {
function d(a) {
a = a;
}
function e(a, b) {
a = b;
b = a;
}
function f(a, b, c) {
a = b;
b = c;
c = a;
}
function g(a, b, c) {
a = a * b + c;
}
}
expect: {
function d(a) {}
function e(a, b) {}
function f(a, b, c) {}
function g(a, b, c) {}
}
}
function_argument_reference: {
options = {
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var a = 1, b = 42;
function f(a) {
b <<= a;
}
f();
console.log(a, b);
}
expect: {
var a = 1, b = 42;
function f(a) {
b <<= a;
}
f();
console.log(a, b);
}
expect_stdout: "1 42"
}
function_parameter_ie8: {
options = {
ie8: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a;
function f() {
console.log("PASS");
}
f(a = 1 + a);
})();
}
expect: {
(function() {
(function f() {
console.log("PASS");
})(0);
})();
}
expect_stdout: "PASS"
}
issue_3664: {
options = {
pure_getters: "strict",
unused: true,
}
input: {
console.log(function() {
var a, b = (a = (a = [ b && console.log("FAIL") ]).p = 0, 0);
return "PASS";
}());
}
expect: {
console.log(function() {
var b = ([ b && console.log("FAIL") ].p = 0, 0);
return "PASS";
}());
}
expect_stdout: "PASS"
}
issue_3673: {
options = {
pure_getters: "strict",
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
(a = [ a ]).p = 42;
console.log("PASS");
}
expect: {
var a;
(a = [ a ]).p = 42;
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -266,12 +266,7 @@ issue_2084: {
}
expect: {
var c = 0;
!function() {
var c;
c = 1 + (c = -1),
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(),
23..toString(),
console.log(c);
}
expect_stdout: "0"
@@ -1911,14 +1906,14 @@ issue_2737_2: {
}
input: {
(function(bar) {
for (;bar(); ) break;
for (;bar();) break;
})(function qux() {
return console.log("PASS"), qux;
});
}
expect: {
(function(bar) {
for (;bar(); ) break;
for (;bar();) break;
})(function qux() {
return console.log("PASS"), qux;
});
@@ -3705,3 +3700,88 @@ pr_3595_4: {
}
expect_stdout: "PASS"
}
issue_3679_1: {
options = {
collapse_vars: true,
inline: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var f = function() {};
f.g = function() {
console.log("PASS");
};
f.g();
})();
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_3679_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
"use strict";
var f = function() {};
f.g = function() {
console.log("PASS");
};
f.g();
})();
}
expect: {
(function() {
"use strict";
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_3679_3: {
options = {
collapse_vars: true,
inline: true,
functions: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var f = function() {};
f.p = "PASS";
f.g = function() {
console.log(f.p);
};
f.g();
})();
}
expect: {
(function() {
function f() {};
f.p = "PASS";
(f.g = function() {
console.log(f.p);
})();
})();
}
expect_stdout: "PASS"
}

View File

@@ -664,7 +664,7 @@ issue_2519: {
}
expect: {
function testFunc() {
return 1 * ((6 + 5) / 2);
return +((6 + 5) / 2);
}
console.log(testFunc());
}

View File

@@ -6,7 +6,7 @@ while_becomes_for: {
while (foo()) bar();
}
expect: {
for (; foo(); ) bar();
for (;foo();) bar();
}
}
@@ -19,7 +19,7 @@ drop_if_break_1: {
if (foo()) break;
}
expect: {
for (; !foo(););
for (;!foo(););
}
}
@@ -32,7 +32,7 @@ drop_if_break_2: {
if (foo()) break;
}
expect: {
for (; bar() && !foo(););
for (;bar() && !foo(););
}
}
@@ -70,7 +70,7 @@ drop_if_break_4: {
}
}
expect: {
for (; bar() && (x(), y(), !foo());) z(), k();
for (;bar() && (x(), y(), !foo());) z(), k();
}
}
@@ -82,7 +82,7 @@ drop_if_else_break_1: {
for (;;) if (foo()) bar(); else break;
}
expect: {
for (; foo(); ) bar();
for (;foo();) bar();
}
}
@@ -97,7 +97,7 @@ drop_if_else_break_2: {
}
}
expect: {
for (; bar() && foo();) baz();
for (;bar() && foo();) baz();
}
}
@@ -114,7 +114,7 @@ drop_if_else_break_3: {
}
}
expect: {
for (; bar() && foo();) {
for (;bar() && foo();) {
baz();
stuff1();
stuff2();
@@ -138,7 +138,7 @@ drop_if_else_break_4: {
}
}
expect: {
for (; bar() && (x(), y(), foo());) baz(), z(), k();
for (;bar() && (x(), y(), foo());) baz(), z(), k();
}
}
@@ -523,13 +523,13 @@ issue_2740_1: {
loops: true,
}
input: {
for (; ; ) break;
for (a(); ; ) break;
for (; b(); ) break;
for (c(); d(); ) break;
for (; ; e()) break;
for (f(); ; g()) break;
for (; h(); i()) break;
for (;;) break;
for (a();;) break;
for (;b();) break;
for (c(); d();) break;
for (;;e()) break;
for (f();; g()) break;
for (;h(); i()) break;
for (j(); k(); l()) break;
}
expect: {
@@ -670,7 +670,7 @@ issue_3371: {
function a() {
console.log("PASS");
}
for (; a(); );
for (;a(););
})();
}
expect_stdout: "PASS"

View File

@@ -91,7 +91,7 @@ evaluate_1: {
expect: {
console.log(
x + 1 + 2,
1 * x * 2,
2 * x,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
@@ -173,7 +173,7 @@ evaluate_2: {
var x = "42", y = null;
[
x + 1 + 2,
1 * x * 2,
2 * x,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
@@ -979,3 +979,257 @@ unsafe_math_swap_constant: {
}
expect_stdout: "6 6 7 6 6 8 9 10"
}
identity_1: {
options = {
evaluate: true,
}
input: {
0 + a;
a + 0;
0 - a;
a - 0;
1 * a;
a * 1;
1 / a;
a / 1;
}
expect: {
0 + a;
a + 0;
0 - a;
+a;
+a;
+a;
1 / a;
+a;
}
}
identity_2: {
options = {
evaluate: true,
}
input: {
0 + !a;
!a + 0;
0 - !a;
!a - 0;
1 * !a;
!a * 1;
1 / !a;
!a / 1;
}
expect: {
+!a;
+!a;
0 - !a;
+!a;
+!a;
+!a;
1 / !a;
+!a;
}
}
identity_3: {
options = {
evaluate: true,
}
input: {
0 + --a;
--a + 0;
0 - --a;
--a - 0;
1 * --a;
--a * 1;
1 / --a;
--a / 1;
}
expect: {
--a;
--a;
0 - --a;
--a;
--a;
--a;
1 / --a;
--a;
}
}
issue_3653: {
options = {
evaluate: true,
}
input: {
console.log(0 - (console && 0));
console.log(0 + (0 - (console && 0)));
console.log(0 - (0 - (console && 0)));
console.log(1 * (0 - (console && 0)));
console.log(1 / (0 - (console && 0)));
console.log((0 - (console && 0)) + 0);
console.log((0 - (console && 0)) - 0);
console.log((0 - (console && 0)) * 1);
console.log((0 - (console && 0)) / 1);
}
expect: {
console.log(0 - (console && 0));
console.log(0 - (console && 0));
console.log(0 - (0 - (console && 0)));
console.log(0 - (console && 0));
console.log(1 / (0 - (console && 0)));
console.log(0 - (console && 0));
console.log(0 - (console && 0));
console.log(0 - (console && 0));
console.log(0 - (console && 0));
}
expect_stdout: [
"0",
"0",
"0",
"0",
"Infinity",
"0",
"0",
"0",
"0",
]
}
issue_3655: {
options = {
evaluate: true,
}
input: {
console.log(0 + 0 * -[].length);
console.log(0 + (0 + 0 * -[].length));
console.log(0 - (0 + 0 * -[].length));
console.log(1 * (0 + 0 * -[].length));
console.log(1 / (0 + 0 * -[].length));
console.log((0 + 0 * -[].length) + 0);
console.log((0 + 0 * -[].length) - 0);
console.log((0 + 0 * -[].length) * 1);
console.log((0 + 0 * -[].length) / 1);
}
expect: {
console.log(0 + 0 * -[].length);
console.log(0 + 0 * -[].length);
console.log(0 - (0 + 0 * -[].length));
console.log(0 + 0 * -[].length);
console.log(1 / (0 + 0 * -[].length));
console.log(0 + 0 * -[].length);
console.log(0 + 0 * -[].length);
console.log(0 + 0 * -[].length);
console.log(0 + 0 * -[].length);
}
expect_stdout: [
"0",
"0",
"0",
"0",
"Infinity",
"0",
"0",
"0",
"0",
]
}
issue_3676_1: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = [];
console.log(false - (a - (a[1] = 42)));
}
expect: {
var a = [];
console.log(false - (a - (a[1] = 42)));
}
expect_stdout: "NaN"
}
issue_3676_2: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a;
console.log(false - ((a = []) - (a[1] = 42)));
}
expect: {
var a;
console.log(false - ((a = []) - (a[1] = 42)));
}
expect_stdout: "NaN"
}
issue_3682_1: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = -0;
console.log(1 / (a - 1 + 1));
}
expect: {
var a = -0;
console.log(1 / (a - 1 + 1));
}
expect_stdout: "Infinity"
}
issue_3682_2: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = -0, b = 1;
console.log(1 / (a - (b - b)));
}
expect: {
var a = -0, b = 1;
console.log(1 / (a - (b - b)));
}
expect_stdout: "-Infinity"
}
issue_3682_3: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = -0, b = 1, c = -1;
console.log(1 / (a - (+b + +c)));
}
expect: {
var a = -0, b = 1, c = -1;
console.log(1 / (a - (+b + +c)));
}
expect_stdout: "-Infinity"
}
issue_3684: {
options = {
evaluate: true,
}
input: {
console.log(1 / (-1 * (0 & console) + 0));
console.log(1 / ((0 & console) / -1 + 0));
}
expect: {
console.log(1 / (-1 * (0 & console) + 0));
console.log(1 / ((0 & console) / -1 + 0));
}
expect_stdout: [
"Infinity",
"Infinity",
]
}

View File

@@ -6847,3 +6847,34 @@ issue_3631_2: {
}
expect_stdout: "undefined"
}
issue_3666: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
var a = "FAIL";
} finally {
for (;!a;)
var c = a++;
var a = "PASS", b = c = "PASS";
}
console.log(a, b);
}
expect: {
try {
var a = "FAIL";
} finally {
for (;!a;)
a++;
var b = a = "PASS";
}
console.log(a, b);
}
expect_stdout: "PASS PASS"
}

View File

@@ -0,0 +1,277 @@
accessor: {
options = {
side_effects: true,
}
input: {
({
get a() {},
set a(v){
this.b = 2;
},
b: 1
});
}
expect: {}
}
issue_2233_1: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
Array.isArray;
Boolean;
console.log;
Date;
decodeURI;
decodeURIComponent;
encodeURI;
encodeURIComponent;
Error.name;
escape;
eval;
EvalError;
Function.length;
isFinite;
isNaN;
JSON;
Math.random;
Number.isNaN;
parseFloat;
parseInt;
RegExp;
Object.defineProperty;
String.fromCharCode;
RangeError;
ReferenceError;
SyntaxError;
TypeError;
unescape;
URIError;
}
expect: {}
expect_stdout: true
}
global_timeout_and_interval_symbols: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
// These global symbols do not exist in the test sandbox
// and must be tested separately.
clearInterval;
clearTimeout;
setInterval;
setTimeout;
}
expect: {}
}
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
var RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
}
}
}
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
UndeclaredGlobal;
}
}
global_fns: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Boolean(1, 2);
decodeURI(1, 2);
decodeURIComponent(1, 2);
Date(1, 2);
encodeURI(1, 2);
encodeURIComponent(1, 2);
Error(1, 2);
escape(1, 2);
EvalError(1, 2);
isFinite(1, 2);
isNaN(1, 2);
Number(1, 2);
Object(1, 2);
parseFloat(1, 2);
parseInt(1, 2);
RangeError(1, 2);
ReferenceError(1, 2);
String(1, 2);
SyntaxError(1, 2);
TypeError(1, 2);
unescape(1, 2);
URIError(1, 2);
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect: {
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect_stdout: [
"SyntaxError",
"SyntaxError",
"RangeError",
]
}
unsafe_builtin_1: {
options = {
side_effects: true,
unsafe: true,
}
input: {
(!w).constructor(x);
Math.abs(y);
[ 1, 2, z ].valueOf();
}
expect: {
w, x;
y;
z;
}
}
unsafe_builtin_2: {
options = {
side_effects: true,
unsafe: true,
}
input: {
var o = {};
constructor.call(o, 42);
__defineGetter__.call(o, "foo", function() {
return o.p;
});
__defineSetter__.call(o, void 0, function(a) {
o.p = a;
});
console.log(typeof o, o.undefined = "PASS", o.foo);
}
expect: {
var o = {};
constructor.call(o, 42);
__defineGetter__.call(o, "foo", function() {
return o.p;
});
__defineSetter__.call(o, void 0, function(a) {
o.p = a;
});
console.log(typeof o, o.undefined = "PASS", o.foo);
}
expect_stdout: "object PASS PASS"
}
unsafe_string_replace: {
options = {
side_effects: true,
unsafe: true,
}
input: {
"foo".replace("f", function() {
console.log("PASS");
});
}
expect: {
"foo".replace("f", function() {
console.log("PASS");
});
}
expect_stdout: "PASS"
}
drop_value: {
options = {
side_effects: true,
}
input: {
(1, [2, foo()], 3, {a:1, b:bar()});
}
expect: {
foo(), bar();
}
}

View File

@@ -103,6 +103,7 @@ if_return: {
booleans: true,
conditionals: true,
if_return: true,
passes: 2,
sequences: true,
side_effects: true,
}

View File

@@ -16,6 +16,81 @@ unicode_parse_variables: {
}
}
unicode_escaped_identifier: {
input: {
var \u0061 = "\ud800\udc00";
console.log(a);
}
expect_exact: 'var a="\ud800\udc00";console.log(a);'
expect_stdout: "\ud800\udc00"
}
unicode_identifier_ascii_only: {
beautify = {
ascii_only: true,
}
input: {
var \u0061 = "testing \udbc4\udd11";
var bar = "h\u0065llo";
console.log(a, \u0062\u0061r);
}
expect_exact: 'var a="testing \\udbc4\\udd11";var bar="hello";console.log(a,bar);'
expect_stdout: "testing \udbc4\udd11 hello"
}
unicode_string_literals: {
beautify = {
ascii_only: true,
}
input: {
var a = "6 length unicode character: \udbc4\udd11";
console.log(\u0061);
}
expect_exact: 'var a="6 length unicode character: \\udbc4\\udd11";console.log(a);'
expect_stdout: "6 length unicode character: \udbc4\udd11"
}
check_escape_style: {
beautify = {
ascii_only: true,
}
input: {
var a = "\x01";
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
var \u0100 = "\u0100";
var \u1000 = "\u1000";
var \u1000 = "\ud800\udc00";
var \u3f80 = "\udbc0\udc00";
console.log(\u0061, \ua0081, \u0100, \u1000, \u3f80);
}
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\u1000="\\ud800\\udc00";var \\u3f80="\\udbc0\\udc00";console.log(a,\\ua0081,\\u0100,\\u1000,\\u3f80);'
expect_stdout: "\u0001 \u0010 \u0100 \ud800\udc00 \udbc0\udc00"
}
escape_non_escaped_identifier: {
beautify = {
ascii_only: true,
}
input: {
var µþ = "µþ";
console.log(\u00b5þ);
}
expect_exact: 'var \\u00b5\\u00fe="\\xb5\\xfe";console.log(\\u00b5\\u00fe);'
expect_stdout: "µþ"
}
non_escape_2_non_escape: {
beautify = {
ascii_only: false,
}
input: {
var µþ = "µþ";
console.log(\u00b5þ);
}
expect_exact: 'var µþ="µþ";console.log(µþ);'
expect_stdout: "µþ"
}
issue_2242_1: {
beautify = {
ascii_only: false,
@@ -24,6 +99,7 @@ issue_2242_1: {
console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00");
}
expect_exact: 'console.log("\\ud83d","\\ude00","\ud83d\ude00","\\ud83d@\\ude00");'
expect_stdout: "\ud83d \ude00 \ud83d\ude00 \ud83d@\ude00"
}
issue_2242_2: {
@@ -34,6 +110,7 @@ issue_2242_2: {
console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00");
}
expect_exact: 'console.log("\\ud83d","\\ude00","\\ud83d\\ude00","\\ud83d@\\ude00");'
expect_stdout: "\ud83d \ude00 \ud83d\ude00 \ud83d@\ude00"
}
issue_2242_3: {
@@ -44,6 +121,7 @@ issue_2242_3: {
console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00");
}
expect_exact: 'console.log("\\ud83d"+"\\ude00","\\ud83d"+"@"+"\\ude00");'
expect_stdout: "\ud83d\ude00 \ud83d@\ude00"
}
issue_2242_4: {
@@ -54,6 +132,7 @@ issue_2242_4: {
console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00");
}
expect_exact: 'console.log("\ud83d\ude00","\\ud83d@\\ude00");'
expect_stdout: "\ud83d\ude00 \ud83d@\ude00"
}
issue_2569: {

View File

@@ -0,0 +1,17 @@
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
function f() {
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
}

View File

@@ -1,17 +1 @@
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
function f() {
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
}
if(x)foo();if(x)foo();else baz();if(x)foo();else if(y)bar();else baz();if(x)if(y)foo();else bar();else baz();if(x)foo();else if(y)bar();else if(z)baz();else moo();function f(){if(x)foo();if(x)foo();else baz();if(x)foo();else if(y)bar();else baz();if(x)if(y)foo();else bar();else baz();if(x)foo();else if(y)bar();else if(z)baz();else moo()}

View File

@@ -1,7 +1,7 @@
var fs = require("fs");
var config = {
limit: 5000,
limit: 10000,
timeout: function(limit) {
this.limit = limit;
}
@@ -55,11 +55,11 @@ process.nextTick(function run() {
var elapsed = Date.now();
var timer;
var done = function() {
reset();
elapsed = Date.now() - elapsed;
if (elapsed > task.limit) {
throw new Error("Timed out: " + elapsed + "ms > " + task.limit + "ms");
}
reset();
log_titles(console.log, task.titles, green('\u221A '));
process.nextTick(run);
};

View File

@@ -176,7 +176,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
assert.strictEqual(stdout, read("test/input/issue-1482/beautify.js"));
done();
});
});
@@ -188,6 +188,22 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should work with `--output-opts`", function(done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -O';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
done();
});
});
it("Should fail when both --beautify & --output-opts are specified", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js -bO";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n");
done();
});
});
it("Should process inline source map", function(done) {
var command = [
uglifyjscmd,

View File

@@ -1090,9 +1090,10 @@ function log(options) {
}
function fuzzy_match(original, uglified) {
original = original.split(" ", 5);
uglified = uglified.split(" ", 5);
for (var i = 0; i < 5; i++) {
uglified = uglified.split(" ");
var i = uglified.length;
original = original.split(" ", i);
while (--i >= 0) {
if (original[i] === uglified[i]) continue;
var a = +original[i];
var b = +uglified[i];
@@ -1128,7 +1129,7 @@ for (var round = 1; round <= num_iterations; round++) {
ok = fuzzy_match(original_result, uglify_result);
if (!ok) {
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"));
ok = sandbox.same_stdout(fuzzy_result, uglify_result, o.toplevel);
ok = sandbox.same_stdout(fuzzy_result, uglify_result);
}
}
} else {