Compare commits

...

9 Commits

Author SHA1 Message Date
Alex Lam S.L
919d5e3482 v2.8.12 2017-03-11 05:00:55 +08:00
Alex Lam S.L
e3a3db73ae temporary fix for boolean bug (#1597)
fixes #1592
2017-03-11 04:59:55 +08:00
Alex Lam S.L
d9344f30b8 disallow parameter substitution for named IIFEs (#1596)
Self-referenced function has non-fixed values assigned to its parameters.

Let `unused` & `!keep_fnames` do the scanning, then apply `reduce_vars` only to unnamed functions.

fixes #1595
2017-03-11 03:34:55 +08:00
Alex Lam S.L
be80f7e706 support multi-line string in tests (#1590)
`expect_exact` sometimes have multiple lines and `\n` are hard to read.

Use array of strings to emulate line breaks and improve readability.
2017-03-10 11:27:30 +08:00
Alex Lam S.L
cf45e2f79b fixup for #1585 (#1589)
As patched on `harmony`, `statement()` is the only user of `embed_tokens()` with a missing error branch.

Updated test case and match up with `harmony` to facilitate future merging.
2017-03-10 10:49:41 +08:00
Alex Lam S.L
8354758f30 v2.8.11 2017-03-10 04:17:21 +08:00
Alex Lam S.L
9e6b128374 fix catch variable reference in IE8 (#1587)
`AST_Scope.def_variable()` will overwrite `AST_Symbol.thedef`, so save a copy before calling.

fixes #1586
2017-03-10 03:15:21 +08:00
Michael Mior
93cdb194f4 Correctly raise a parse exception with a missing loop body (#1585) 2017-03-10 03:08:43 +08:00
Alex Lam S.L
b633706ce4 fix & improve function argument compression (#1584)
- one-use function call => IIFE should take `eval()` & `arguments` into account
- if unused parameter cannot be eliminated, replace it with `0`

fixes #1583
2017-03-09 19:11:05 +08:00
12 changed files with 379 additions and 31 deletions

View File

@@ -279,8 +279,12 @@ merge(Compressor.prototype, {
} }
var iife; var iife;
if (node instanceof AST_Function if (node instanceof AST_Function
&& !node.name
&& (iife = tw.parent()) instanceof AST_Call && (iife = tw.parent()) instanceof AST_Call
&& iife.expression === node) { && iife.expression === node) {
// Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
node.argnames.forEach(function(arg, i) { node.argnames.forEach(function(arg, i) {
var d = arg.definition(); var d = arg.definition();
d.fixed = iife.args[i] || make_node(AST_Undefined, iife); d.fixed = iife.args[i] || make_node(AST_Undefined, iife);
@@ -400,7 +404,7 @@ merge(Compressor.prototype, {
return make_node(AST_Number, orig, { value: val }); return make_node(AST_Number, orig, { value: val });
case "boolean": case "boolean":
return make_node(val ? AST_True : AST_False, orig).transform(compressor); return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
case "undefined": case "undefined":
return make_node(AST_Undefined, orig).transform(compressor); return make_node(AST_Undefined, orig).transform(compressor);
default: default:
@@ -1810,10 +1814,12 @@ merge(Compressor.prototype, {
node.name = null; node.name = null;
} }
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
if (!compressor.option("keep_fargs")) { var trim = !compressor.option("keep_fargs");
for (var a = node.argnames, i = a.length; --i >= 0;) { for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i]; var sym = a[i];
if (!(sym.definition().id in in_use_ids)) { if (!(sym.definition().id in in_use_ids)) {
sym.__unused = true;
if (trim) {
a.pop(); a.pop();
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", { compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
name : sym.name, name : sym.name,
@@ -1822,7 +1828,9 @@ merge(Compressor.prototype, {
col : sym.start.col col : sym.start.col
}); });
} }
else break; }
else {
trim = false;
} }
} }
} }
@@ -2609,6 +2617,9 @@ merge(Compressor.prototype, {
exp = def.fixed; exp = def.fixed;
if (compressor.option("unused") if (compressor.option("unused")
&& def.references.length == 1 && def.references.length == 1
&& !(def.scope.uses_arguments
&& def.orig[0] instanceof AST_SymbolFunarg)
&& !def.scope.uses_eval
&& compressor.find_parent(AST_Scope) === def.scope) { && compressor.find_parent(AST_Scope) === def.scope) {
self.expression = exp; self.expression = exp;
} }
@@ -2617,16 +2628,26 @@ merge(Compressor.prototype, {
if (compressor.option("unused") if (compressor.option("unused")
&& exp instanceof AST_Function && exp instanceof AST_Function
&& !exp.uses_arguments && !exp.uses_arguments
&& !exp.uses_eval && !exp.uses_eval) {
&& self.args.length > exp.argnames.length) { var pos = 0, last = 0;
var end = exp.argnames.length; for (var i = 0, len = self.args.length; i < len; i++) {
for (var i = end, len = self.args.length; i < len; i++) { var trim = i >= exp.argnames.length;
var node = self.args[i].drop_side_effect_free(compressor); if (trim || exp.argnames[i].__unused) {
if (node) { var node = self.args[i].drop_side_effect_free(compressor);
self.args[end++] = node; if (node) {
self.args[pos++] = node;
} else if (!trim) {
self.args[pos++] = make_node(AST_Number, self.args[i], {
value: 0
});
continue;
}
} else {
self.args[pos++] = self.args[i];
} }
last = pos;
} }
self.args.length = end; self.args.length = last;
} }
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
if (exp instanceof AST_SymbolRef && exp.undeclared()) { if (exp instanceof AST_SymbolRef && exp.undeclared()) {

View File

@@ -928,11 +928,9 @@ function parse($TEXT, options) {
expression : parenthesised(), expression : parenthesised(),
body : statement() body : statement()
}); });
default:
unexpected();
} }
} }
unexpected();
}); });
function labeled_statement() { function labeled_statement() {

View File

@@ -212,9 +212,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(new TreeWalker(function(node, descend) { self.walk(new TreeWalker(function(node, descend) {
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
var name = node.name; var name = node.name;
var refs = node.thedef.references;
var scope = node.thedef.scope.parent_scope; var scope = node.thedef.scope.parent_scope;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node); var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
node.thedef.references.forEach(function(ref) { refs.forEach(function(ref) {
ref.thedef = def; ref.thedef = def;
ref.reference(options); ref.reference(options);
}); });

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "2.8.10", "version": "2.8.12",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -761,3 +761,33 @@ assign_chain: {
} }
} }
} }
issue_1583: {
options = {
keep_fargs: 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(e) {
t = (function() {
return (function(a) {
return a;
})(function(a) {});
})();
})();
}
}
}

View File

@@ -295,7 +295,15 @@ issue_186_beautify: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) do {\n do {\n alert(x);\n } while (--x);\n} while (x); else bar();' expect_exact: [
'var x = 3;',
'',
'if (foo()) do {',
' do {',
' alert(x);',
' } while (--x);',
'} while (x); else bar();',
]
} }
issue_186_beautify_ie8: { issue_186_beautify_ie8: {
@@ -314,7 +322,17 @@ issue_186_beautify_ie8: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else bar();' expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else bar();',
]
} }
issue_186_bracketize: { issue_186_bracketize: {
@@ -374,7 +392,19 @@ issue_186_beautify_bracketize: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else {\n bar();\n}' expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
} }
issue_186_beautify_bracketize_ie8: { issue_186_beautify_bracketize_ie8: {
@@ -394,5 +424,17 @@ issue_186_beautify_bracketize_ie8: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else {\n bar();\n}' expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
} }

