Compare commits

...

7 Commits

Author SHA1 Message Date
Alex Lam S.L
08514030f4 v3.4.8 2018-08-23 15:27:34 +08:00
Alex Lam S.L
694ca5d045 fix corner case in unused (#3234)
fixes #3233
2018-08-23 06:03:39 +08:00
Alex Lam S.L
57fb58b263 enhance if_return (#3232) 2018-08-21 18:34:16 +08:00
alexlamsl
18c1c9b38a update dependencies
- commander@2.17.1
2018-08-14 17:06:09 +08:00
Alex Lam S.L
5c1ae3662d v3.4.7 2018-08-09 14:47:24 +00:00
Alex Lam S.L
cfebeb2f63 fix corner case in mangle workaround for Safari (#3230)
fixes #3227
2018-08-09 17:34:28 +08:00
Alex Lam S.L
fc78423f1d clean up webkit quirks (#3229) 2018-08-08 16:15:45 +08:00
12 changed files with 420 additions and 168 deletions

View File

@@ -450,8 +450,7 @@ merge(Compressor.prototype, {
return value instanceof AST_Node && def.fixed.parent_scope === scope; return value instanceof AST_Node && def.fixed.parent_scope === scope;
} }
return all(def.orig, function(sym) { return all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolDefun return !(sym instanceof AST_SymbolDefun || sym instanceof AST_SymbolLambda);
|| sym instanceof AST_SymbolLambda);
}); });
} }
@@ -1699,6 +1698,19 @@ merge(Compressor.prototype, {
continue; continue;
} }
if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
var negated = stat.condition.negate(compressor);
if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
CHANGED = true;
stat = stat.clone();
stat.condition = negated;
statements[j] = stat.body;
stat.body = next;
statements[i] = stat.transform(compressor);
continue;
}
}
var ab = aborts(stat.alternative); var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) { if (can_merge_flow(ab)) {
if (ab.label) { if (ab.label) {
@@ -3316,13 +3328,14 @@ merge(Compressor.prototype, {
} else if (node instanceof AST_Unary && node.write_only) { } else if (node instanceof AST_Unary && node.write_only) {
sym = node.expression; sym = node.expression;
} }
if (/strict/.test(compressor.option("pure_getters"))) { if (!/strict/.test(compressor.option("pure_getters"))) return sym instanceof AST_SymbolRef && sym;
while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) { while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
if (sym instanceof AST_Sub) props.unshift(sym.property); if (sym instanceof AST_Sub) props.unshift(sym.property);
sym = sym.expression; sym = sym.expression;
} }
} return sym instanceof AST_SymbolRef && all(sym.definition().orig, function(sym) {
return sym; return !(sym instanceof AST_SymbolLambda);
}) && sym;
}; };
var in_use = []; var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
@@ -3417,7 +3430,7 @@ merge(Compressor.prototype, {
var parent = tt.parent(); var parent = tt.parent();
if (drop_vars) { if (drop_vars) {
var props = [], sym = assign_as_unused(node, props); var props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef) { if (sym) {
var def = sym.definition(); var def = sym.definition();
var in_use = def.id in in_use_ids; var in_use = def.id in in_use_ids;
var value = null; var value = null;
@@ -3616,8 +3629,7 @@ merge(Compressor.prototype, {
function scan_ref_scoped(node, descend) { function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props); var node_def, props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
&& self.variables.get(sym.name) === (node_def = sym.definition())) {
props.forEach(function(prop) { props.forEach(function(prop) {
prop.walk(tw); prop.walk(tw);
}); });
@@ -5758,9 +5770,10 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Defun) {
fixed._squeezed = true; fixed._squeezed = true;
fixed = make_node(AST_Function, fixed, fixed); fixed = make_node(AST_Function, fixed, fixed);
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
} }
var value; var value;
if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) { if (d.recursive_refs > 0) {
value = fixed.clone(true); value = fixed.clone(true);
var defun_def = value.name.definition(); var defun_def = value.name.definition();
var lambda_def = value.variables.get(value.name.name); var lambda_def = value.variables.get(value.name.name);

View File

@@ -694,23 +694,15 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably // a function expression needs parens around it when it's provably
// the first token to appear in a statement. // the first token to appear in a statement.
PARENS(AST_Function, function(output) { PARENS(AST_Function, function(output) {
if (!output.has_parens() && first_in_statement(output)) { if (!output.has_parens() && first_in_statement(output)) return true;
return true;
}
if (output.option('webkit')) { if (output.option('webkit')) {
var p = output.parent(); var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) { if (p instanceof AST_PropAccess && p.expression === this) return true;
return true;
} }
}
if (output.option('wrap_iife')) { if (output.option('wrap_iife')) {
var p = output.parent(); var p = output.parent();
return p instanceof AST_Call && p.expression === this; if (p instanceof AST_Call && p.expression === this) return true;
} }
return false;
}); });
// same goes for an object literal, because otherwise it would be // same goes for an object literal, because otherwise it would be
@@ -784,17 +776,17 @@ function OutputStream(options) {
}); });
PARENS(AST_Call, function(output) { PARENS(AST_Call, function(output) {
var p = output.parent(), p1; var p = output.parent();
if (p instanceof AST_New && p.expression === this) if (p instanceof AST_New && p.expression === this) return true;
return true;
// workaround for Safari bug.
// https://bugs.webkit.org/show_bug.cgi?id=123506 // https://bugs.webkit.org/show_bug.cgi?id=123506
if (output.option('webkit')) {
var g = output.parent(1);
return this.expression instanceof AST_Function return this.expression instanceof AST_Function
&& p instanceof AST_PropAccess && p instanceof AST_PropAccess
&& p.expression === this && p.expression === this
&& (p1 = output.parent(1)) instanceof AST_Assign && g instanceof AST_Assign
&& p1.left === p; && g.left === p;
}
}); });
PARENS(AST_New, function(output) { PARENS(AST_New, function(output) {

View File

@@ -320,14 +320,6 @@ function next_mangled_name(scope, options, def) {
var in_use = names_in_use(scope, options); var in_use = names_in_use(scope, options);
var holes = scope.cname_holes; var holes = scope.cname_holes;
var names = Object.create(null); var names = Object.create(null);
// #179, #326
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
if (scope instanceof AST_Function && scope.name && def.orig[0] instanceof AST_SymbolFunarg) {
var tricky_def = scope.name.definition();
// the function's mangled_name is null when keep_fnames is true
names[tricky_def.mangled_name || tricky_def.name] = true;
}
var scopes = [ scope ]; var scopes = [ scope ];
def.references.forEach(function(sym) { def.references.forEach(function(sym) {
var scope = sym.scope; var scope = sym.scope;

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.4.6", "version": "3.4.8",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -23,7 +23,7 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.16.0", "commander": "~2.17.1",
"source-map": "~0.6.1" "source-map": "~0.6.1"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1982,3 +1982,26 @@ issue_3192: {
"foo bar", "foo bar",
] ]
} }
issue_3233: {
options = {
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
var a = function b() {
b.c = "PASS";
};
a();
console.log(a.c);
}
expect: {
var a = function b() {
b.c = "PASS";
};
a();
console.log(a.c);
}
expect_stdout: "PASS"
}

View File

@@ -236,32 +236,6 @@ issue_203: {
expect_stdout: "42" expect_stdout: "42"
} }
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: { issue_2084: {
options = { options = {
collapse_vars: true, collapse_vars: true,

View File

@@ -396,3 +396,151 @@ if_if_return_return: {
} }
} }
} }
if_body_return_1: {
options = {
if_return: true,
}
input: {
var c = "PASS";
function f(a, b) {
if (a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect: {
var c = "PASS";
function f(a, b) {
if (a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect_stdout: [
"true",
"true",
"42",
"PASS",
]
}
if_body_return_2: {
options = {
if_return: true,
}
input: {
var c = "PASS";
function f(a, b) {
if (0 + a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect: {
var c = "PASS";
function f(a, b) {
if (0 + a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect_stdout: [
"true",
"true",
"42",
"PASS",
]
}
if_body_return_3: {
options = {
if_return: true,
}
input: {
var c = "PASS";
function f(a, b) {
if (1 == a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect: {
var c = "PASS";
function f(a, b) {
if (1 != a) return true;
if (b) throw new Error(c);
return 42;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect_stdout: [
"true",
"true",
"42",
"PASS",
]
}

109
test/compress/webkit.js Normal file
View File

@@ -0,0 +1,109 @@
lambda_call_dot_assign: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
return {};
}().a = 1);
}
expect_exact: "console.log(function(){return{}}().a=1);"
expect_stdout: "1"
}
lambda_call_dot_assign_webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
return {};
}().a = 1);
}
expect_exact: "console.log((function(){return{}}()).a=1);"
expect_stdout: "1"
}
lambda_dot_assign: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
lambda_dot_assign_webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
lambda_name_mangle: {
mangle = {}
input: {
console.log(typeof function foo(bar) {});
}
expect_exact: "console.log(typeof function o(n){});"
expect_stdout: "function"
}
lambda_name_mangle_ie8: {
mangle = {
ie8: true,
toplevel: true,
}
input: {
console.log(typeof function foo(bar) {});
}
expect_exact: "console.log(typeof function n(o){});"
expect_stdout: "function"
}
function_name_mangle: {
options = {
keep_fnames: true,
reduce_vars: true,
unused: true,
}
mangle = {}
input: {
(function() {
function foo(bar) {}
console.log(typeof foo);
})();
}
expect_exact: "(function(){console.log(typeof function o(n){})})();"
expect_stdout: "function"
}
function_name_mangle_ie8: {
options = {
keep_fnames: true,
reduce_vars: true,
unused: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
(function() {
function foo(bar) {}
console.log(typeof foo);
})();
}
expect_exact: "(function(){console.log(typeof function n(o){})})();"
expect_stdout: "function"
}

View File

@@ -23,10 +23,9 @@ module.exports = function(url, callback) {
var options = parse(url); var options = parse(url);
options.rejectUnauthorized = false; options.rejectUnauthorized = false;
require(options.protocol.slice(0, -1)).get(options, function(res) { require(options.protocol.slice(0, -1)).get(options, function(res) {
if (res.statusCode !== 200) return callback(res); if (res.statusCode !== 200) return callback(res.statusCode);
res.pipe(fs.createWriteStream(local(url)).on("close", function() { res.pipe(fs.createWriteStream(local(url)));
callback(null, read(url)); callback(null, res);
}));
}); });
}).on("open", function() { }).on("open", function() {
callback(null, result); callback(null, result);

View File

@@ -25,7 +25,11 @@ if (typeof phantom == "undefined") {
request.resume(); request.resume();
var url = site + request.url; var url = site + request.url;
fetch(url, function(err, res) { fetch(url, function(err, res) {
if (err) throw err; if (err) {
if (typeof err != "number") throw err;
response.writeHead(err);
response.end();
} else {
response.writeHead(200, { response.writeHead(200, {
"Content-Type": { "Content-Type": {
css: "text/css", css: "text/css",
@@ -50,6 +54,7 @@ if (typeof phantom == "undefined") {
} else { } else {
res.pipe(response); res.pipe(response);
} }
}
}); });
}).listen(); }).listen();
server.on("listening", function() { server.on("listening", function() {

View File

@@ -651,7 +651,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should work with explicit --no-rename", function(done) { it("Should work with explicit --no-rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -mc --no-rename"; var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2 --no-rename";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n"); assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n");
@@ -659,7 +659,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should work with implicit --rename", function(done) { it("Should work with implicit --rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -mc"; var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(n){return n}\n"); assert.strictEqual(stdout, "function f(n){return n}\n");
@@ -667,7 +667,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should work with implicit --no-rename", function(done) { it("Should work with implicit --no-rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -c"; var command = uglifyjscmd + " test/input/rename/input.js -c passes=2";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n"); assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n");

View File

@@ -1,21 +1,6 @@
var semver = require("semver"); var semver = require("semver");
var vm = require("vm"); var vm = require("vm");
function createContext() {
return vm.createContext(Object.defineProperty({}, "console", {
value: {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
}
}));
}
function safe_log(arg, level) { function safe_log(arg, level) {
if (arg) switch (typeof arg) { if (arg) switch (typeof arg) {
case "function": case "function":
@@ -25,20 +10,20 @@ function safe_log(arg, level) {
arg.constructor.toString(); arg.constructor.toString();
if (level--) for (var key in arg) { if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key); var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) { if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
arg[key] = safe_log(arg[key], level);
}
} }
} }
return arg; return arg;
} }
function strip_func_ids(text) { function log(msg) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>"); if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
} }
var context; var func_toString = new vm.Script([
var FUNC_TOSTRING = [
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {", "[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
" f.toString = Function.prototype.toString;", " f.toString = Function.prototype.toString;",
"});", "});",
@@ -59,7 +44,15 @@ var FUNC_TOSTRING = [
' return "function(){}";', ' return "function(){}";',
" };", " };",
"}();", "}();",
]).join("\n"); ]).join("\n"));
function createContext() {
var ctx = vm.createContext(Object.defineProperty({}, "console", { value: { log: log } }));
func_toString.runInContext(ctx);
return ctx;
}
var context;
exports.run_code = function(code, reuse) { exports.run_code = function(code, reuse) {
var stdout = ""; var stdout = "";
var original_write = process.stdout.write; var original_write = process.stdout.write;
@@ -69,7 +62,6 @@ exports.run_code = function(code, reuse) {
try { try {
if (!reuse || !context) context = createContext(); if (!reuse || !context) context = createContext();
vm.runInContext([ vm.runInContext([
FUNC_TOSTRING,
"!function() {", "!function() {",
code, code,
"}();", "}();",
@@ -86,6 +78,11 @@ exports.run_code = function(code, reuse) {
} }
} }
}; };
function strip_func_ids(text) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
}
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false; if (typeof expected != typeof actual) return false;
if (typeof expected != "string") { if (typeof expected != "string") {