Compare commits

...

6 Commits

Author SHA1 Message Date
Alex Lam S.L
6b2f34769a v2.8.15 2017-03-23 13:36:47 +08:00
Alex Lam S.L
48ffbef51d account for cross-scope modifications in collapse_vars (#1634)
mostly done by @kzc

fixes #1631
2017-03-23 07:17:34 +08:00
Alex Lam S.L
c0f3feae9f introduce compressor.info() (#1633)
report the following only when `options.warnings = "verbose"`
- unused elements due to inlining
- collpased variables
2017-03-23 06:49:49 +08:00
Alex Lam S.L
a00040dd93 fix a bug in simple_glob (#1632)
- "?" should not match "/"
- other minor clean-ups
2017-03-23 06:11:16 +08:00
Alex Lam S.L
ee95c1b38b metadata cleanup (#1630)
- mention performance anomaly in Node 7 and drop from CI
- remove unused npm "scripts"
- mark browserify dependency as optional
- stop `test/mozilla-ast.js` from spamming console output in later versions of Node.js
2017-03-23 01:31:46 +08:00
Alex Lam S.L
4bceb85cbf throw parse error on invalid assignments (#1627)
fixes #1626
2017-03-21 14:11:32 +08:00
14 changed files with 204 additions and 79 deletions

View File

@@ -5,7 +5,6 @@ node_js:
- "0.12"
- "4"
- "6"
- "7"
env:
- UGLIFYJS_TEST_ALL=1
matrix:

View File

@@ -10,8 +10,10 @@ There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari).
Note: release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
#### Note:
- release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
Install
-------

View File

@@ -131,6 +131,11 @@ merge(Compressor.prototype, {
}
return node;
},
info: function() {
if (this.options.warnings == "verbose") {
AST_Node.warn.apply(AST_Node, arguments);
}
},
warn: function(text, props) {
if (this.options.warnings) {
// only emit unique warnings
@@ -614,12 +619,24 @@ merge(Compressor.prototype, {
|| node instanceof AST_IterationStatement
|| (parent instanceof AST_If && node !== parent.condition)
|| (parent instanceof AST_Conditional && node !== parent.condition)
|| (node instanceof AST_SymbolRef
&& !are_references_in_scope(node.definition(), self))
|| (parent instanceof AST_Binary
&& (parent.operator == "&&" || parent.operator == "||")
&& node === parent.right)
|| (parent instanceof AST_Switch && node !== parent.expression)) {
return side_effects_encountered = unwind = true, node;
}
function are_references_in_scope(def, scope) {
if (def.orig.length === 1
&& def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope !== scope) return false;
var refs = def.references;
for (var i = 0, len = refs.length; i < len; i++) {
if (refs[i].scope !== scope) return false;
}
return true;
}
},
function postorder(node) {
if (unwind) return node;
@@ -664,7 +681,7 @@ merge(Compressor.prototype, {
// Further optimize statement after substitution.
stat.reset_opt_flags(compressor);
compressor.warn("Collapsing " + (is_constant ? "constant" : "variable") +
compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
" " + var_name + " [{file}:{line},{col}]", node.start);
CHANGED = true;
return value;
@@ -1828,7 +1845,7 @@ merge(Compressor.prototype, {
sym.__unused = true;
if (trim) {
a.pop();
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
@@ -1843,7 +1860,7 @@ merge(Compressor.prototype, {
}
if (drop_funcs && node instanceof AST_Defun && node !== self) {
if (!(node.name.definition().id in in_use_ids)) {
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", {
name : node.name.name,
file : node.name.start.file,
line : node.name.start.line,
@@ -1867,7 +1884,7 @@ merge(Compressor.prototype, {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
return true;
}
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
return false;
});
// place uninitialized names at the start

View File

@@ -1501,9 +1501,7 @@ function parse($TEXT, options) {
};
function is_assignable(expr) {
if (!options.strict) return true;
if (expr instanceof AST_This) return false;
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
};
var maybe_assign = function(no_in) {

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "2.8.14",
"version": "2.8.15",
"engines": {
"node": ">=0.8.0"
},
@@ -30,7 +30,6 @@
],
"dependencies": {
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0"
},
"devDependencies": {
@@ -40,13 +39,15 @@
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
},
"scripts": {
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]

View File

@@ -1415,3 +1415,110 @@ issue_1605_2: {
(new Object).p = 1;
}
}
issue_1631_1: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var pc = 0;
function f(x) {
pc = 200;
return 100;
}
function x() {
var t = f();
pc += t;
return pc;
}
console.log(x());
}
expect: {
function f(x) {
return pc = 200, 100;
}
function x() {
var t = f();
return pc += t;
}
var pc = 0;
console.log(x());
}
expect_stdout: "300"
}
issue_1631_2: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
function g() {
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function f() {
return a = 2, 4;
}
function g() {
var t = f();
return b = a + t;
}
var a = 0, b = 1;
console.log(g());
}
expect_stdout: "6"
}
issue_1631_3: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
function g() {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function g() {
function f() {
return a = 2, 4;
}
var a = 0, b = 1, t = f();
return b = a + t;
}
console.log(g());
}
expect_stdout: "6"
}

View File

@@ -47,22 +47,6 @@ html_comment_in_greater_than_or_equal: {
expect_exact: "function f(a,b){return a-- >=b}";
}
html_comment_in_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>= b; }
}
expect_exact: "function f(a,b){return a-- >>=b}";
}
html_comment_in_zero_fill_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>>= b; }
}
expect_exact: "function f(a,b){return a-- >>>=b}";
}
html_comment_in_string_literal: {
input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; }

View File

@@ -39,7 +39,7 @@ non_hoisted_function_after_return_2a: {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2
collapse_vars: false, passes: 2, warnings: "verbose"
}
input: {
function foo(x) {
@@ -75,7 +75,7 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]"
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
]
}
@@ -114,8 +114,5 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:95,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:97,16]"
]
}