View File

@@ -7,7 +7,13 @@ too_short: {
return { c: 42, d: a(), e: "foo"}; return { c: 42, d: a(), e: "foo"};
} }
} }
expect_exact: 'function f(a){\nreturn{\nc:42,\nd:a(),\ne:"foo"}}' expect_exact: [
'function f(a){',
'return{',
'c:42,',
'd:a(),',
'e:"foo"}}',
]
expect_warnings: [ expect_warnings: [
"WARN: Output exceeds 10 characters" "WARN: Output exceeds 10 characters"
] ]
@@ -22,7 +28,12 @@ just_enough: {
return { c: 42, d: a(), e: "foo"}; return { c: 42, d: a(), e: "foo"};
} }
} }
expect_exact: 'function f(a){\nreturn{c:42,\nd:a(),e:"foo"}\n}' expect_exact: [
'function f(a){',
'return{c:42,',
'd:a(),e:"foo"}',
'}',
]
expect_warnings: [ expect_warnings: [
] ]
} }

View File

@@ -1144,3 +1144,186 @@ double_reference: {
} }
} }
} }
iife_arguments_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(x) {
console.log(x() === arguments[0]);
})(function f() {
return f;
});
}
expect: {
(function(x) {
console.log(x() === arguments[0]);
})(function f() {
return f;
});
}
}
iife_arguments_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
var x = function f() {
return f;
};
console.log(x() === arguments[0]);
})();
}
expect: {
(function() {
console.log(function f() {
return f;
}() === arguments[0]);
})();
}
}
iife_eval_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(x) {
console.log(x() === eval("x"));
})(function f() {
return f;
});
}
expect: {
(function(x) {
console.log(x() === eval("x"));
})(function f() {
return f;
});
}
}
iife_eval_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
var x = function f() {
return f;
};
console.log(x() === eval("x"));
})();
}
expect: {
(function() {
var x = function f() {
return f;
};
console.log(x() === eval("x"));
})();
}
}
iife_func_side_effects: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a, b, c) {
return b();
})(x(), function() {
return y();
}, z());
}
expect: {
(function(a, b, c) {
return function() {
return y();
}();
})(x(), 0, z());
}
}
issue_1595_1: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return f(a + 1);
})(2);
}
expect: {
(function f(a) {
return f(a + 1);
})(2);
}
}
issue_1595_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
(function(a) {
return g(a + 1);
})(2);
}
}
issue_1595_3: {
options = {
evaluate: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
(function(a) {
return g(3);
})();
}
}
issue_1595_4: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function iife(a, b, c) {
console.log(a, b, c);
if (a) iife(a - 1, b, c);
})(3, 4, 5);
}
expect: {
(function iife(a, b, c) {
console.log(a, b, c);
if (a) iife(a - 1, b, c);
})(3, 4, 5);
}
}

