Compare commits

..

18 Commits

Author SHA1 Message Date
Alex Lam S.L
01d6e0f223 v3.7.3 2019-12-27 06:11:29 +08:00
Alex Lam S.L
ab050e7a94 fix corner case in directives (#3645) 2019-12-25 00:55:39 +00:00
Alex Lam S.L
75aa6ef848 enhance conditionals (#3643) 2019-12-22 04:29:32 +00:00
Alex Lam S.L
519a00bd8a fix corner case in collapse_vars (#3642)
fixes #3641
2019-12-22 01:08:56 +00:00
Alex Lam S.L
3ff0feddee suppress false positives from fuzzer (#3638) 2019-12-16 17:32:47 +02:00
Alex Lam S.L
74396acc86 fix corner case in loops (#3635)
fixes #3634
2019-12-11 06:39:46 +08:00
Alex Lam S.L
036bca980c enhance loops (#3633) 2019-12-10 12:57:47 +00:00
Alex Lam S.L
18c2b1841b fix corner case in reduce_vars (#3632)
fixes #3631
2019-12-10 09:45:51 +00:00
Alex Lam S.L
fe19ab7c57 v3.7.2 2019-12-08 15:36:18 +00:00
Alex Lam S.L
9074f05129 fix corner case in collapse_vars (#3629)
fixes #3628
2019-12-05 05:08:37 +08:00
Alex Lam S.L
04fbb1f949 avoid collision with HTML comments (#3625)
fixes #3624
2019-12-05 02:43:25 +08:00
Alex Lam S.L
bf7e4ca1a3 fix corner case in collapse_vars (#3627)
fixes #3626
2019-12-05 00:59:57 +08:00
Alex Lam S.L
d68ddc31f9 fix corner case in reduce_vars (#3623)
fixes #3622
2019-12-04 20:24:55 +08:00
Alex Lam S.L
500e31e03b enhance collapse_vars (#3621) 2019-12-02 15:25:38 +08:00
Alex Lam S.L
bef856addb fix corner case in keep_fargs (#3620)
fixes #3619
2019-12-02 12:28:17 +08:00
Alex Lam S.L
9a6faf365b fix corner cases in keep_fargs & unused (#3618) 2019-12-02 06:43:54 +08:00
Alex Lam S.L
e915832a36 enhance unused (#3617) 2019-12-01 18:10:37 +08:00
Alex Lam S.L
0593892d6e enhance collapse_vars (#3616) 2019-12-01 02:31:04 +08:00
18 changed files with 1552 additions and 250 deletions

View File

@@ -648,7 +648,7 @@ merge(Compressor.prototype, {
tw.in_loop = this;
push(tw);
this.body.walk(tw);
if (has_break_or_continue(this)) {
if (has_break_or_continue(this, tw.parent())) {
pop(tw);
push(tw);
}
@@ -665,7 +665,7 @@ merge(Compressor.prototype, {
if (this.condition) this.condition.walk(tw);
this.body.walk(tw);
if (this.step) {
if (has_break_or_continue(this)) {
if (has_break_or_continue(this, tw.parent())) {
pop(tw);
push(tw);
}
@@ -701,8 +701,11 @@ merge(Compressor.prototype, {
node.argnames.forEach(function(arg, i) {
var d = arg.definition();
if (d.fixed === undefined && (!node.uses_arguments || tw.has_directive("use strict"))) {
var value = iife.args[i];
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
var j = node.argnames.indexOf(arg);
if (j < 0) return value;
return iife.args[j] || make_node(AST_Undefined, iife);
};
tw.loop_ids[d.id] = tw.in_loop;
mark(tw, d, true);
@@ -1124,7 +1127,7 @@ merge(Compressor.prototype, {
hit_index++;
if (hit_index < hit_stack.length) return handle_custom_scan_order(node);
hit = true;
stop_after = find_stop(node, 0);
stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
if (stop_after === node) abort = true;
return node;
}
@@ -1147,7 +1150,7 @@ merge(Compressor.prototype, {
&& (scan_lhs && lhs.equivalent_to(node)
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true;
if (!hit_rhs || !value_def) abort = true;
return node;
}
if (is_lhs(node, parent)) {
@@ -1540,12 +1543,9 @@ merge(Compressor.prototype, {
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Array) return value_def ? find_stop(parent, level + 1) : node;
if (parent instanceof AST_Array) return node;
if (parent instanceof AST_Assign) return node;
if (parent instanceof AST_Binary) {
if (!value_def || parent.left !== node) return node;
return find_stop(parent, level + 1);
}
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
@@ -1553,9 +1553,7 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_ObjectKeyVal) {
return value_def ? find_stop(scanner.parent(level + 1), level + 2) : node;
}
if (parent instanceof AST_ObjectKeyVal) return node;
if (parent instanceof AST_PropAccess) return node;
if (parent instanceof AST_Sequence) {
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
@@ -1567,12 +1565,92 @@ merge(Compressor.prototype, {
return null;
}
function find_stop_value(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
if (parent instanceof AST_Assign) {
if (may_throw(parent)) return node;
if (parent.left instanceof AST_SymbolRef) {
var name = parent.left.name;
if (lhs.name == name) return node;
if (value_def.name == name) return node;
}
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Binary) {
if (lazy_op[parent.operator] && parent.left !== node) {
do {
node = parent;
parent = scanner.parent(++level);
} while (parent instanceof AST_Binary && parent.operator == node.operator);
return node;
}
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Call) return parent;
if (parent instanceof AST_Case) {
if (parent.expression !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Conditional) {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Do) return node;
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_For) {
if (parent.init !== node && parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_ForIn) {
if (parent.init !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_If) {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_ObjectKeyVal) {
var obj = scanner.parent(level + 1);
return all(obj.properties, function(prop) {
return prop instanceof AST_ObjectKeyVal;
}) ? find_stop_value(obj, level + 2) : obj;
}
if (parent instanceof AST_PropAccess) return find_stop_value(parent, level + 1);
if (parent instanceof AST_Sequence) {
return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
}
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Switch) {
if (parent.expression !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Unary) {
if (parent.operator == "delete") return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_VarDef) {
var name = parent.name.name;
if (lhs.name == name) return node;
if (value_def.name == name) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_While) {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
return null;
}
function find_stop_unused(node, level) {
var parent = scanner.parent(level);
if (is_last_node(node, parent)) return node;
if (in_conditional(node, parent)) return node;
if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Assign) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Assign) {
return may_throw(parent) ? node : find_stop_unused(parent, level + 1);
}
if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
@@ -1581,7 +1659,12 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_ObjectKeyVal) return find_stop_unused(scanner.parent(level + 1), level + 2);
if (parent instanceof AST_ObjectKeyVal) {
var obj = scanner.parent(level + 1);
return all(obj.properties, function(prop) {
return prop instanceof AST_ObjectKeyVal;
}) ? find_stop_unused(obj, level + 2) : obj;
}
if (parent instanceof AST_PropAccess) {
var exp = parent.expression;
if (exp === node) return find_stop_unused(parent, level + 1);
@@ -4638,8 +4721,11 @@ merge(Compressor.prototype, {
});
function if_break_in_loop(self, compressor) {
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (compressor.option("dead_code") && is_break(first)) {
var first = first_statement(self.body);
if (compressor.option("dead_code")
&& (first instanceof AST_Break
|| first instanceof AST_Continue && external_target(first)
|| first instanceof AST_Exit)) {
var body = [];
if (self.init instanceof AST_Statement) {
body.push(self.init);
@@ -4648,10 +4734,19 @@ merge(Compressor.prototype, {
body: self.init
}));
}
if (self.condition) {
var retain = external_target(first) || first instanceof AST_Exit;
if (self.condition && retain) {
body.push(make_node(AST_If, self, {
condition: self.condition,
body: first,
alternative: null
}));
} else if (self.condition) {
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
} else if (retain) {
body.push(first);
}
extract_declarations_from_unreachable_code(self.body, body);
return make_node(AST_BlockStatement, self, {
@@ -4659,7 +4754,8 @@ merge(Compressor.prototype, {
});
}
if (first instanceof AST_If) {
if (is_break(first.body)) {
var ab = first_statement(first.body);
if (ab instanceof AST_Break && !external_target(ab)) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -4669,8 +4765,12 @@ merge(Compressor.prototype, {
} else {
self.condition = first.condition.negate(compressor);
}
drop_it(first.alternative);
} else if (is_break(first.alternative)) {
var body = as_statement_array(first.alternative);
extract_declarations_from_unreachable_code(first.body, body);
return drop_it(body);
}
ab = first_statement(first.alternative);
if (ab instanceof AST_Break && !external_target(ab)) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -4680,18 +4780,22 @@ merge(Compressor.prototype, {
} else {
self.condition = first.condition;
}
drop_it(first.body);
var body = as_statement_array(first.body);
extract_declarations_from_unreachable_code(first.alternative, body);
return drop_it(body);
}
}
return self;
function is_break(node) {
return node instanceof AST_Break
&& compressor.loopcontrol_target(node) === compressor.self();
function first_statement(body) {
return body instanceof AST_BlockStatement ? body.body[0] : body;
}
function external_target(node) {
return compressor.loopcontrol_target(node) !== compressor.self();
}
function drop_it(rest) {
rest = as_statement_array(rest);
if (self.body instanceof AST_BlockStatement) {
self.body = self.body.clone();
self.body.body = rest.concat(self.body.body.slice(1));
@@ -4701,7 +4805,7 @@ merge(Compressor.prototype, {
body: rest
}).transform(compressor);
}
self = if_break_in_loop(self, compressor);
return if_break_in_loop(self, compressor);
}
}
@@ -5157,24 +5261,52 @@ merge(Compressor.prototype, {
&& !fn.uses_arguments
&& !fn.pinned()) {
var pos = 0, last = 0;
var drop_fargs = exp === fn && !fn.name && compressor.drop_fargs(fn, self);
var side_effects = [];
for (var i = 0; i < self.args.length; i++) {
var trim = i >= fn.argnames.length;
if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor);
if (node) {
self.args[pos++] = node;
if (drop_fargs) {
fn.argnames.splice(i, 1);
self.args.splice(i, 1);
if (node) side_effects.push(node);
i--;
continue;
} else if (node) {
side_effects.push(node);
self.args[pos++] = make_sequence(self, side_effects);
side_effects = [];
} else if (!trim) {
self.args[pos++] = make_node(AST_Number, self.args[i], {
value: 0
});
if (side_effects.length) {
node = make_sequence(self, side_effects);
side_effects = [];
} else {
node = make_node(AST_Number, self.args[i], {
value: 0
});
}
self.args[pos++] = node;
continue;
}
} else {
self.args[pos++] = self.args[i];
side_effects.push(self.args[i]);
self.args[pos++] = make_sequence(self, side_effects);
side_effects = [];
}
last = pos;
}
if (drop_fargs) for (; i < fn.argnames.length; i++) {
if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1);
}
self.args.length = last;
if (side_effects.length) {
var arg = make_sequence(self, side_effects);
self.args.push(self.args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, self, {
operator: "void",
expression: arg
}) : arg);
}
}
if (compressor.option("unsafe")) {
if (is_undeclared_ref(exp)) switch (exp.name) {
@@ -6534,6 +6666,7 @@ merge(Compressor.prototype, {
name.scope = value;
value.name = name;
lambda_def = value.def_function(name);
lambda_def.recursive_refs = def.recursive_refs;
}
value.walk(new TreeWalker(function(node) {
if (!(node instanceof AST_SymbolRef)) return;
@@ -6872,25 +7005,42 @@ merge(Compressor.prototype, {
});
}
}
// x ? y(a) : y(b) --> y(x ? a : b)
var arg_index;
// x ? y : y --> x, y
if (consequent.equivalent_to(alternative)) return make_sequence(self, [
condition,
consequent
]).optimize(compressor);
if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE
&& consequent.args.length > 0
&& consequent.args.length == alternative.args.length
&& consequent.expression.equivalent_to(alternative.expression)
&& !condition.has_side_effects(compressor)
&& !consequent.expression.has_side_effects(compressor)
&& typeof (arg_index = single_arg_diff()) == "number") {
var node = consequent.clone();
node.args[arg_index] = make_node(AST_Conditional, self, {
condition: condition,
consequent: consequent.args[arg_index],
alternative: alternative.args[arg_index]
});
return node;
&& consequent.args.length == alternative.args.length) {
var arg_index = arg_diff();
// x ? y(a) : z(a) --> (x ? y : z)(a)
if (arg_index == -1
&& !(consequent.expression instanceof AST_PropAccess)
&& !(alternative.expression instanceof AST_PropAccess)) {
var node = consequent.clone();
node.expression = make_node(AST_Conditional, self, {
condition: condition,
consequent: consequent.expression,
alternative: alternative.expression
});
return node;
}
// x ? y(a) : y(b) --> y(x ? a : b)
if (arg_index >= 0
&& consequent.expression.equivalent_to(alternative.expression)
&& !condition.has_side_effects(compressor)
&& !consequent.expression.has_side_effects(compressor)) {
var node = consequent.clone();
node.args[arg_index] = make_node(AST_Conditional, self, {
condition: condition,
consequent: consequent.args[arg_index],
alternative: alternative.args[arg_index]
});
return node;
}
}
// x?y?z:a:a --> x&&y?z:a
// 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, {
@@ -6903,12 +7053,18 @@ merge(Compressor.prototype, {
alternative: alternative
});
}
// x ? y : y --> x, y
if (consequent.equivalent_to(alternative)) {
return make_sequence(self, [
condition,
consequent
]).optimize(compressor);
// 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, {
condition: make_node(AST_Binary, self, {
left: condition,
operator: "||",
right: alternative.condition
}),
consequent: consequent,
alternative: alternative.alternative
});
}
// x ? (y, w) : (z, w) --> x ? y : z, w
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
@@ -7012,17 +7168,18 @@ merge(Compressor.prototype, {
&& node.expression.getValue());
}
function single_arg_diff() {
function arg_diff() {
var a = consequent.args;
var b = alternative.args;
for (var i = 0, len = a.length; i < len; i++) {
if (!a[i].equivalent_to(b[i])) {
for (var j = i + 1; j < len; j++) {
if (!a[j].equivalent_to(b[j])) return;
if (!a[j].equivalent_to(b[j])) return -2;
}
return i;
}
}
return -1;
}
function can_shift_lhs_of_tail(node) {

View File

@@ -43,8 +43,6 @@
"use strict";
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
function is_some_comments(comment) {
// multiline comment
return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
@@ -269,7 +267,7 @@ function OutputStream(options) {
}
}
newline_insert = -1;
var prev = last.charAt(last.length - 1);
var prev = last.slice(-1);
if (might_need_semicolon) {
might_need_semicolon = false;
@@ -298,16 +296,16 @@ function OutputStream(options) {
}
if (might_need_space) {
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\\"))
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| last == "--" && ch == ">") {
OUTPUT += " ";
current_col++;
current_pos++;
}
might_need_space = false;
if (prev != "<" || str != "!") might_need_space = false;
}
if (mapping_token) {
@@ -322,7 +320,7 @@ function OutputStream(options) {
}
OUTPUT += str;
has_parens = str[str.length - 1] == "(";
has_parens = str.slice(-1) == "(";
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n;
@@ -378,7 +376,7 @@ function OutputStream(options) {
};
function force_semicolon() {
might_need_semicolon = false;
if (might_need_semicolon) print(";");
print(";");
}
@@ -585,17 +583,7 @@ function OutputStream(options) {
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\\") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
print_string : function(str, quote) { print(encode_string(str, quote)) },
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
@@ -633,17 +621,10 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
}
var in_directive = false;
var active_scope = null;
var use_asm = null;
var use_asm = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
var self = this, generator = self._codegen;
if (self instanceof AST_Scope) {
active_scope = self;
} else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope;
}
function doit() {
stream.prepend_comments(self);
self.add_source_map(stream);
@@ -657,9 +638,6 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self === use_asm) {
use_asm = null;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
@@ -828,7 +806,18 @@ function OutputStream(options) {
/* -----[ PRINTERS ]----- */
DEFPRINT(AST_Directive, function(self, output) {
output.print_string(self.value, self.quote);
var quote = self.quote;
var value = self.value;
switch (output.option("quote_style")) {
case 0:
case 2:
if (value.indexOf('"') == -1) quote = '"';
break;
case 1:
if (value.indexOf("'") == -1) quote = "'";
break;
}
output.print(quote + value + quote);
output.semicolon();
});
DEFPRINT(AST_Debugger, function(self, output) {
@@ -840,30 +829,27 @@ function OutputStream(options) {
function display_body(body, is_toplevel, output, allow_directives) {
var last = body.length - 1;
in_directive = allow_directives;
var in_directive = allow_directives;
var was_asm = use_asm;
body.forEach(function(stmt, i) {
if (in_directive === true && !(stmt instanceof AST_Directive ||
stmt instanceof AST_EmptyStatement ||
(stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
)) {
in_directive = false;
}
if (!(stmt instanceof AST_EmptyStatement)) {
output.indent();
stmt.print(output);
if (!(i == last && is_toplevel)) {
output.newline();
if (is_toplevel) output.newline();
if (in_directive) {
if (stmt instanceof AST_Directive) {
if (stmt.value == "use asm") use_asm = true;
} else if (!(stmt instanceof AST_EmptyStatement)) {
if (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) {
output.force_semicolon();
}
in_directive = false;
}
}
if (in_directive === true &&
stmt instanceof AST_SimpleStatement &&
stmt.body instanceof AST_String
) {
in_directive = false;
}
if (stmt instanceof AST_EmptyStatement) return;
output.indent();
stmt.print(output);
if (i == last && is_toplevel) return;
output.newline();
if (is_toplevel) output.newline();
});
in_directive = false;
use_asm = was_asm;
}
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
@@ -1254,29 +1240,10 @@ function OutputStream(options) {
output.print(self.operator);
});
DEFPRINT(AST_Binary, function(self, output) {
var op = self.operator;
self.left.print(output);
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
&& self.left instanceof AST_UnaryPostfix
&& self.left.operator == "--") {
// space is mandatory to avoid outputting -->
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.print(op);
if ((op == "<" || op == "<<")
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "!"
&& self.right.expression instanceof AST_UnaryPrefix
&& self.right.expression.operator == "--") {
// space is mandatory to avoid outputting <!--
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.space();
output.print(self.operator);
output.space();
self.right.print(output);
});
DEFPRINT(AST_Conditional, function(self, output) {
@@ -1367,7 +1334,7 @@ function OutputStream(options) {
output.print(self.getValue());
});
DEFPRINT(AST_String, function(self, output) {
output.print_string(self.getValue(), self.quote, in_directive);
output.print_string(self.getValue(), self.quote);
});
DEFPRINT(AST_Number, function(self, output) {
if (use_asm && self.start && self.start.raw != null) {

View File

@@ -790,9 +790,10 @@ function parse($TEXT, options) {
var dir = S.in_directives;
var body = expression(true);
if (dir) {
var token = body.start;
if (body instanceof AST_String && token.raw.indexOf("\\") == -1) {
S.input.add_directive(token.value);
if (body instanceof AST_String) {
var value = body.start.raw.slice(1, -1);
S.input.add_directive(value);
body.value = value;
} else {
S.in_directives = dir = false;
}

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.1",
"version": "3.7.3",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -1047,12 +1047,11 @@ collapse_vars_repeated: {
var a = 3, a = x;
return a;
}
(function(x){
(function(x) {
var a = "GOOD" + x, e = "BAD", k = "!", e = a;
console.log(e + k);
})("!"),
(function(x){
(function(x) {
var a = "GOOD" + x, e = "BAD" + x, k = "!", e = a;
console.log(e + k);
})("!");
@@ -1064,10 +1063,10 @@ collapse_vars_repeated: {
function f2(x) {
return x;
}
(function(x){
(function(x) {
console.log("GOOD!!");
})(),
(function(x){
(function(x) {
console.log("GOOD!!");
})();
}
@@ -2579,6 +2578,23 @@ chained_3: {
expect_stdout: "2"
}
chained_4: {
options = {
collapse_vars: true,
}
input: {
var a = "foo", b = 42;
var b = void (b = a);
console.log(a, b);
}
expect: {
var a = "foo", b = 42;
var b = void (b = a);
console.log(a, b);
}
expect_stdout: "foo undefined"
}
boolean_binary_1: {
options = {
collapse_vars: true,
@@ -4566,8 +4582,8 @@ replace_all_var_scope: {
var a = 100, b = 10;
(function(r, a) {
switch (~a) {
case (b += a):
case a++:
case (b += a):
case a++:
}
})(--b, a);
console.log(a, b);
@@ -4576,8 +4592,8 @@ replace_all_var_scope: {
var a = 100, b = 10;
(function(c, o) {
switch (~a) {
case (b += a):
case o++:
case (b += a):
case o++:
}
})(--b, a);
console.log(a, b);
@@ -6965,3 +6981,444 @@ setter_side_effect: {
}
expect_stdout: "PASS"
}
substitution_assign: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
f1 = b = a;
console.log(a, b);
}
function f2(a, b) {
a = 1 + (b = a);
console.log(a, b);
}
function f3(a, b) {
b = 1 + (b = a);
console.log(a, b);
}
f1(42, "foo");
f2(42, "foo");
f3(42, "foo");
}
expect: {
function f1(a, b) {
f1 = a;
console.log(a, a);
}
function f2(a, b) {
a = 1 + (b = a);
console.log(a, b);
}
function f3(a, b) {
b = 1 + (b = a);
console.log(a, b);
}
f1(42, "foo");
f2(42, "foo");
f3(42, "foo");
}
expect_stdout: [
"42 42",
"43 42",
"42 43",
]
}
substitution_arithmetic: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
console.log((b = a) + a, b);
}
function f2(a, b) {
console.log(a - (b = a), b);
}
function f3(a, b) {
console.log(a / (b = a) + b, b);
}
f1(42, "foo");
f2(42, "foo");
f3(42, "foo");
}
expect: {
function f1(a, b) {
console.log(a + a, a);
}
function f2(a, b) {
console.log(a - a, a);
}
function f3(a, b) {
console.log(a / a + a, a);
}
f1(42, "foo");
f2(42, "foo");
f3(42, "foo");
}
expect_stdout: [
"84 42",
"0 42",
"43 42",
]
}
substitution_logical_1: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
console.log((b = a) && a, b);
}
function f2(a, b) {
console.log(a && (b = a), b);
}
f1(42, "foo");
f1(null, true);
f2(42, "foo");
f2(null, true);
}
expect: {
function f1(a, b) {
console.log(a && a, a);
}
function f2(a, b) {
console.log(a && (b = a), b);
}
f1(42, "foo");
f1(null, true);
f2(42, "foo");
f2(null, true);
}
expect_stdout: [
"42 42",
"null null",
"42 42",
"null true"
]
}
substitution_logical_2: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
console.log((b = a) && a && b);
}
function f2(a, b) {
console.log((b = a) && a || b);
}
function f3(a, b) {
console.log((b = a) || a && b);
}
function f4(a, b) {
console.log((b = a) || a || b);
}
f1(42, "foo");
f1(null, true);
f2(42, "foo");
f2(null, true);
f3(42, "foo");
f3(null, true);
f4(42, "foo");
f4(null, true);
}
expect: {
function f1(a, b) {
console.log(a && a && a);
}
function f2(a, b) {
console.log(a && a || a);
}
function f3(a, b) {
console.log(a || a && a);
}
function f4(a, b) {
console.log(a || a || a);
}
f1(42, "foo");
f1(null, true);
f2(42, "foo");
f2(null, true);
f3(42, "foo");
f3(null, true);
f4(42, "foo");
f4(null, true);
}
expect_stdout: [
"42",
"null",
"42",
"null",
"42",
"null",
"42",
"null",
]
}
substitution_logical_3: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
console.log(a && (b = a) && b);
}
function f2(a, b) {
console.log(a && (b = a) || b);
}
function f3(a, b) {
console.log(a || (b = a) && b);
}
function f4(a, b) {
console.log(a || (b = a) || b);
}
f1(42, "foo");
f1(null, true);
f2(42, "foo");
f2(null, true);
f3(42, "foo");
f3(null, true);
f4(42, "foo");
f4(null, true);
}
expect: {
function f1(a, b) {
console.log(a && a && a);
}
function f2(a, b) {
console.log(a && (b = a) || b);
}
function f3(a, b) {
console.log(a || a && a);
}
function f4(a, b) {
console.log(a || a || a);
}
f1(42, "foo");
f1(null, true);
f2(42, "foo");
f2(null, true);
f3(42, "foo");
f3(null, true);
f4(42, "foo");
f4(null, true);
}
expect_stdout: [
"42",
"null",
"42",
"true",
"42",
"null",
"42",
"null",
]
}
substitution_conditional: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
console.log((b = a) ? a : b, a, b);
}
function f2(a, b) {
console.log(a ? b = a : b, a, b);
}
function f3(a, b) {
console.log(a ? a : b = a, a, b);
}
f1("foo", "bar");
f1(null, true);
f2("foo", "bar");
f2(null, true);
f3("foo", "bar");
f3(null, true);
}
expect: {
function f1(a, b) {
console.log(a ? a : a, a, a);
}
function f2(a, b) {
console.log(a ? b = a : b, a, b);
}
function f3(a, b) {
console.log(a ? a : b = a, a, b);
}
f1("foo", "bar");
f1(null, true);
f2("foo", "bar");
f2(null, true);
f3("foo", "bar");
f3(null, true);
}
expect_stdout: [
"foo foo foo",
"null null null",
"foo foo foo",
"true null true",
"foo foo bar",
"null null null",
]
}
substitution_unary: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
console.log(typeof (b = a), a, b);
}
function f2(a, b) {
console.log(void (b = a), a, b);
}
function f3(a, b) {
console.log(delete (b = a), a, b);
}
f1(42, "foo");
f2(42, "foo");
f3(42, "foo");
}
expect: {
function f1(a, b) {
console.log(typeof a, a, a);
}
function f2(a, b) {
console.log(void a, a, a);
}
function f3(a, b) {
console.log(delete (b = a), a, b);
}
f1(42, "foo");
f2(42, "foo");
f3(42, "foo");
}
expect_stdout: [
"number 42 42",
"undefined 42 42",
"true 42 42",
]
}
issue_3626_1: {
options = {
collapse_vars: true,
}
input: {
var a = "foo", b = 42;
a.p && (b = a) && a;
console.log(a, b);
}
expect: {
var a = "foo", b = 42;
a.p && (b = a) && a;
console.log(a, b);
}
expect_stdout: "foo 42"
}
issue_3626_2: {
options = {
collapse_vars: true,
conditionals: true,
}
input: {
var a = "foo", b = 42, c = null;
if (a && a.p)
if (b = a)
c++ + a;
console.log(a, b, c);
}
expect: {
var a = "foo", b = 42, c = null;
a && a.p && (b = a) && c++ + a;
console.log(a, b, c);
}
expect_stdout: "foo 42 null"
}
issue_3628_1: {
options = {
collapse_vars: true,
}
input: {
var a = "bar", b;
({
get p() {
a = "foo";
},
q: b = a
}).p;
console.log(a, b);
}
expect: {
var a = "bar", b;
({
get p() {
a = "foo";
},
q: b = a
}).p;
console.log(a, b);
}
expect_stdout: "foo bar"
}
issue_3628_2: {
options = {
collapse_vars: true,
}
input: {
var a = "bar", b;
({
get p() {
a = "foo";
},
q: (b = a, 42)
}).p;
console.log(a, b);
}
expect: {
var a = "bar", b;
({
get p() {
a = "foo";
},
q: (b = a, 42)
}).p;
console.log(a, b);
}
expect_stdout: "foo bar"
}
issue_3641: {
options = {
collapse_vars: true,
}
input: {
var a, b;
try {
a = "foo";
b = (a += (A.p = 0, "bar")) % 0;
} catch (e) {}
console.log(a, b);
}
expect: {
var a, b;
try {
a = "foo";
b = (a += (A.p = 0, "bar")) % 0;
} catch (e) {}
console.log(a, b);
}
expect_stdout: "foo undefined"
}

View File

@@ -33,10 +33,10 @@ unsafe_comps: {
}
expect: {
var obj1, obj2;
obj2 < obj1 ? g1() : f1();
obj1 < obj2 ? f2() : g2();
obj1 < obj2 ? g3() : f3();
obj2 < obj1 ? f4() : g4();
(obj2 < obj1 ? g1 : f1)();
(obj1 < obj2 ? f2 : g2)();
(obj1 < obj2 ? g3 : f3)();
(obj2 < obj1 ? f4 : g4)();
}
}

View File

@@ -41,7 +41,7 @@ ifs_2: {
}
expect: {
foo ? x() : bar ? y() : baz && z();
foo ? x() : bar ? y() : baz ? z() : t();
(foo ? x : bar ? y : baz ? z : t)();
}
}
@@ -289,7 +289,7 @@ cond_5: {
}
}
expect: {
some_condition() && some_other_condition() ? do_something() : alternate();
(some_condition() && some_other_condition() ? do_something : alternate)();
some_condition() && some_other_condition() && do_something();
}
}
@@ -663,6 +663,69 @@ cond_9: {
}
}
cond_10: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f(a) {
if (1 == a) return "foo";
if (2 == a) return "foo";
if (3 == a) return "foo";
if (4 == a) return 42;
if (5 == a) return "foo";
if (6 == a) return "foo";
return "bar";
}
console.log(f(1), f(2), f(3), f(4), f(5), f(6), f(7));
}
expect: {
function f(a) {
return 1 == a || 2 == a || 3 == a ? "foo" : 4 == a ? 42 : 5 == a || 6 == a ? "foo" : "bar";
}
console.log(f(1), f(2), f(3), f(4), f(5), f(6), f(7));
}
expect_stdout: "foo foo foo 42 foo foo bar"
}
cond_11: {
options = {
conditionals: true,
}
input: {
var o = {
p: "foo",
q: function() {
return this.p;
}
};
function f() {
return "bar";
}
function g(a) {
return a ? f() : o.q();
}
console.log(g(0), g(1));
}
expect: {
var o = {
p: "foo",
q: function() {
return this.p;
}
};
function f() {
return "bar";
}
function g(a) {
return a ? f() : o.q();
}
console.log(g(0), g(1));
}
expect_stdout: "foo bar"
}
ternary_boolean_consequent: {
options = {
booleans: true,

View File

@@ -93,3 +93,41 @@ issue_3166: {
}
}
}
valid_after_invalid_1: {
input: {
console.log(typeof function() {
"use\x20strict";
"use strict";
return this;
}());
}
expect: {
console.log(typeof function() {
"use\x20strict";
"use strict";
return this;
}());
}
expect_stdout: "undefined"
}
valid_after_invalid_2: {
options = {
directives: true,
}
input: {
console.log(typeof function() {
"use\x20strict";
"use strict";
return this;
}());
}
expect: {
console.log(typeof function() {
"use strict";
return this;
}());
}
expect_stdout: "undefined"
}

View File

@@ -1,55 +1,107 @@
html_comment_in_expression: {
input: {
function f(a, b, x, y) { return a < !--b && x-- > y; }
(function(a, b) {
console.log(a < !--b && a-- > b, a, b);
})(1, 2);
}
expect_exact: "function f(a,b,x,y){return a< !--b&&x-- >y}";
expect_exact: "(function(a,b){console.log(a<! --b&&a-- >b,a,b)})(1,2);"
expect_stdout: "false 1 1"
}
html_comment_in_less_than: {
input: {
function f(a, b) { return a < !--b; }
(function(a, b, c) {
console.log(
a < !--b,
a < !--b + c,
a + b < !--c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a< !--b}";
expect_exact: "(function(a,b,c){console.log(a<! --b,a<! --b+c,a+b<! --c,a,b,c)})(1,2,3);"
expect_stdout: "false true false 1 0 2"
}
html_comment_in_left_shift: {
input: {
function f(a, b) { return a << !--b; }
(function(a, b, c) {
console.log(
a << !--b,
a << !--b + c,
a + b << !--c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a<< !--b}";
}
html_comment_in_right_shift: {
input: {
function f(a, b) { return a-- >> b; }
}
expect_exact: "function f(a,b){return a-- >>b}";
}
html_comment_in_zero_fill_right_shift: {
input: {
function f(a, b) { return a-- >>> b; }
}
expect_exact: "function f(a,b){return a-- >>>b}";
expect_exact: "(function(a,b,c){console.log(a<<! --b,a<<! --b+c,a+b<<! --c,a,b,c)})(1,2,3);"
expect_stdout: "1 16 1 1 0 2"
}
html_comment_in_greater_than: {
input: {
function f(a, b) { return a-- > b; }
(function(a, b, c) {
console.log(
a-- > b,
a-- > b + c,
a + b-- > c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a-- >b}";
expect_exact: "(function(a,b,c){console.log(a-- >b,a-- >b+c,a+b-- >c,a,b,c)})(1,2,3);"
expect_stdout: "false false false -1 1 3"
}
html_comment_in_greater_than_or_equal: {
input: {
function f(a, b) { return a-- >= b; }
(function(a, b, c) {
console.log(
a-- >= b,
a-- >= b + c,
a + b-- >= c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a-- >=b}";
expect_exact: "(function(a,b,c){console.log(a-- >=b,a-- >=b+c,a+b-- >=c,a,b,c)})(1,2,3);"
expect_stdout: "false false false -1 1 3"
}
html_comment_in_right_shift: {
input: {
(function(a, b, c) {
console.log(
a-- >> b,
a-- >> b + c,
a + b-- >> c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "(function(a,b,c){console.log(a-- >>b,a-- >>b+c,a+b-- >>c,a,b,c)})(1,2,3);"
expect_stdout: "0 0 0 -1 1 3"
}
html_comment_in_zero_fill_right_shift: {
input: {
(function(a, b, c) {
console.log(
a-- >>> b,
a-- >>> b + c,
a + b-- >>> c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "(function(a,b,c){console.log(a-- >>>b,a-- >>>b+c,a+b-- >>>c,a,b,c)})(1,2,3);"
expect_stdout: "0 0 0 -1 1 3"
}
html_comment_in_string_literal: {
input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
console.log("<!--HTML-->comment in<!--string literal-->".length);
}
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
expect_exact: 'console.log("\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e".length);'
expect_stdout: "42"
}

View File

@@ -30,7 +30,7 @@ non_hoisted_function_after_return: {
}
expect: {
function foo(x) {
return x ? bar() : baz();
return (x ? bar : baz)();
function bar() { return 7 }
function baz() { return 8 }
}
@@ -181,7 +181,7 @@ non_hoisted_function_after_return_strict: {
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
return (x ? bar : baz)();
function bar() { return 7 }
function baz() { return 8 }
}

View File

@@ -21,7 +21,7 @@ cond_5: {
}
}
expect: {
some_condition() && some_other_condition() ? do_something() : alternate();
(some_condition() && some_other_condition() ? do_something : alternate)();
if (some_condition() && some_other_condition()) do_something();
}
}

View File

@@ -873,13 +873,13 @@ iife_func_side_effects: {
function z() {
console.log("z");
}
(function(a, b) {
(function(b) {
return function() {
console.log("FAIL");
} + b();
})(x(), function() {
})((x(), function() {
return y();
}, z());
}), z());
}
expect_stdout: [
"x",
@@ -1155,3 +1155,303 @@ issue_3423_2: {
}
expect_stdout: "1"
}
collapse_vars_repeated: {
options = {
booleans: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
evaluate: true,
hoist_funs: true,
if_return: true,
join_vars: true,
keep_fargs: "strict",
loops: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var dummy = 3, a = 5, unused = 2, a = 1, a = 3;
return -a;
}
function f2(x) {
var a = 3, a = x;
return a;
}
(function(x) {
var a = "GOOD" + x, e = "BAD", k = "!", e = a;
console.log(e + k);
})("!"),
(function(x) {
var a = "GOOD" + x, e = "BAD" + x, k = "!", e = a;
console.log(e + k);
})("!");
}
expect: {
function f1() {
return -3;
}
function f2(x) {
return x;
}
(function() {
console.log("GOOD!!");
})(),
(function() {
console.log("GOOD!!");
})();
}
expect_stdout: true
}
chained_3: {
options = {
collapse_vars: true,
keep_fargs: "strict",
unused: true,
}
input: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect: {
console.log(function(b) {
var c = 1;
c = b;
b++;
return c;
}(2));
}
expect_stdout: "2"
}
replace_all_var_scope: {
rename = true
options = {
collapse_vars: true,
keep_fargs: "strict",
unused: true,
}
mangle = {}
input: {
var a = 100, b = 10;
(function(r, a) {
switch (~a) {
case (b += a):
case a++:
}
})(--b, a);
console.log(a, b);
}
expect: {
var a = 100, b = 10;
(function(c) {
switch (~a) {
case (b += a):
case c++:
}
})((--b, a));
console.log(a, b);
}
expect_stdout: "100 109"
}
issue_1583: {
options = {
keep_fargs: "strict",
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function m(t) {
(function(e) {
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
expect: {
function m(t) {
(function() {
(function() {
return (function() {
return function(a) {};
})();
})();
})();
}
}
}
issues_3267_1: {
options = {
collapse_vars: true,
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(x) {
x();
})(function() {
(function(i) {
if (i)
return console.log("PASS");
throw "FAIL";
})(Object());
});
}
expect: {
!function() {
if (Object())
return console.log("PASS");
throw "FAIL";
}();
}
expect_stdout: "PASS"
}
trailing_argument_side_effects: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
function f() {
return "FAIL";
}
console.log(function(a, b) {
return b || "PASS";
}(f()));
}
expect: {
function f() {
return "FAIL";
}
console.log(function(b) {
return b || "PASS";
}(void f()));
}
expect_stdout: "PASS"
}
recursive_iife_1: {
options = {
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}());
}
expect: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}());
}
expect_stdout: "PASS"
}
recursive_iife_2: {
options = {
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}(null, 0));
}
expect: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}(0, 0));
}
expect_stdout: "PASS"
}
recursive_iife_3: {
options = {
keep_fargs: false,
reduce_vars: true,
unused: true,
}
input: {
var a = 1, c = "PASS";
(function() {
function f(b, d, e) {
a-- && f(null, 42, 0);
e && (c = "FAIL");
d && d.p;
}
var a_1 = f();
})();
console.log(c);
}
expect: {
var a = 1, c = "PASS";
(function() {
(function f(b, d, e) {
a-- && f(null, 42, 0);
e && (c = "FAIL");
d && d.p;
})();
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3619: {
options = {
keep_fargs: false,
unused: true,
}
input: {
var a = 1, b = "FAIL";
(function f(c, d) {
function g() {
d && (b = "PASS", 0 <= --a && g());
0 <= --a && f(0, "function");
}
g();
})();
console.log(b);
}
expect: {
var a = 1, b = "FAIL";
(function f(c, d) {
function g() {
d && (b = "PASS", 0 <= --a && g());
0 <= --a && f(0, "function");
}
g();
})();
console.log(b);
}
expect_stdout: "PASS"
}

View File

@@ -574,9 +574,11 @@ issue_2740_3: {
console.log(x, y);
}
expect: {
L1: for (var x = 0; x < 3; x++)
for (var y = 0; y < 2; y++)
L1: for (var x = 0; x < 3; x++) {
var y = 0;
if (y < 2)
break L1;
}
console.log(x, y);
}
expect_stdout: "0 0"
@@ -753,3 +755,175 @@ empty_for_in_side_effects: {
"WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]",
]
}
issue_3631_1: {
options = {
dead_code: true,
evaluate: true,
loops: true,
reduce_vars: true,
toplevel: true,
}
input: {
var c = 0;
L: do {
for (;;) continue L;
var b = 1;
} while (b && c++);
console.log(c);
}
expect: {
var c = 0;
do {
var b;
} while (b && c++);
console.log(c);
}
expect_stdout: "0"
}
issue_3631_2: {
options = {
dead_code: true,
evaluate: true,
loops: true,
reduce_vars: true,
toplevel: true,
}
input: {
L: for (var a = 1; a--; console.log(b)) {
for (;;) continue L;
var b = "FAIL";
}
}
expect: {
for (var a = 1; a--; console.log(b))
var b;
}
expect_stdout: "undefined"
}
loop_if_break: {
options = {
dead_code: true,
loops: true,
}
input: {
function f(a, b) {
try {
while (a) {
if (b) {
break;
var c = 42;
console.log(c);
} else {
var d = false;
throw d;
}
}
} catch (e) {
console.log("E:", e);
}
console.log(a, b, c, d);
}
f(0, 0);
f(0, 1);
f(1, 0);
f(1, 1);
}
expect: {
function f(a, b) {
try {
for (;a && !b;) {
var d = false;
throw d;
var c;
}
} catch (e) {
console.log("E:", e);
}
console.log(a, b, c, d);
}
f(0, 0);
f(0, 1);
f(1, 0);
f(1, 1);
}
expect_stdout: [
"0 0 undefined undefined",
"0 1 undefined undefined",
"E: false",
"1 0 undefined false",
"1 1 undefined undefined",
]
}
loop_return: {
options = {
dead_code: true,
loops: true,
}
input: {
function f(a) {
while (a) return 42;
return "foo";
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
if (a) return 42;
return "foo";
}
console.log(f(0), f(1));
}
expect_stdout: "foo 42"
}
issue_3634_1: {
options = {
loops: true,
}
input: {
var b = 0;
L: while (++b < 2)
while (1)
if (b) break L;
console.log(b);
}
expect: {
var b = 0;
L: for (;++b < 2;)
for (;1;)
if (b) break L;
console.log(b);
}
expect_stdout: "1"
}
issue_3634_2: {
options = {
loops: true,
}
input: {
var b = 0;
L: while (++b < 2)
while (1)
if (!b)
continue L;
else
break L;
console.log(b);
}
expect: {
var b = 0;
L: for (;++b < 2;)
for (;1;)
if (!b)
continue L;
else
break L;
console.log(b);
}
expect_stdout: "1"
}

View File

@@ -6774,3 +6774,76 @@ issue_3509: {
}
expect_stdout: "PASS"
}
issue_3622: {
options = {
evaluate: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var c = "FAIL";
!function(b, a) {
a && (c = "PASS");
}(42, this);
console.log(c);
}
expect: {
var c = "FAIL";
var a;
a = this,
!void (a && (c = "PASS")),
console.log(c);
}
expect_stdout: "PASS"
}
issue_3631_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var c = 0;
L: do {
for (;;) continue L;
var b = 1;
} while (b && c++);
console.log(c);
}
expect: {
var c = 0;
L: do {
for (;;) continue L;
var b = 1;
} while (b && c++);
console.log(c);
}
expect_stdout: "0"
}
issue_3631_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
L: for (var a = 1; a--; console.log(b)) {
for (;;) continue L;
var b = "FAIL";
}
}
expect: {
L: for (var a = 1; a--; console.log(b)) {
for (;;) continue L;
var b = "FAIL";
}
}
expect_stdout: "undefined"
}

View File

@@ -69,13 +69,13 @@ describe("Directives", function() {
],
[
'"use \\\nstrict";"use strict";',
[],
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
[ "use strict" ],
[ "use\nstrict", "use \nstrict", "use asm" ]
],
[
'"\\76";',
[],
[ ">", "\\76" ]
[ "\\76" ],
[ ">" ]
],
[
// no ; or newline
@@ -106,13 +106,13 @@ describe("Directives", function() {
],
[
'function foo() {"use \\\nstrict";"use strict";',
[],
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
[ "use strict" ],
[ "use\nstrict", "use \nstrict", "use asm" ]
],
[
'var foo = function() {"\\76";',
[],
[ ">", "\\76" ]
[ "\\76" ],
[ ">" ]
],
[
'var foo = function() {"use strict"', // no ; or newline
@@ -156,21 +156,24 @@ describe("Directives", function() {
});
});
});
it("Should test EXPECT_DIRECTIVE RegExp", function() {
it("Should print semicolon to separate strings from directives", function() {
[
[ "", true ],
[ "'test';", true ],
[ "'test';;", true ],
[ "'tests';\n", true ],
[ "'tests'", false ],
[ "'tests'; \n\t", true ],
[ "'tests';\n\n", true ],
[ "\n\n\"use strict\";\n\n", true ],
[ "", ';"";' ],
[ '"test";', '"test";;"";' ],
[ '"test";;', '"test";;"";' ],
[ '"tests";\n', '"tests";;"";' ],
[ '"tests"', '"tests";;"";' ],
[ '"tests"; \n\t', '"tests";;"";' ],
[ '"tests";\n\n', '"tests";;"";' ],
[ '\n\n"use strict";\n\n', '"use strict";;"";' ],
].forEach(function(test) {
var ast = UglifyJS.parse(test[0]);
ast.body.push(new UglifyJS.AST_SimpleStatement({
body: new UglifyJS.AST_String({ value: "" })
}));
var out = UglifyJS.OutputStream();
out.print(test[0]);
out.print_string("", null, true);
assert.strictEqual(out.get() === test[0] + ';""', test[1], test[0]);
ast.print(out);
assert.strictEqual(out.get(), test[1], test[0]);
});
});
it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() {
@@ -178,8 +181,8 @@ describe("Directives", function() {
'"use strict";',
"'use strict';",
'"use strict";',
'"use strict";;',
"'use strict';",
'"use strict";',
";'use strict';",
"console.log('use strict');"
].join(""), {
compress: false,
@@ -201,19 +204,23 @@ describe("Directives", function() {
it("Should not add double semicolons in non-scoped block statements to avoid strings becoming directives", function() {
[
[
'{"use\x20strict"}',
'"use strict";"use\\x20strict";',
'"use strict";"use\\x20strict";'
],
[
'{"use\\x20strict"}',
'{"use strict"}'
],
[
'function foo(){"use\x20strict";}', // Valid place for directives
'function foo(){"use strict"}'
'function foo(){"use\\x20strict";}', // Valid place for directives
'function foo(){"use\\x20strict"}'
],
[
'try{"use\x20strict"}catch(e){}finally{"use\x20strict"}',
'try{"use\\x20strict"}catch(e){}finally{"use\\x20strict"}',
'try{"use strict"}catch(e){}finally{"use strict"}'
],
[
'if(1){"use\x20strict"} else {"use strict"}',
'if(1){"use\\x20strict"} else {"use strict"}',
'if(1){"use strict"}else{"use strict"}'
]
].forEach(function(test) {
@@ -225,16 +232,6 @@ describe("Directives", function() {
assert.strictEqual(result.code, test[1], test[0]);
});
});
it("Should add double semicolon when relying on automatic semicolon insertion", function() {
var result = UglifyJS.minify('"use strict";"use\\x20strict";', {
compress: false,
output: {
semicolons: false
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code, '"use strict";;"use strict"\n');
});
it("Should check quote style of directives", function() {
[
// 0. Prefer double quotes, unless string contains more double quotes than single quotes
@@ -249,9 +246,9 @@ describe("Directives", function() {
'"use strict";'
],
[
'"\\\'use strict\\\'";', // Not a directive as it contains quotes
'"\\\'use strict\\\'";',
0,
';"\'use strict\'";',
'"\\\'use strict\\\'";',
],
[
"'\"use strict\"';",
@@ -273,7 +270,7 @@ describe("Directives", function() {
'"\'use strict\'";',
1,
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
"'\\'use strict\\'';",
'"\'use strict\'";',
],
[
"'\\'use strict\\'';", // Not a valid directive
@@ -305,7 +302,7 @@ describe("Directives", function() {
"'\"use strict\"';",
2,
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
'"\\\"use strict\\\"";',
"'\"use strict\"';",
],
[
'"\\"use strict\\"";', // Not a valid directive
@@ -353,8 +350,7 @@ describe("Directives", function() {
[
// Nothing gets optimised in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;',
// Yet, the parser noticed that "use strict" wasn't a directive
'"use asm";;"use strict";1+1;',
'"use asm";"use\\x20strict";1+1;'
],
[
'function f(){ "use strict" }',

View File

@@ -59,13 +59,13 @@ describe("String literals", function() {
it("Should not throw error outside strict mode if string contains escaped octalIntegerLiteral", function() {
var tests = [
['"\\76";', ';">";'],
['"\\0"', '"\\0";'],
['"\\08"', '"\\x008";'],
['"\\008"', '"\\x008";'],
['"\\0008"', '"\\x008";'],
['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'],
['"use\\\n strict";\n"\\07";', ';"use strict";"\07";']
[ ';"\\76";', ';">";' ],
[ ';"\\0";', ';"\\0";' ],
[ ';"\\08"', ';"\\x008";' ],
[ ';"\\008"', ';"\\x008";' ],
[ ';"\\0008"', ';"\\x008";' ],
[ ';"use\\\n strict";\n"\\07";', ';"use strict";"\07";' ],
[ '"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";' ],
];
for (var test in tests) {
@@ -75,8 +75,8 @@ describe("String literals", function() {
});
it("Should not throw error when digit is 8 or 9", function() {
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\x008";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\x009";');
assert.equal(UglifyJS.parse('"use strict";;"\\08"').print_to_string(), '"use strict";;"\\x008";');
assert.equal(UglifyJS.parse('"use strict";;"\\09"').print_to_string(), '"use strict";;"\\x009";');
});
it("Should not unescape unpaired surrogates", function() {
@@ -93,7 +93,7 @@ describe("String literals", function() {
for (; i <= 0xFFFF; i++) {
code.push("\\u" + i.toString(16));
}
code = '"' + code.join() + '"';
code = ';"' + code.join() + '"';
var normal = UglifyJS.minify(code, {
compress: false,
mangle: false,

View File

@@ -1089,6 +1089,19 @@ function log(options) {
}
}
function fuzzy_match(original, uglified) {
original = original.split(" ", 5);
uglified = uglified.split(" ", 5);
for (var i = 0; i < 5; i++) {
if (original[i] === uglified[i]) continue;
var a = +original[i];
var b = +uglified[i];
if (Math.abs((b - a) / a) < 1e-10) continue;
return false;
}
return true;
}
var fallback_options = [ JSON.stringify({
compress: false,
mangle: false
@@ -1111,8 +1124,12 @@ for (var round = 1; round <= num_iterations; round++) {
uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code, o.toplevel);
ok = sandbox.same_stdout(original_result, uglify_result);
if (!ok && o.compress.unsafe_math) {
ok = sandbox.same_stdout(sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3")), uglify_result, o.toplevel);
if (!ok && typeof uglify_result == "string" && o.compress.unsafe_math) {
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);
}
}
} else {
uglify_code = uglify_code.error;

View File

@@ -12,17 +12,16 @@ function spawn(endTime) {
], {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
var line = "";
var stdout = "";
child.stdout.on("data", function(data) {
line += data;
stdout += data;
});
child.stderr.once("data", function() {
process.exitCode = 1;
}).pipe(process.stdout);
var stderr = "";
child.stderr.on("data", trap).pipe(process.stdout);
var keepAlive = setInterval(function() {
var end = line.lastIndexOf("\r");
console.log(line.slice(line.lastIndexOf("\r", end - 1) + 1, end));
line = line.slice(end + 1);
var end = stdout.lastIndexOf("\r");
console.log(stdout.slice(stdout.lastIndexOf("\r", end - 1) + 1, end));
stdout = stdout.slice(end + 1);
}, ping);
var timer = setTimeout(function() {
clearInterval(keepAlive);
@@ -31,9 +30,17 @@ function spawn(endTime) {
}, endTime - Date.now());
function respawn() {
console.log(line);
console.log(stdout.replace(/[^\r\n]*\r/g, ""));
clearInterval(keepAlive);
clearTimeout(timer);
spawn(endTime);
}
function trap(data) {
stderr += data;
if (~stderr.indexOf("\nminify(options):\n")) {
process.exitCode = 1;
child.stderr.removeListener("data", trap);
}
}
}