View File

@@ -0,0 +1 @@
console.log(x);

View File

@@ -1,10 +1,11 @@
var Uglify = require('../../');
var assert = require("assert");
var path = require("path");
describe("minify() with input file globs", function() {
it("minify() with one input file glob string.", function() {
var result = Uglify.minify("test/input/issue-1242/foo.*");
assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);');
assert.strictEqual(result.code, 'function foo(o){var n=2*o;print("Foo:",n)}var print=console.log.bind(console);');
});
it("minify() with an array of one input file glob.", function() {
var result = Uglify.minify([
@@ -19,6 +20,39 @@ describe("minify() with input file globs", function() {
], {
compress: { toplevel: true }
});
assert.strictEqual(result.code, 'var print=console.log.bind(console);print("qux",function(n){return 3*n}(3),function(n){return n/2}(12)),function(n){print("Foo:",2*n)}(11);');
assert.strictEqual(result.code, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){var o=2*n;print("Foo:",o)}(11);');
});
it("should throw with non-matching glob string", function() {
var glob = "test/input/issue-1242/blah.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.throws(function() {
Uglify.minify(glob);
}, "should throw file not found");
});
it('"?" in glob string should not match "/"', function() {
var glob = "test/input?issue-1242/foo.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.throws(function() {
Uglify.minify(glob);
}, "should throw file not found");
});
it("should handle special characters in glob string", function() {
var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
assert.strictEqual(result.code, "console.log(x);");
});
it("should handle array of glob strings - matching and otherwise", function() {
var dir = "test/input/issue-1242";
var matches = Uglify.simple_glob([
path.join(dir, "b*.es5"),
path.join(dir, "z*.es5"),
path.join(dir, "*.js"),
]);
assert.strictEqual(matches.length, 4);
assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
assert.strictEqual(matches[3], path.join(dir, "qux.js"));
});
});

View File