View File

@@ -182,3 +182,39 @@ reduce_vars: {
} }
} }
} }
issue_1586_1: {
options = {
screw_ie8: false,
}
mangle = {
screw_ie8: false,
}
input: {
function f() {
try {
} catch (err) {
console.log(err.message);
}
}
}
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
}
issue_1586_2: {
options = {
screw_ie8: true,
}
mangle = {
screw_ie8: true,
}
input: {
function f() {
try {
} catch (err) {
console.log(err.message);
}
}
}
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
}

View File

@@ -0,0 +1 @@
for (var i = 0; i < 1; i++)

View File

@@ -238,4 +238,17 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should fail with a missing loop body", function(done) {
var command = uglifyjscmd + ' test/input/invalid/loop-no-body.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0");
assert.strictEqual(lines[1], "for (var i = 0; i < 1; i++) ");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
}); });

View File

@@ -214,6 +214,23 @@ function parse_test(file) {
})); }));
} }
function read_string(stat) {
if (stat.TYPE === "SimpleStatement") {
var body = stat.body;
out: switch(body.TYPE) {
case "String":
return body.value;
case "Array":
return body.elements.map(function(element) {
if (element.TYPE !== "String")
throw new Error("Should be array of strings");
return element.value;
}).join("\n");
}
}
throw new Error("Should be string or array of strings");
}
function get_one_test(name, block) { function get_one_test(name, block) {
var test = { name: name, options: {} }; var test = { name: name, options: {} };
var tw = new U.TreeWalker(function(node, descend){ var tw = new U.TreeWalker(function(node, descend){
@@ -240,12 +257,7 @@ function parse_test(file) {
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement(); else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
} }
if (node.label.name === "expect_exact") { if (node.label.name === "expect_exact") {
if (!(stat.TYPE === "SimpleStatement" && stat.body.TYPE === "String")) { test[node.label.name] = read_string(stat);
throw new Error(
"The value of the expect_exact clause should be a string, " +
"like `expect_exact: \"some.exact.javascript;\"`");
}
test[node.label.name] = stat.body.start.value
} else { } else {
test[node.label.name] = stat; test[node.label.name] = stat;
} }