@@ -5,7 +5,7 @@ var UglifyJS = require(".."),
escodegen = require("escodegen"),
esfuzz = require("esfuzz"),
estraverse = require("estraverse"),
prefix = Array(20).join("\b") + " ";
prefix = "\r ";
// Normalizes input AST for UglifyJS in order to get correct comparison.
@@ -62,7 +62,7 @@ module.exports = function(options) {
var ast1 = normalizeInput(esfuzz.generate({
maxDepth: options.maxDepth
}));
var ast2 =
UglifyJS
.AST_Node

View File

@@ -114,7 +114,7 @@ function run_compress_tests() {
U.AST_Node.warn_function = function(text) {
warnings_emitted.push("WARN: " + text);
};
options.warnings = true;
if (!options.warnings) options.warnings = true;
}
var cmp = new U.Compressor(options, true);
var output_options = test.beautify || {};

View File

@@ -18,6 +18,6 @@ exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;
exports["SymbolDef"] = SymbolDef;
if (typeof DEBUG !== "undefined" && DEBUG) {
if (global.UGLIFY_DEBUG) {
exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE;
}

View File

@@ -7,7 +7,8 @@
var path = require("path");
var fs = require("fs");
var FILES = exports.FILES = [
var UglifyJS = exports;
var FILES = UglifyJS.FILES = [
"../lib/utils.js",
"../lib/ast.js",
"../lib/parse.js",
@@ -20,17 +21,14 @@ var FILES = exports.FILES = [
"../lib/propmangle.js",
"./exports.js",
].map(function(file){
return fs.realpathSync(path.join(path.dirname(__filename), file));
return require.resolve(file);
});
var UglifyJS = exports;
new Function("MOZ_SourceMap", "exports", "DEBUG", FILES.map(function(file){
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(
require("source-map"),
UglifyJS,
!!global.UGLIFY_DEBUG
UglifyJS
);
UglifyJS.AST_Node.warn_function = function(txt) {
@@ -46,7 +44,7 @@ function read_source_map(code) {
return JSON.parse(new Buffer(match[2], "base64"));
}
exports.minify = function(files, options) {
UglifyJS.minify = function(files, options) {
options = UglifyJS.defaults(options, {
spidermonkey : false,
outSourceMap : null,
@@ -181,7 +179,7 @@ exports.minify = function(files, options) {
};
};
// exports.describe_ast = function() {
// UglifyJS.describe_ast = function() {
// function doitem(ctor) {
// var sub = {};
// ctor.SUBCLASSES.forEach(function(ctor){
@@ -195,7 +193,7 @@ exports.minify = function(files, options) {
// return doitem(UglifyJS.AST_Node).sub;
// }
exports.describe_ast = function() {
UglifyJS.describe_ast = function() {
var out = UglifyJS.OutputStream({ beautify: true });
function doitem(ctor) {
out.print("AST_" + ctor.TYPE);
@@ -249,13 +247,13 @@ function readReservedFile(filename, reserved) {
return reserved;
}
exports.readReservedFile = readReservedFile;
UglifyJS.readReservedFile = readReservedFile;
exports.readDefaultReservedFile = function(reserved) {
return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
UglifyJS.readDefaultReservedFile = function(reserved) {
return readReservedFile(require.resolve("./domprops.json"), reserved);
};
exports.readNameCache = function(filename, key) {
UglifyJS.readNameCache = function(filename, key) {
var cache = null;
if (filename) {
try {
@@ -273,7 +271,7 @@ exports.readNameCache = function(filename, key) {
return cache;
};
exports.writeNameCache = function(filename, key, cache) {
UglifyJS.writeNameCache = function(filename, key, cache) {
if (filename) {
var data;
try {
@@ -294,13 +292,9 @@ exports.writeNameCache = function(filename, key, cache) {
// Example: "foo/bar/*baz??.*.js"
// Argument `glob` may be a string or an array of strings.
// Returns an array of strings. Garbage in, garbage out.
exports.simple_glob = function simple_glob(glob) {
var results = [];
UglifyJS.simple_glob = function simple_glob(glob) {
if (Array.isArray(glob)) {
glob.forEach(function(elem) {
results = results.concat(simple_glob(elem));
});
return results;
return [].concat.apply([], glob.map(simple_glob));
}
if (glob.match(/\*|\?/)) {
var dir = path.dirname(glob);
@@ -308,28 +302,19 @@ exports.simple_glob = function simple_glob(glob) {
var entries = fs.readdirSync(dir);
} catch (ex) {}
if (entries) {
var pattern = "^" + (path.basename(glob)
.replace(/\(/g, "\\(")
.replace(/\)/g, "\\)")
.replace(/\{/g, "\\{")
.replace(/\}/g, "\\}")
.replace(/\[/g, "\\[")
.replace(/\]/g, "\\]")
.replace(/\+/g, "\\+")
.replace(/\^/g, "\\^")
.replace(/\$/g, "\\$")
var pattern = "^" + path.basename(glob)
.replace(/[.+^$[\]\\(){}]/g, "\\$&")
.replace(/\*/g, "[^/\\\\]*")
.replace(/\./g, "\\.")
.replace(/\?/g, ".")) + "$";
.replace(/\?/g, "[^/\\\\]") + "$";
var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod);
for (var i in entries) {
if (rx.test(entries[i]))
results.push(dir + "/" + entries[i]);
}
var results = entries.filter(function(name) {
return rx.test(name);
}).map(function(name) {
return path.join(dir, name);
});
if (results.length) return results;
}
}
if (results.length === 0)
results = [ glob ];
return results;
return [ glob ];
};