Compare commits

..

10 Commits

Author SHA1 Message Date
Alex Lam S.L
5dfda6e212 v2.8.23 2017-05-07 04:31:54 +08:00
Alex Lam S.L
d08c772eb3 Merge pull request #1871 from alexlamsl/v2.8.23 2017-05-07 04:06:51 +08:00
Alex Lam S.L
90ed54401b fix test for #1865 (#1873) 2017-05-07 03:04:17 +08:00
Alex Lam S.L
d8106b6c63 fix label-related bugs (#1835)
- deep cloning of `AST_LabeledStatement`
- `L:do{...}while(false)`
- empty statement with label within block

extend `test/ufuzz.js`
- generate labels for blocks & loops
- generate for-in statements
- skip suspicious option search if `minify()` errs

fixes #1833
2017-05-07 00:16:30 +08:00
alexlamsl
dda4eb96e1 backport test scripts 2017-05-06 23:48:28 +08:00
Alex Lam S.L
7305ba0296 fix unsafe on evaluate of reduce_vars (#1870)
Determine if variables with non-constant values can escape and be modified.

fixes #1865
2017-05-06 23:40:19 +08:00
Alex Lam S.L
2c21dc5e8e fix unused on for-in statements (#1843)
Only need to avoid `var` within the initialisation block.

fixes #1841
2017-05-06 23:34:21 +08:00
Alex Lam S.L
d0faa471db fix unused on labeled for-loop (#1831)
fixes #1830
2017-05-06 23:31:22 +08:00
Alex Lam S.L
6ad823d1e8 fix reduce_vars within try-block (#1818)
Possible partial execution due to exceptions.
2017-05-06 23:28:07 +08:00
Alex Lam S.L
43ad4e9775 fix variable substitution (#1816)
- let `collapse_vars` take care of value containing any symbols
- improve overhead accounting
2017-05-06 23:26:54 +08:00
101 changed files with 9799 additions and 11049 deletions

View File

@@ -1,20 +1,9 @@
**Bug report or feature request?** - Bug report or feature request? <!-- Note: sub-optimal but correct code is not a bug -->
- `uglify-js` version (`uglifyjs -V`)
<!-- Note: sub-optimal but correct code is not a bug --> - JavaScript input - ideally as small as possible.
- The `uglifyjs` CLI command executed or `minify()` options used.
**ES5 or ES6+ input?** - An example of JavaScript output produced and/or the error or warning.
<!-- Note: for ES6 see: https://github.com/mishoo/UglifyJS2/tree/harmony#harmony -->
**Uglify version (`uglifyjs -V`)**
**JavaScript input** <!-- ideally as small as possible -->
**The `uglifyjs` CLI command executed or `minify()` options used.**
**JavaScript output or error produced.**
<!-- <!--
Note: `uglify-js` only supports ES5. Note: the release version of uglify-js only supports ES5. Those wishing
Those wishing to minify ES6 should use `uglify-es`. to minify ES6 should use the experimental harmony branch.
--> -->

View File

@@ -1,4 +1,5 @@
language: node_js language: node_js
before_install: "npm install -g npm"
node_js: node_js:
- "0.10" - "0.10"
- "0.12" - "0.12"

1279
README.md

File diff suppressed because it is too large Load Diff

77
bin/extract-props.js Executable file
View File

@@ -0,0 +1,77 @@
#! /usr/bin/env node
"use strict";
var U2 = require("../tools/node");
var fs = require("fs");
var yargs = require("yargs");
var ARGS = yargs
.describe("o", "Output file")
.argv;
var files = ARGS._.slice();
var output = {
vars: {},
props: {}
};
if (ARGS.o) try {
output = JSON.parse(fs.readFileSync(ARGS.o, "utf8"));
} catch(ex) {}
files.forEach(getProps);
if (ARGS.o) {
fs.writeFileSync(ARGS.o, JSON.stringify(output, null, 2), "utf8");
} else {
console.log("%s", JSON.stringify(output, null, 2));
}
function getProps(filename) {
var code = fs.readFileSync(filename, "utf8");
var ast = U2.parse(code);
ast.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof U2.AST_ObjectProperty) {
add(node.key.name);
}
else if (node instanceof U2.AST_Dot) {
add(node.property);
}
else if (node instanceof U2.AST_Sub) {
addStrings(node.property);
}
}));
function addStrings(node) {
var out = {};
try {
(function walk(node){
node.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof U2.AST_String) {
add(node.value);
return true;
}
if (node instanceof U2.AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function add(name) {
output.props[name] = true;
}
}

View File

@@ -3,402 +3,633 @@
"use strict"; "use strict";
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var fs = require("fs");
var info = require("../package.json");
var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node"); var UglifyJS = require("../tools/node");
var sys = require("util");
var yargs = require("yargs");
var fs = require("fs");
var path = require("path");
var acorn;
var screw_ie8 = true;
var ARGS = yargs
.usage("$0 input1.js [input2.js ...] [options]\n\
Use a single dash to read input from the standard input.\
\n\n\
NOTE: by default there is no mangling/compression.\n\
Without [options] it will simply parse input files and dump the AST\n\
with whitespace and comments discarded. To achieve compression and\n\
mangling you need to use `-c` and `-m`.\
")
.describe("source-map", "Specify an output file where to generate source map.")
.describe("source-map-root", "The path to the original source to be included in the source map.")
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
.describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default")
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("screw-ie8", "Do not support Internet Explorer 6/7/8. This flag is enabled by default.")
.describe("support-ie8", "Support non-standard Internet Explorer 6/7/8 javascript.")
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
.describe("p", "Skip prefix for original filenames that appear in source maps. \
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
the source map and the output file.")
.describe("o", "Output file (default STDOUT).")
.describe("b", "Beautify output/specify output options.")
.describe("m", "Mangle names/pass mangler options.")
.describe("r", "Reserved names to exclude from mangling.")
.describe("c", "Enable compressor/pass compressor options. \
Pass options like -c hoist_vars=false,if_return=false. \
Use -c with no argument to use the default compression options.")
.describe("d", "Global definitions")
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
var skip_keys = [ "cname", "enclosed", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ]; .describe("comments", "Preserve copyright comments in the output. \
var files = {}; By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
var options = { You can optionally pass one of the following arguments to this flag:\n\
compress: false, - \"all\" to keep all comments\n\
mangle: false - a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\
}; \
program.version(info.name + " " + info.version); Note that currently not *all* comments can be kept when compression is on, \
program.parseArgv = program.parse; because of dead code removal or cascading statements into sequences.")
program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast; .describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() { comment, for example for licensing information. This will not be \
var text = []; parsed, but the source map will adjust for its presence.")
var options = UglifyJS.default_options();
for (var option in options) { .describe("stats", "Display operations run time on STDERR.")
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:"); .describe("acorn", "Use Acorn for parsing.")
var defs = options[option]; .describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
var padding = ""; .describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
Object.keys(defs).map(function(name) { .describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
if (padding.length < name.length) padding = Array(name.length + 1).join(" "); You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
return [ name, JSON.stringify(defs[name]) ]; .describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
}).forEach(function(tokens) { .describe("lint", "Display some scope warnings")
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]); .describe("v", "Verbose")
}); .describe("V", "Print version number and exit.")
text.push(""); .describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
} .describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
return text.join("\n"); .describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
}; .describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true)); .describe("reserved-file", "File containing reserved names")
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); .describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); .describe("mangle-props", "Mangle property names (0 - disabled, 1 - mangle all properties, 2 - mangle unquoted properies)")
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true)); .describe("mangle-regex", "Only mangle property names matching the regex")
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true)); .describe("name-cache", "File to hold mangled names mappings")
program.option("-o, --output <file>", "Output file (default STDOUT)."); .describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
program.option("--comments [filter]", "Preserve copyright comments in the output."); .describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
program.option("--config-file <file>", "Read minify() options from JSON file."); .describe("wrap-iife", "Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option")
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
program.option("--ie8", "Support non-standard Internet Explorer 8."); .alias("p", "prefix")
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); .alias("o", "output")
program.option("--name-cache <file>", "File to hold mangled name mappings."); .alias("v", "verbose")
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"); .alias("b", "beautify")
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map()); .alias("m", "mangle")
program.option("--timings", "Display operations run time on STDERR.") .alias("c", "compress")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); .alias("d", "define")
program.option("--verbose", "Print diagnostic messages."); .alias("r", "reserved")
program.option("--warn", "Print warning messages."); .alias("V", "version")
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally."); .alias("e", "enclose")
program.arguments("[files...]").parseArgv(process.argv); .alias("q", "quotes")
if (program.configFile) {
options = JSON.parse(read_file(program.configFile)); .string("source-map")
.string("source-map-root")
.string("source-map-url")
.string("b")
.string("beautify")
.string("m")
.string("mangle")
.string("mangle-props-debug")
.string("c")
.string("compress")
.string("d")
.string("define")
.string("e")
.string("enclose")
.string("comments")
.string("wrap")
.string("p")
.string("prefix")
.string("name-cache")
.array("reserved-file")
.array("pure-funcs")
.boolean("expr")
.boolean("source-map-inline")
.boolean("source-map-include-sources")
.boolean("screw-ie8")
.boolean("support-ie8")
.boolean("export-all")
.boolean("self")
.boolean("v")
.boolean("verbose")
.boolean("stats")
.boolean("acorn")
.boolean("spidermonkey")
.boolean("dump-spidermonkey-ast")
.boolean("lint")
.boolean("V")
.boolean("version")
.boolean("noerr")
.boolean("bare-returns")
.boolean("keep-fnames")
.boolean("reserve-domprops")
.boolean("wrap-iife")
.wrap(80)
.argv
;
normalize(ARGS);
if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) {
print_error("WARN: " + msg);
};
} }
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
fatal("ERROR: cannot write source map to STDOUT"); if (ARGS.version || ARGS.V) {
var json = require("../package.json");
print(json.name + ' ' + json.version);
process.exit(0);
} }
[
"compress", if (ARGS.ast_help) {
"ie8", var desc = UglifyJS.describe_ast();
"mangle", print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
"sourceMap", process.exit(0);
"toplevel", }
"wrap"
].forEach(function(name) { if (ARGS.h || ARGS.help) {
if (name in program) { print(yargs.help());
options[name] = program[name]; process.exit(0);
} }
if (ARGS.acorn) {
acorn = require("acorn");
}
var COMPRESS = getOptions("c", true);
var MANGLE = getOptions("m", true);
var BEAUTIFY = getOptions("b", true);
var RESERVED = null;
if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
}); });
if (program.beautify) {
options.output = typeof program.beautify == "object" ? program.beautify : {}; if (ARGS.reserve_domprops) {
if (!("beautify" in options.output)) { RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
options.output.beautify = true;
}
} }
if (program.comments) {
if (typeof options.output != "object") options.output = {}; if (ARGS.d) {
options.output.comments = typeof program.comments == "string" ? program.comments : "some"; if (COMPRESS) COMPRESS.global_defs = getOptions("d");
} }
if (program.define) {
if (typeof options.compress != "object") options.compress = {}; if (ARGS.pure_funcs) {
if (typeof options.compress.global_defs != "object") options.compress.global_defs = {}; if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
for (var expr in program.define) {
options.compress.global_defs[expr] = program.define[expr];
}
} }
if (program.keepFnames) {
options.keep_fnames = true; if (ARGS.r) {
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
} }
if (program.mangleProps) {
if (program.mangleProps.domprops) { if (RESERVED && MANGLE) {
delete program.mangleProps.domprops; if (!MANGLE.except) MANGLE.except = RESERVED.vars;
else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
}
function readNameCache(key) {
return UglifyJS.readNameCache(ARGS.name_cache, key);
}
function writeNameCache(key, cache) {
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
}
function extractRegex(str) {
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
var regex_pos = str.lastIndexOf("/");
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
} else { } else {
if (typeof program.mangleProps != "object") program.mangleProps = {}; throw new Error("Invalid regular expression: " + str);
if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS._push_uniq(program.mangleProps.reserved, name);
});
}
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
}
var cache;
if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
} }
} }
if (program.output == "ast") {
options.output = { if (ARGS.quotes === true) {
ast: true, ARGS.quotes = 3;
code: false
};
} }
if (program.parse) {
if (!program.parse.acorn && !program.parse.spidermonkey) { if (ARGS.mangle_props === true) {
options.parse = program.parse; ARGS.mangle_props = 1;
} else if (program.sourceMap && program.sourceMap.content == "inline") { } else if (ARGS.mangle_props === "unquoted") {
fatal("ERROR: inline source map only works with built-in parser"); ARGS.mangle_props = 2;
}
} }
var convert_path = function(name) {
return name; var OUTPUT_OPTIONS = {
beautify : BEAUTIFY ? true : false,
max_line_len : 32000,
preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0,
}; };
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
convert_path = function() { if (ARGS.mangle_props == 2) {
var base = program.sourceMap.base; OUTPUT_OPTIONS.keep_quoted_props = true;
delete options.sourceMap.base; if (COMPRESS && !("properties" in COMPRESS))
return function(name) { COMPRESS.properties = false;
return path.relative(base, name);
};
}();
} }
if (program.verbose) {
options.warnings = "verbose"; if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) {
} else if (program.warn) { screw_ie8 = false;
options.warnings = true;
} }
if (program.self) {
if (program.args.length) { if (COMPRESS) COMPRESS.screw_ie8 = screw_ie8;
console.error("WARN: Ignoring input files since --self was passed"); if (MANGLE) MANGLE.screw_ie8 = screw_ie8;
} OUTPUT_OPTIONS.screw_ie8 = screw_ie8;
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) { if (ARGS.keep_fnames) {
files[convert_path(name)] = read_file(name); if (COMPRESS) COMPRESS.keep_fnames = true;
}); if (MANGLE) MANGLE.keep_fnames = true;
run(); }
} else if (program.args.length) {
simple_glob(program.args).forEach(function(name) { if (ARGS.wrap_iife) {
files[convert_path(name)] = read_file(name); if (COMPRESS) COMPRESS.negate_iife = false;
}); OUTPUT_OPTIONS.wrap_iife = true;
run(); }
if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments === "") {
OUTPUT_OPTIONS.comments = "some";
} else { } else {
var chunks = []; OUTPUT_OPTIONS.comments = ARGS.comments;
process.stdin.setEncoding("utf8"); }
process.stdin.on("data", function(chunk) {
chunks.push(chunk); var files = ARGS._.slice();
}).on("end", function() {
files = [ chunks.join("") ]; if (process.platform === "win32")
run(); files = UglifyJS.simple_glob(files);
if (ARGS.self) {
if (files.length > 0) {
print_error("WARN: Ignoring input files since --self was passed");
}
files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
}
var ORIG_MAP = ARGS.in_source_map;
if (ORIG_MAP && ORIG_MAP != "inline") {
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (files.length == 0) {
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
files = [ ORIG_MAP.file ];
}
}
if (files.length == 0) {
files = [ "-" ];
}
if (ORIG_MAP == "inline") {
if (files.length > 1) {
print_error("ERROR: Inline source map only works with singular input");
process.exit(1);
}
if (ARGS.acorn || ARGS.spidermonkey) {
print_error("ERROR: Inline source map only works with built-in parser");
process.exit(1);
}
}
if (files.indexOf("-") >= 0 && ARGS.source_map) {
print_error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1);
}
if (files.filter(function(el){ return el == "-" }).length > 1) {
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
process.exit(1);
}
var STATS = {};
var TOPLEVEL = null;
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
var SOURCES_CONTENT = {};
var index = 0;
!function cb() {
if (index == files.length) return done();
var file = files[index++];
read_whole_file(file, function (err, code) {
if (err) {
print_error("ERROR: can't read file: " + file);
process.exit(1);
}
if (ORIG_MAP == "inline") {
ORIG_MAP = read_source_map(code);
}
if (ARGS.p != null) {
if (P_RELATIVE) {
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
} else {
var p = parseInt(ARGS.p, 10);
if (!isNaN(p)) {
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
}
}
}
SOURCES_CONTENT[file] = code;
time_it("parse", function(){
if (ARGS.spidermonkey) {
var program = JSON.parse(code);
if (!TOPLEVEL) TOPLEVEL = program;
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
}
else if (ARGS.acorn) {
TOPLEVEL = acorn.parse(code, {
locations : true,
sourceFile : file,
program : TOPLEVEL
}); });
process.stdin.resume(); }
} else {
function convert_ast(fn) {
return UglifyJS.AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null));
}
function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
};
if (program.timings) options.timings = true;
try { try {
if (program.parse) { TOPLEVEL = UglifyJS.parse(code, {
if (program.parse.acorn) { filename : file,
files = convert_ast(function(toplevel, name) { toplevel : TOPLEVEL,
return require("acorn").parse(files[name], { expression : ARGS.expr,
locations: true, bare_returns : ARGS.bare_returns,
program: toplevel,
sourceFile: name
}); });
}); } catch(ex) {
} else if (program.parse.spidermonkey) { if (ex instanceof UglifyJS.JS_Parse_Error) {
files = convert_ast(function(toplevel, name) { print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
var obj = JSON.parse(files[name]);
if (!toplevel) return obj;
toplevel.body = toplevel.body.concat(obj.body);
return toplevel;
});
}
}
} catch (ex) {
fatal(ex);
}
var result = UglifyJS.minify(files, options);
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col; var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/); var line = code.split(/\r?\n/)[ex.line - (col ? 1 : 2)];
var line = lines[ex.line - 1];
if (!line && !col) {
line = lines[ex.line - 2];
col = line.length;
}
if (line) { if (line) {
if (col > 40) { if (col > 40) {
line = line.slice(col - 40); line = line.slice(col - 40);
col = 40; col = 40;
} }
console.error(line.slice(0, 80)); if (col) {
console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); print_error(line.slice(0, 80));
} print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
if (ex.defs) {
console.error("Supported options:");
console.error(ex.defs);
}
fatal(ex);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
if (value instanceof UglifyJS.AST_Node) {
var result = {
_class: "AST_" + value.TYPE
};
value.CTOR.PROPS.forEach(function(prop) {
result[prop] = value[prop];
});
return result;
}
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
}).ast.to_mozilla_ast(), null, 2));
} else if (program.output) {
fs.writeFileSync(program.output, result.code);
if (result.map) {
fs.writeFileSync(program.output + ".map", result.map);
}
} else { } else {
console.log(result.code); print_error(line.slice(-40));
print_error(line.slice(-40).replace(/\S/g, " ") + "^");
} }
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
} }
if (result.timings) for (var phase in result.timings) { print_error(ex.stack);
console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
}
function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message);
process.exit(1); process.exit(1);
}
// A file glob function that only supports "*" and "?" wildcards in the basename.
// 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.
function simple_glob(glob) {
if (Array.isArray(glob)) {
return [].concat.apply([], glob.map(simple_glob));
} }
if (glob.match(/\*|\?/)) { throw ex;
var dir = path.dirname(glob);
try {
var entries = fs.readdirSync(dir);
} catch (ex) {}
if (entries) {
var pattern = "^" + path.basename(glob)
.replace(/[.+^$[\]\\(){}]/g, "\\$&")
.replace(/\*/g, "[^/\\\\]*")
.replace(/\?/g, "[^/\\\\]") + "$";
var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod);
var results = entries.filter(function(name) {
return rx.test(name);
}).map(function(name) {
return path.join(dir, name);
});
if (results.length) return results;
} }
}
return [ glob ];
}
function read_file(path, default_value) {
try {
return fs.readFileSync(path, "utf8");
} catch (ex) {
if (ex.code == "ENOENT" && default_value != null) return default_value;
fatal(ex);
}
}
function parse_js(flag, constants) {
return function(value, options) {
options = options || {};
try {
UglifyJS.minify(value, {
parse: {
expression: true
},
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
}).ast.walk(new UglifyJS.TreeWalker(function(node) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (!constants) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
} else {
options[name] = to_string(value);
}
return true;
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) {
var name = node.print_to_string();
options[name] = true;
return true;
}
if (!(node instanceof UglifyJS.AST_Sequence)) throw node;
function to_string(value) {
return value instanceof UglifyJS.AST_Constant ? value.getValue() : value.print_to_string({
quote_keys: true
});
}
}));
} catch(ex) {
options[value] = null;
}
return options;
}
}
function parse_source_map() {
var parse = parse_js("sourceMap", true);
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content);
settings.content = read_file(settings.content, settings.content);
}
return settings;
}
}
function to_cache(key) {
if (cache[key]) {
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
} else {
cache[key] = {
cname: -1,
props: new UglifyJS.Dictionary()
}; };
});
cb();
});
}();
function done() {
var OUTPUT_FILE = ARGS.o;
var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
root: ARGS.source_map_root || ORIG_MAP && ORIG_MAP.sourceRoot,
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.message);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
});
if (ARGS.wrap != null) {
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
}
if (ARGS.enclose != null) {
var arg_parameter_list = ARGS.enclose;
if (arg_parameter_list === true) {
arg_parameter_list = [];
}
else if (!(arg_parameter_list instanceof Array)) {
arg_parameter_list = [arg_parameter_list];
}
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
}
if (ARGS.mangle_props || ARGS.name_cache) (function(){
var reserved = RESERVED ? RESERVED.props : null;
var cache = readNameCache("props");
var regex;
try {
regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
} catch (e) {
print_error("ERROR: Invalid --mangle-regex: " + e.message);
process.exit(1);
}
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
reserved : reserved,
cache : cache,
only_cache : !ARGS.mangle_props,
regex : regex,
ignore_quoted : ARGS.mangle_props == 2,
debug : typeof ARGS.mangle_props_debug === "undefined" ? false : ARGS.mangle_props_debug
});
writeNameCache("props", cache);
})();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars");
if (MANGLE) MANGLE.cache = TL_CACHE;
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (ARGS.lint) {
TOPLEVEL.scope_warnings();
}
});
}
if (COMPRESS) {
time_it("squeeze", function(){
TOPLEVEL = compressor.compress(TOPLEVEL);
});
}
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (MANGLE && !TL_CACHE) {
TOPLEVEL.compute_char_frequency(MANGLE);
}
});
}
if (MANGLE) time_it("mangle", function(){
TOPLEVEL.mangle_names(MANGLE);
});
writeNameCache("vars", TL_CACHE);
if (ARGS.source_map_include_sources) {
for (var file in SOURCES_CONTENT) {
if (SOURCES_CONTENT.hasOwnProperty(file)) {
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
}
}
}
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
time_it("generate", function(){
TOPLEVEL.print(output);
});
output = output.get();
if (SOURCE_MAP) {
if (ARGS.source_map_inline) {
var base64_string = new Buffer(SOURCE_MAP.toString()).toString('base64');
output += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64_string;
} else {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
var source_map_url = ARGS.source_map_url || (
P_RELATIVE
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
: ARGS.source_map
);
output += "\n//# sourceMappingURL=" + source_map_url;
}
}
if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else {
print(output);
}
}
if (ARGS.stats) {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
count: files.length
}));
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
print_error(UglifyJS.string_template("- {name}: {time}s", {
name: i,
time: (STATS[i] / 1000).toFixed(3)
}));
}
} }
return cache[key];
} }
function skip_key(key) { /* -----[ functions ]----- */
return skip_keys.indexOf(key) >= 0;
function normalize(o) {
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
o[i.replace(/-/g, "_")] = o[i];
delete o[i];
}
}
function getOptions(flag, constants) {
var x = ARGS[flag];
if (x == null || x === false) return null;
var ret = {};
if (x !== "") {
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
var ast;
try {
ast = UglifyJS.parse(x, { cli: true, expression: true });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}
}
ast.walk(new UglifyJS.TreeWalker(function(node){
if (node instanceof UglifyJS.AST_Seq) return; // descend
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string().replace(/-/g, "_");
var value = node.right;
if (constants)
value = new Function("return (" + value.print_to_string() + ")")();
ret[name] = value;
return true; // no descend
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
var name = node.print_to_string().replace(/-/g, "_");
ret[name] = true;
return true; // no descend
}
print_error(node.TYPE)
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}));
}
return ret;
}
function read_whole_file(filename, cb) {
if (filename == "-") {
var chunks = [];
process.stdin.setEncoding('utf-8');
process.stdin.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function () {
cb(null, chunks.join(""));
});
process.openStdin();
} else {
fs.readFile(filename, "utf-8", cb);
}
}
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
print_error("WARN: inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
function time_it(name, cont) {
var t1 = new Date().getTime();
var ret = cont();
if (ARGS.stats) {
var spent = new Date().getTime() - t1;
if (STATS[name]) STATS[name] += spent;
else STATS[name] = spent;
}
return ret;
}
function print_error(msg) {
console.error("%s", msg);
}
function print(txt) {
console.log("%s", txt);
} }

View File

@@ -182,13 +182,21 @@ var AST_BlockStatement = DEFNODE("BlockStatement", null, {
}, AST_Block); }, AST_Block);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)" $documentation: "The empty statement (empty block or simply a semicolon)",
_walk: function(visitor) {
return visitor._visit(this);
}
}, AST_Statement); }, AST_Statement);
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
$propdoc: { $propdoc: {
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.body._walk(visitor);
});
} }
}, AST_Statement); }, AST_Statement);
@@ -318,14 +326,63 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$propdoc: { $propdoc: {
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
}, },
wrap_commonjs: function(name) { wrap_enclose: function(arg_parameter_pairs) {
var body = this.body; var self = this;
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; var args = [];
var parameters = [];
arg_parameter_pairs.forEach(function(pair) {
var splitAt = pair.lastIndexOf(":");
args.push(pair.substr(0, splitAt));
parameters.push(pair.substr(splitAt + 1));
});
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
wrapped_tl = parse(wrapped_tl); wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_Directive && node.value == "$ORIG") { if (node instanceof AST_Directive && node.value == "$ORIG") {
return MAP.splice(self.body);
}
}));
return wrapped_tl;
},
wrap_commonjs: function(name, export_all) {
var self = this;
var to_export = [];
if (export_all) {
self.figure_out_scope();
self.walk(new TreeWalker(function(node){
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
if (!find_if(function(n){ return n.name == node.name }, to_export))
to_export.push(node);
}
}));
}
var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))";
wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_Directive) {
switch (node.value) {
case "$ORIG":
return MAP.splice(self.body);
case "$EXPORTS":
var body = [];
to_export.forEach(function(sym){
body.push(new AST_SimpleStatement({
body: new AST_Assign({
left: new AST_Sub({
expression: new AST_SymbolRef({ name: "exports" }),
property: new AST_String({ value: sym.name }),
}),
operator: "=",
right: new AST_SymbolRef(sym),
}),
}));
});
return MAP.splice(body); return MAP.splice(body);
} }
}
})); }));
return wrapped_tl; return wrapped_tl;
} }
@@ -495,10 +552,10 @@ var AST_Finally = DEFNODE("Finally", null, {
$documentation: "A `finally` node; only makes sense as part of a `try` statement" $documentation: "A `finally` node; only makes sense as part of a `try` statement"
}, AST_Block); }, AST_Block);
/* -----[ VAR ]----- */ /* -----[ VAR/CONST ]----- */
var AST_Definitions = DEFNODE("Definitions", "definitions", { var AST_Definitions = DEFNODE("Definitions", "definitions", {
$documentation: "Base class for `var` nodes (variable declarations/initializations)", $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
$propdoc: { $propdoc: {
definitions: "[AST_VarDef*] array of variable definitions" definitions: "[AST_VarDef*] array of variable definitions"
}, },
@@ -516,10 +573,14 @@ var AST_Var = DEFNODE("Var", null, {
$documentation: "A `var` statement" $documentation: "A `var` statement"
}, AST_Definitions); }, AST_Definitions);
var AST_Const = DEFNODE("Const", null, {
$documentation: "A `const` statement"
}, AST_Definitions);
var AST_VarDef = DEFNODE("VarDef", "name value", { var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node", $documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: { $propdoc: {
name: "[AST_SymbolVar] name of the variable", name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer" value: "[AST_Node?] initializer, or null of there's no initializer"
}, },
_walk: function(visitor) { _walk: function(visitor) {
@@ -540,11 +601,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.expression._walk(visitor);
var args = this.args; var args = this.args;
for (var i = 0, len = args.length; i < len; i++) { for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor); args[i]._walk(visitor);
} }
this.expression._walk(visitor);
}); });
} }
}); });
@@ -553,16 +614,68 @@ var AST_New = DEFNODE("New", null, {
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
}, AST_Call); }, AST_Call);
var AST_Sequence = DEFNODE("Sequence", "expressions", { var AST_Seq = DEFNODE("Seq", "car cdr", {
$documentation: "A sequence expression (comma-separated expressions)", $documentation: "A sequence expression (two comma-separated expressions)",
$propdoc: { $propdoc: {
expressions: "[AST_Node*] array of expressions (at least two)" car: "[AST_Node] first element in sequence",
cdr: "[AST_Node] second element in sequence"
},
$cons: function(x, y) {
var seq = new AST_Seq(x);
seq.car = x;
seq.cdr = y;
return seq;
},
$from_array: function(array) {
if (array.length == 0) return null;
if (array.length == 1) return array[0].clone();
var list = null;
for (var i = array.length; --i >= 0;) {
list = AST_Seq.cons(array[i], list);
}
var p = list;
while (p) {
if (p.cdr && !p.cdr.cdr) {
p.cdr = p.cdr.car;
break;
}
p = p.cdr;
}
return list;
},
to_array: function() {
var p = this, a = [];
while (p) {
a.push(p.car);
if (p.cdr && !(p.cdr instanceof AST_Seq)) {
a.push(p.cdr);
break;
}
p = p.cdr;
}
return a;
},
add: function(node) {
var p = this;
while (p) {
if (!(p.cdr instanceof AST_Seq)) {
var cell = AST_Seq.cons(p.cdr, node);
return p.cdr = cell;
}
p = p.cdr;
}
},
len: function() {
if (this.cdr instanceof AST_Seq) {
return this.cdr.len() + 1;
} else {
return 2;
}
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.expressions.forEach(function(node) { this.car._walk(visitor);
node._walk(visitor); if (this.cdr) this.cdr._walk(visitor);
});
}); });
} }
}); });
@@ -615,7 +728,7 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
$documentation: "Unary postfix expression, i.e. `i++`" $documentation: "Unary postfix expression, i.e. `i++`"
}, AST_Unary); }, AST_Unary);
var AST_Binary = DEFNODE("Binary", "operator left right", { var AST_Binary = DEFNODE("Binary", "left operator right", {
$documentation: "Binary expression, i.e. `a + b`", $documentation: "Binary expression, i.e. `a + b`",
$propdoc: { $propdoc: {
left: "[AST_Node] left-hand side expression", left: "[AST_Node] left-hand side expression",
@@ -685,8 +798,8 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties", $documentation: "Base class for literal object properties",
$propdoc: { $propdoc: {
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.", key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.",
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor." value: "[AST_Node] property value. For setters and getters this is an AST_Function."
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
@@ -724,13 +837,17 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)", $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolVar = DEFNODE("SymbolVar", null, { var AST_SymbolVar = DEFNODE("SymbolVar", null, {
$documentation: "Symbol defining a variable", $documentation: "Symbol defining a variable",
}, AST_SymbolDeclaration); }, AST_SymbolDeclaration);
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
$documentation: "A constant declaration"
}, AST_SymbolDeclaration);
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
$documentation: "Symbol naming a function argument", $documentation: "Symbol naming a function argument",
}, AST_SymbolVar); }, AST_SymbolVar);
@@ -865,7 +982,7 @@ TreeWalker.prototype = {
parent: function(n) { parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)]; return this.stack[this.stack.length - 2 - (n || 0)];
}, },
push: function(node) { push: function (node) {
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) { } else if (node instanceof AST_Directive && !this.directives[node.value]) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,177 +0,0 @@
"use strict";
var to_ascii = typeof atob == "undefined" ? function(b64) {
return new Buffer(b64, "base64").toString();
} : atob;
var to_base64 = typeof btoa == "undefined" ? function(str) {
return new Buffer(str).toString("base64");
} : btoa;
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
AST_Node.warn("inline source map not found");
return null;
}
return to_ascii(match[2]);
}
function set_shorthand(name, options, keys) {
if (options[name]) {
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
options = defaults(options, {
compress: {},
ie8: false,
keep_fnames: false,
mangle: {},
output: {},
parse: {},
sourceMap: false,
timings: false,
toplevel: false,
warnings: false,
wrap: false,
}, true);
var timings = options.timings && {
start: Date.now()
};
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
eval: false,
ie8: false,
keep_fnames: false,
properties: false,
reserved: [],
toplevel: false,
}, true);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
content: null,
filename: null,
includeSources: false,
root: null,
url: null,
}, true);
}
var warnings = [];
if (options.warnings && !AST_Node.warn_function) {
AST_Node.warn_function = function(warning) {
warnings.push(warning);
};
}
if (timings) timings.parse = Date.now();
var toplevel;
if (files instanceof AST_Toplevel) {
toplevel = files;
} else {
if (typeof files == "string") {
files = [ files ];
}
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
if (Object.keys(files).length > 1)
throw new Error("inline source map only works with singular input");
options.sourceMap.content = read_source_map(files[name]);
}
}
toplevel = options.parse.toplevel;
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
base54.reset();
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
}
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now();
var result = {};
if (options.output.ast) {
result.ast = toplevel;
}
if (!HOP(options.output, "code") || options.output.code) {
if (options.sourceMap) {
if (typeof options.sourceMap.content == "string") {
options.sourceMap.content = JSON.parse(options.sourceMap.content);
}
options.output.source_map = SourceMap({
file: options.sourceMap.filename,
orig: options.sourceMap.content,
root: options.sourceMap.root
});
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}
}
delete options.output.ast;
delete options.output.code;
var stream = OutputStream(options.output);
toplevel.print(stream);
result.code = stream.get();
if (options.sourceMap) {
result.map = options.output.source_map.toString();
if (options.sourceMap.url == "inline") {
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map);
} else if (options.sourceMap.url) {
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
}
}
}
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),
total: 1e-3 * (timings.end - timings.start)
}
}
if (warnings.length) {
result.warnings = warnings;
}
return result;
} catch (ex) {
return { error: ex };
} finally {
AST_Node.warn_function = warn_function;
}
}

View File

@@ -111,19 +111,23 @@
}, },
Property: function(M) { Property: function(M) {
var key = M.key; var key = M.key;
var name = key.type == "Identifier" ? key.name : key.value;
var args = { var args = {
start : my_start_token(key), start : my_start_token(key),
end : my_end_token(M.value), end : my_end_token(M.value),
key : key.type == "Identifier" ? key.name : key.value, key : name,
value : from_moz(M.value) value : from_moz(M.value)
}; };
if (M.kind == "init") return new AST_ObjectKeyVal(args); switch (M.kind) {
args.key = new AST_SymbolAccessor({ case "init":
name: args.key return new AST_ObjectKeyVal(args);
}); case "set":
args.value = new AST_Accessor(args.value); args.value.name = from_moz(key);
if (M.kind == "get") return new AST_ObjectGetter(args); return new AST_ObjectSetter(args);
if (M.kind == "set") return new AST_ObjectSetter(args); case "get":
args.value.name = from_moz(key);
return new AST_ObjectGetter(args);
}
}, },
ArrayExpression: function(M) { ArrayExpression: function(M) {
return new AST_Array({ return new AST_Array({
@@ -145,11 +149,7 @@
}); });
}, },
SequenceExpression: function(M) { SequenceExpression: function(M) {
return new AST_Sequence({ return AST_Seq.from_array(M.expressions.map(from_moz));
start : my_start_token(M),
end : my_end_token(M),
expressions: M.expressions.map(from_moz)
});
}, },
MemberExpression: function(M) { MemberExpression: function(M) {
return new (M.computed ? AST_Sub : AST_Dot)({ return new (M.computed ? AST_Sub : AST_Dot)({
@@ -168,7 +168,7 @@
}); });
}, },
VariableDeclaration: function(M) { VariableDeclaration: function(M) {
return new AST_Var({ return new (M.kind === "const" ? AST_Const : AST_Var)({
start : my_start_token(M), start : my_start_token(M),
end : my_end_token(M), end : my_end_token(M),
definitions : M.declarations.map(from_moz) definitions : M.declarations.map(from_moz)
@@ -204,7 +204,7 @@
Identifier: function(M) { Identifier: function(M) {
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
return new ( p.type == "LabeledStatement" ? AST_Label return new ( p.type == "LabeledStatement" ? AST_Label
: p.type == "VariableDeclarator" && p.id === M ? AST_SymbolVar : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
: p.type == "CatchClause" ? AST_SymbolCatch : p.type == "CatchClause" ? AST_SymbolCatch
@@ -256,7 +256,10 @@
map("CallExpression", AST_Call, "callee>expression, arguments@args"); map("CallExpression", AST_Call, "callee>expression, arguments@args");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) { def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return to_moz_scope("Program", M); return {
type: "Program",
body: M.body.map(to_moz)
};
}); });
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
@@ -264,7 +267,7 @@
type: "FunctionDeclaration", type: "FunctionDeclaration",
id: to_moz(M.name), id: to_moz(M.name),
params: M.argnames.map(to_moz), params: M.argnames.map(to_moz),
body: to_moz_scope("BlockStatement", M) body: to_moz_block(M)
} }
}); });
@@ -273,7 +276,7 @@
type: "FunctionExpression", type: "FunctionExpression",
id: to_moz(M.name), id: to_moz(M.name),
params: M.argnames.map(to_moz), params: M.argnames.map(to_moz),
body: to_moz_scope("BlockStatement", M) body: to_moz_block(M)
} }
}); });
@@ -324,15 +327,15 @@
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
return { return {
type: "VariableDeclaration", type: "VariableDeclaration",
kind: "var", kind: M instanceof AST_Const ? "const" : "var",
declarations: M.definitions.map(to_moz) declarations: M.definitions.map(to_moz)
}; };
}); });
def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) { def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
return { return {
type: "SequenceExpression", type: "SequenceExpression",
expressions: M.expressions.map(to_moz) expressions: M.to_array().map(to_moz)
}; };
}); });
@@ -379,10 +382,11 @@
}); });
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = { var key = (
type: "Literal", is_identifier(M.key)
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key ? {type: "Identifier", name: M.key}
}; : {type: "Literal", value: M.key}
);
var kind; var kind;
if (M instanceof AST_ObjectKeyVal) { if (M instanceof AST_ObjectKeyVal) {
kind = "init"; kind = "init";
@@ -543,8 +547,8 @@
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
exports, my_start_token, my_end_token, from_moz exports, my_start_token, my_end_token, from_moz
); );
me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")( me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
to_moz, to_moz_block, to_moz_scope to_moz, to_moz_block
); );
MOZ_TO_ME[moztype] = moz_to_me; MOZ_TO_ME[moztype] = moz_to_me;
def_to_moz(mytype, me_to_moz); def_to_moz(mytype, me_to_moz);
@@ -602,14 +606,4 @@
}; };
}; };
function to_moz_scope(type, node) {
var body = node.body.map(to_moz);
if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) {
body.unshift(to_moz(new AST_EmptyStatement(node.body[0])));
}
return {
type: type,
body: body
};
};
})(); })();

View File

@@ -57,7 +57,6 @@ function OutputStream(options) {
beautify : false, beautify : false,
bracketize : false, bracketize : false,
comments : false, comments : false,
ie8 : false,
indent_level : 4, indent_level : 4,
indent_start : 0, indent_start : 0,
inline_script : true, inline_script : true,
@@ -67,9 +66,12 @@ function OutputStream(options) {
preserve_line : false, preserve_line : false,
quote_keys : false, quote_keys : false,
quote_style : 0, quote_style : 0,
screw_ie8 : true,
semicolons : true, semicolons : true,
shebang : true, shebang : true,
source_map : null, source_map : null,
space_colon : true,
unescape_regexps : false,
width : 80, width : 80,
wrap_iife : false, wrap_iife : false,
}, true); }, true);
@@ -134,7 +136,7 @@ function OutputStream(options) {
case "\t": return "\\t"; case "\t": return "\\t";
case "\b": return "\\b"; case "\b": return "\\b";
case "\f": return "\\f"; case "\f": return "\\f";
case "\x0B": return options.ie8 ? "\\x0B" : "\\v"; case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
case "\u2028": return "\\u2028"; case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029"; case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff"; case "\ufeff": return "\\ufeff";
@@ -189,43 +191,12 @@ function OutputStream(options) {
var might_need_semicolon = false; var might_need_semicolon = false;
var might_add_newline = 0; var might_add_newline = 0;
var last = ""; var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var do_add_mapping = mappings ? function() {
mappings.forEach(function(mapping) {
try {
options.source_map.add(
mapping.token.file,
mapping.line, mapping.col,
mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
});
mappings = [];
} : noop;
var ensure_line_len = options.max_line_len ? function() { var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) { if (current_col > options.max_line_len) {
if (might_add_newline) { if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline); var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline); var right = OUTPUT.slice(might_add_newline);
if (mappings) {
var delta = right.length - current_col;
mappings.forEach(function(mapping) {
mapping.line++;
mapping.col += delta;
});
}
OUTPUT = left + "\n" + right; OUTPUT = left + "\n" + right;
current_line++; current_line++;
current_pos++; current_pos++;
@@ -235,10 +206,7 @@ function OutputStream(options) {
AST_Node.warn("Output exceeds {max_line_len} characters", options); AST_Node.warn("Output exceeds {max_line_len} characters", options);
} }
} }
if (might_add_newline) {
might_add_newline = 0; might_add_newline = 0;
do_add_mapping();
}
} : noop; } : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , ."); var requireSemicolonChars = makePredicate("( [ + * / - , .");
@@ -298,18 +266,6 @@ function OutputStream(options) {
} }
might_need_space = false; might_need_space = false;
} }
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str; OUTPUT += str;
current_pos += str.length; current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1; var a = str.split(/\r?\n/), n = a.length - 1;
@@ -401,12 +357,27 @@ function OutputStream(options) {
function colon() { function colon() {
print(":"); print(":");
space(); if (options.space_colon) space();
}; };
var add_mapping = mappings ? function(token, name) { var add_mapping = options.source_map ? function(token, name) {
mapping_token = token; try {
mapping_name = name; if (token) options.source_map.add(
token.file || "?",
current_line, current_col,
token.line, token.col,
(!name && token.type == "name") ? token.value : name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: token.file,
line: token.line,
col: token.col,
cline: current_line,
ccol: current_col,
name: name || ""
})
}
} : noop; } : noop;
function get() { function get() {
@@ -617,7 +588,7 @@ function OutputStream(options) {
|| p instanceof AST_Call && p.expression === this; || p instanceof AST_Call && p.expression === this;
}); });
PARENS(AST_Sequence, function(output){ PARENS(AST_Seq, function(output){
var p = output.parent(); var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| p instanceof AST_Unary // !(foo, bar, baz) || p instanceof AST_Unary // !(foo, bar, baz)
@@ -706,7 +677,7 @@ function OutputStream(options) {
} }
}); });
PARENS([ AST_Assign, AST_Conditional ], function(output){ PARENS([ AST_Assign, AST_Conditional ], function (output){
var p = output.parent(); var p = output.parent();
// !(a = false) → true // !(a = false) → true
if (p instanceof AST_Unary) if (p instanceof AST_Unary)
@@ -931,7 +902,7 @@ function OutputStream(options) {
function make_then(self, output) { function make_then(self, output) {
var b = self.body; var b = self.body;
if (output.option("bracketize") if (output.option("bracketize")
|| output.option("ie8") && b instanceof AST_Do) || !output.option("screw_ie8") && b instanceof AST_Do)
return make_block(b, output); return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single // The squeezer replaces "block"-s that contain only a single
// statement with the statement itself; technically, the AST // statement with the statement itself; technically, the AST
@@ -1062,6 +1033,9 @@ function OutputStream(options) {
DEFPRINT(AST_Var, function(self, output){ DEFPRINT(AST_Var, function(self, output){
self._do_print(output, "var"); self._do_print(output, "var");
}); });
DEFPRINT(AST_Const, function(self, output){
self._do_print(output, "const");
});
function parenthesize_for_noin(node, output, noin) { function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output); if (!noin) node.print(output);
@@ -1109,19 +1083,18 @@ function OutputStream(options) {
AST_Call.prototype._codegen(self, output); AST_Call.prototype._codegen(self, output);
}); });
AST_Sequence.DEFMETHOD("_do_print", function(output){ AST_Seq.DEFMETHOD("_do_print", function(output){
this.expressions.forEach(function(node, index) { this.car.print(output);
if (index > 0) { if (this.cdr) {
output.comma(); output.comma();
if (output.should_break()) { if (output.should_break()) {
output.newline(); output.newline();
output.indent(); output.indent();
} }
this.cdr.print(output);
} }
node.print(output);
}); });
}); DEFPRINT(AST_Seq, function(self, output){
DEFPRINT(AST_Sequence, function(self, output){
self._do_print(output); self._do_print(output);
// var p = output.parent(); // var p = output.parent();
// if (p instanceof AST_Statement) { // if (p instanceof AST_Statement) {
@@ -1244,7 +1217,7 @@ function OutputStream(options) {
&& +key + "" == key) && +key + "" == key)
&& parseFloat(key) >= 0) { && parseFloat(key) >= 0) {
output.print(make_num(key)); output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) { } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) { if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote); output.print_string(key, quote);
} else { } else {
@@ -1290,14 +1263,45 @@ function OutputStream(options) {
} }
}); });
function regexp_safe_literal(code) {
return [
0x5c , // \
0x2f , // /
0x2e , // .
0x2b , // +
0x2a , // *
0x3f , // ?
0x28 , // (
0x29 , // )
0x5b , // [
0x5d , // ]
0x7b , // {
0x7d , // }
0x24 , // $
0x5e , // ^
0x3a , // :
0x7c , // |
0x21 , // !
0x0a , // \n
0x0d , // \r
0x00 , // \0
0xfeff , // Unicode BOM
0x2028 , // unicode "line separator"
0x2029 , // unicode "paragraph separator"
].indexOf(code) < 0;
};
DEFPRINT(AST_RegExp, function(self, output){ DEFPRINT(AST_RegExp, function(self, output){
var regexp = self.getValue(); var str = self.getValue().toString();
var str = regexp.toString();
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) { if (output.option("ascii_only")) {
str = output.to_ascii(str); str = output.to_ascii(str);
} else if (output.option("unescape_regexps")) {
str = str.split("\\\\").map(function(str){
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
var code = parseInt(s.substr(2), 16);
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
});
}).join("\\\\");
} }
output.print(str); output.print(str);
var p = output.parent(); var p = output.parent();

View File

@@ -111,7 +111,7 @@ var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u20
var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029")); var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029"));
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:")); var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
@@ -285,11 +285,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value))); (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
if (type == "punc" && value == ".") { prev_was_dot = (type == "punc" && value == ".");
prev_was_dot = true;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = { var ret = {
type : type, type : type,
value : value, value : value,
@@ -477,31 +473,29 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return name; return name;
}; };
var read_regexp = with_eof_error("Unterminated regular expression", function(source) { var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var prev_backslash = false, ch, in_class = false; var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) { while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("Unexpected line terminator"); parse_error("Unexpected line terminator");
} else if (prev_backslash) { } else if (prev_backslash) {
source += "\\" + ch; regexp += "\\" + ch;
prev_backslash = false; prev_backslash = false;
} else if (ch == "[") { } else if (ch == "[") {
in_class = true; in_class = true;
source += ch; regexp += ch;
} else if (ch == "]" && in_class) { } else if (ch == "]" && in_class) {
in_class = false; in_class = false;
source += ch; regexp += ch;
} else if (ch == "/" && !in_class) { } else if (ch == "/" && !in_class) {
break; break;
} else if (ch == "\\") { } else if (ch == "\\") {
prev_backslash = true; prev_backslash = true;
} else { } else {
source += ch; regexp += ch;
} }
var mods = read_name(); var mods = read_name();
try { try {
var regexp = new RegExp(source, mods); return token("regexp", new RegExp(regexp, mods));
regexp.raw_source = source;
return token("regexp", regexp);
} catch(e) { } catch(e) {
parse_error(e.message); parse_error(e.message);
} }
@@ -635,7 +629,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
next_token.has_directive = function(directive) { next_token.has_directive = function(directive) {
return S.directives[directive] > 0; return S.directives[directive] !== undefined &&
S.directives[directive] > 0;
} }
return next_token; return next_token;
@@ -694,13 +689,14 @@ function parse($TEXT, options) {
options = defaults(options, { options = defaults(options, {
bare_returns : false, bare_returns : false,
cli : false,
expression : false, expression : false,
filename : null, filename : null,
html5_comments : true, html5_comments : true,
shebang : true, shebang : true,
strict : false, strict : false,
toplevel : null, toplevel : null,
}, true); });
var S = { var S = {
input : (typeof $TEXT == "string" input : (typeof $TEXT == "string"
@@ -807,23 +803,28 @@ function parse($TEXT, options) {
}; };
var statement = embed_tokens(function() { var statement = embed_tokens(function() {
var tmp;
handle_regexp(); handle_regexp();
switch (S.token.type) { switch (S.token.type) {
case "string": case "string":
if (S.in_directives) { var dir = false;
var token = peek(); if (S.in_directives === true) {
if (S.token.raw.indexOf("\\") == -1 if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) {
&& (token.nlb
|| is_token(token, "eof")
|| is_token(token, "punc", ";")
|| is_token(token, "punc", "}"))) {
S.input.add_directive(S.token.value); S.input.add_directive(S.token.value);
} else { } else {
S.in_directives = false; S.in_directives = false;
} }
} }
var dir = S.in_directives, stat = simple_statement(); var dir = S.in_directives, stat = simple_statement();
return dir ? new AST_Directive(stat.body) : stat; if (dir) {
return new AST_Directive({
start : stat.body.start,
end : stat.body.end,
quote : stat.body.quote,
value : stat.body.value,
});
}
return stat;
case "num": case "num":
case "regexp": case "regexp":
case "operator": case "operator":
@@ -855,97 +856,75 @@ function parse($TEXT, options) {
} }
case "keyword": case "keyword":
switch (S.token.value) { switch (tmp = S.token.value, next(), tmp) {
case "break": case "break":
next();
return break_cont(AST_Break); return break_cont(AST_Break);
case "continue": case "continue":
next();
return break_cont(AST_Continue); return break_cont(AST_Continue);
case "debugger": case "debugger":
next();
semicolon(); semicolon();
return new AST_Debugger(); return new AST_Debugger();
case "do": case "do":
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
semicolon(true);
return new AST_Do({ return new AST_Do({
body : body, body : in_loop(statement),
condition : condition condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
}); });
case "while": case "while":
next();
return new AST_While({ return new AST_While({
condition : parenthesised(), condition : parenthesised(),
body : in_loop(statement) body : in_loop(statement)
}); });
case "for": case "for":
next();
return for_(); return for_();
case "function": case "function":
next();
return function_(AST_Defun); return function_(AST_Defun);
case "if": case "if":
next();
return if_(); return if_();
case "return": case "return":
if (S.in_function == 0 && !options.bare_returns) if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function"); croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({ return new AST_Return({
value: value value: ( is("punc", ";")
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
}); });
case "switch": case "switch":
next();
return new AST_Switch({ return new AST_Switch({
expression : parenthesised(), expression : parenthesised(),
body : in_loop(switch_body_) body : in_loop(switch_body_)
}); });
case "throw": case "throw":
next();
if (S.token.nlb) if (S.token.nlb)
croak("Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({ return new AST_Throw({
value: value value: (tmp = expression(true), semicolon(), tmp)
}); });
case "try": case "try":
next();
return try_(); return try_();
case "var": case "var":
next(); return tmp = var_(), semicolon(), tmp;
var node = var_();
semicolon(); case "const":
return node; return tmp = const_(), semicolon(), tmp;
case "with": case "with":
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement"); croak("Strict mode may not include a with statement");
} }
next();
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesised(),
body : statement() body : statement()
@@ -1055,32 +1034,29 @@ function parse($TEXT, options) {
if (in_statement && !name) if (in_statement && !name)
unexpected(); unexpected();
expect("("); expect("(");
var argnames = []; return new ctor({
for (var first = true; !is("punc", ")");) { name: name,
argnames: (function(first, a){
while (!is("punc", ")")) {
if (first) first = false; else expect(","); if (first) first = false; else expect(",");
argnames.push(as_symbol(AST_SymbolFunarg)); a.push(as_symbol(AST_SymbolFunarg));
} }
next(); next();
var loop = S.in_loop; return a;
var labels = S.labels; })(true, []),
body: (function(loop, labels){
++S.in_function; ++S.in_function;
S.in_directives = true; S.in_directives = true;
S.input.push_directives_stack(); S.input.push_directives_stack();
S.in_loop = 0; S.in_loop = 0;
S.labels = []; S.labels = [];
var body = block_(); var a = block_();
if (S.input.has_directive("use strict")) {
if (name) strict_verify_symbol(name);
argnames.forEach(strict_verify_symbol);
}
S.input.pop_directives_stack(); S.input.pop_directives_stack();
--S.in_function; --S.in_function;
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
return new ctor({ return a;
name: name, })(S.in_loop, S.labels)
argnames: argnames,
body: body
}); });
}; };
@@ -1176,12 +1152,12 @@ function parse($TEXT, options) {
}); });
}; };
function vardefs(no_in) { function vardefs(no_in, in_const) {
var a = []; var a = [];
for (;;) { for (;;) {
a.push(new AST_VarDef({ a.push(new AST_VarDef({
start : S.token, start : S.token,
name : as_symbol(AST_SymbolVar), name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
value : is("operator", "=") ? (next(), expression(false, no_in)) : null, value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
end : prev() end : prev()
})); }));
@@ -1195,7 +1171,15 @@ function parse($TEXT, options) {
var var_ = function(no_in) { var var_ = function(no_in) {
return new AST_Var({ return new AST_Var({
start : prev(), start : prev(),
definitions : vardefs(no_in), definitions : vardefs(no_in, false),
end : prev()
});
};
var const_ = function() {
return new AST_Const({
start : prev(),
definitions : vardefs(false, true),
end : prev() end : prev()
}); });
}; };
@@ -1336,15 +1320,10 @@ function parse($TEXT, options) {
var type = start.type; var type = start.type;
var name = as_property_name(); var name = as_property_name();
if (type == "name" && !is("punc", ":")) { if (type == "name" && !is("punc", ":")) {
var key = new AST_SymbolAccessor({
start: S.token,
name: as_property_name(),
end: prev()
});
if (name == "get") { if (name == "get") {
a.push(new AST_ObjectGetter({ a.push(new AST_ObjectGetter({
start : start, start : start,
key : key, key : as_atom_node(),
value : create_accessor(), value : create_accessor(),
end : prev() end : prev()
})); }));
@@ -1353,7 +1332,7 @@ function parse($TEXT, options) {
if (name == "set") { if (name == "set") {
a.push(new AST_ObjectSetter({ a.push(new AST_ObjectSetter({
start : start, start : start,
key : key, key : as_atom_node(),
value : create_accessor(), value : create_accessor(),
end : prev() end : prev()
})); }));
@@ -1375,15 +1354,14 @@ function parse($TEXT, options) {
function as_property_name() { function as_property_name() {
var tmp = S.token; var tmp = S.token;
next();
switch (tmp.type) { switch (tmp.type) {
case "operator":
if (!KEYWORDS(tmp.value)) unexpected();
case "num": case "num":
case "string": case "string":
case "name": case "name":
case "operator":
case "keyword": case "keyword":
case "atom": case "atom":
next();
return tmp.value; return tmp.value;
default: default:
unexpected(); unexpected();
@@ -1392,9 +1370,16 @@ function parse($TEXT, options) {
function as_name() { function as_name() {
var tmp = S.token; var tmp = S.token;
if (tmp.type != "name") unexpected();
next(); next();
switch (tmp.type) {
case "name":
case "operator":
case "keyword":
case "atom":
return tmp.value; return tmp.value;
default:
unexpected();
}
}; };
function _make_symbol(type) { function _make_symbol(type) {
@@ -1406,20 +1391,12 @@ function parse($TEXT, options) {
}); });
}; };
function strict_verify_symbol(sym) {
if (sym.name == "arguments" || sym.name == "eval")
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
}
function as_symbol(type, noerror) { function as_symbol(type, noerror) {
if (!is("name")) { if (!is("name")) {
if (!noerror) croak("Name expected"); if (!noerror) croak("Name expected");
return null; return null;
} }
var sym = _make_symbol(type); var sym = _make_symbol(type);
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
strict_verify_symbol(sym);
}
next(); next();
return sym; return sym;
}; };
@@ -1463,14 +1440,14 @@ function parse($TEXT, options) {
if (is("operator") && UNARY_PREFIX(start.value)) { if (is("operator") && UNARY_PREFIX(start.value)) {
next(); next();
handle_regexp(); handle_regexp();
var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls)); var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
ex.start = start; ex.start = start;
ex.end = prev(); ex.end = prev();
return ex; return ex;
} }
var val = expr_atom(allow_calls); var val = expr_atom(allow_calls);
while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
val = make_unary(AST_UnaryPostfix, S.token, val); val = make_unary(AST_UnaryPostfix, S.token.value, val);
val.start = start; val.start = start;
val.end = S.token; val.end = S.token;
next(); next();
@@ -1478,19 +1455,9 @@ function parse($TEXT, options) {
return val; return val;
}; };
function make_unary(ctor, token, expr) { function make_unary(ctor, op, expr) {
var op = token.value; if ((op == "++" || op == "--") && !is_assignable(expr))
switch (op) { croak("Invalid use of " + op + " operator", null, ctor === AST_UnaryPrefix ? expr.start.col - 1 : null);
case "++":
case "--":
if (!is_assignable(expr))
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
break;
case "delete":
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
break;
}
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
}; };
@@ -1535,6 +1502,7 @@ function parse($TEXT, options) {
}; };
function is_assignable(expr) { function is_assignable(expr) {
if (options.cli) return true;
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
}; };
@@ -1559,18 +1527,17 @@ function parse($TEXT, options) {
var expression = function(commas, no_in) { var expression = function(commas, no_in) {
var start = S.token; var start = S.token;
var exprs = []; var expr = maybe_assign(no_in);
while (true) { if (commas && is("punc", ",")) {
exprs.push(maybe_assign(no_in));
if (!commas || !is("punc", ",")) break;
next(); next();
commas = true; return new AST_Seq({
}
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
start : start, start : start,
expressions : exprs, car : expr,
cdr : expression(true, no_in),
end : peek() end : peek()
}); });
}
return expr;
}; };
function in_loop(cont) { function in_loop(cont) {

View File

@@ -43,16 +43,16 @@
"use strict"; "use strict";
function find_builtins(reserved) { function find_builtins() {
// NaN will be included due to Number.NaN // NaN will be included due to Number.NaN
[ var a = [
"null", "null",
"true", "true",
"false", "false",
"Infinity", "Infinity",
"-Infinity", "-Infinity",
"undefined", "undefined",
].forEach(add); ];
[ Object, Array, Function, Number, [ Object, Array, Function, Number,
String, Boolean, Error, Math, String, Boolean, Error, Math,
Date, RegExp Date, RegExp
@@ -63,23 +63,24 @@ function find_builtins(reserved) {
} }
}); });
function add(name) { function add(name) {
push_uniq(reserved, name); push_uniq(a, name);
} }
return a;
} }
function mangle_properties(ast, options) { function mangle_properties(ast, options) {
options = defaults(options, { options = defaults(options, {
builtins: false,
cache: null, cache: null,
debug: false, debug: false,
keep_quoted: false, ignore_quoted: false,
only_cache: false, only_cache: false,
regex: null, regex: null,
reserved: null, reserved: null,
}); });
var reserved = options.reserved || []; var reserved = options.reserved;
if (!options.builtins) find_builtins(reserved); if (reserved == null)
reserved = find_builtins();
var cache = options.cache; var cache = options.cache;
if (cache == null) { if (cache == null) {
@@ -90,12 +91,12 @@ function mangle_properties(ast, options) {
} }
var regex = options.regex; var regex = options.regex;
var keep_quoted = options.keep_quoted; var ignore_quoted = options.ignore_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled). // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
// the same as passing an empty string. // the same as passing an empty string.
var debug = options.debug !== false; var debug = (options.debug !== false);
var debug_name_suffix; var debug_name_suffix;
if (debug) { if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug); debug_name_suffix = (options.debug === true ? "" : options.debug);
@@ -103,12 +104,12 @@ function mangle_properties(ast, options) {
var names_to_mangle = []; var names_to_mangle = [];
var unmangleable = []; var unmangleable = [];
var to_keep = {}; var ignored = {};
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){ ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote); add(node.key, ignore_quoted && node.quote);
} }
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above // setter or getter, since KeyVal is handled above
@@ -118,14 +119,14 @@ function mangle_properties(ast, options) {
add(node.property); add(node.property);
} }
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted); addStrings(node.property, ignore_quoted);
} }
})); }));
// step 2: transform the tree, renaming properties // step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){ return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote)) if (!(ignore_quoted && node.quote))
node.key = mangle(node.key); node.key = mangle(node.key);
} }
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
@@ -136,7 +137,7 @@ function mangle_properties(ast, options) {
node.property = mangle(node.property); node.property = mangle(node.property);
} }
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
if (!keep_quoted) if (!ignore_quoted)
node.property = mangleStrings(node.property); node.property = mangleStrings(node.property);
} }
// else if (node instanceof AST_String) { // else if (node instanceof AST_String) {
@@ -166,16 +167,16 @@ function mangle_properties(ast, options) {
} }
function should_mangle(name) { function should_mangle(name) {
if (keep_quoted && name in to_keep) return false; if (ignore_quoted && name in ignored) return false;
if (regex && !regex.test(name)) return false; if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name) return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0; || names_to_mangle.indexOf(name) >= 0;
} }
function add(name, keep) { function add(name, ignore) {
if (keep) { if (ignore) {
to_keep[name] = true; ignored[name] = true;
return; return;
} }
@@ -198,19 +199,19 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_"; var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) { if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
mangled = debug_mangled; mangled = debug_mangled;
} }
} }
// either debug mode is off, or it is on and we could not use the mangled name // either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) { if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set // note can_mangle() does not check if the name collides with the 'ignored' set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this // (filled with quoted properties when ignore_quoted set). Make sure we add this
// check so we don't collide with a quoted name. // check so we don't collide with a quoted name.
do { do {
mangled = base54(++cache.cname); mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep); } while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
} }
cache.props.set(name, mangled); cache.props.set(name, mangled);
@@ -218,17 +219,17 @@ function mangle_properties(ast, options) {
return mangled; return mangled;
} }
function addStrings(node, keep) { function addStrings(node, ignore) {
var out = {}; var out = {};
try { try {
(function walk(node){ (function walk(node){
node.walk(new TreeWalker(function(node){ node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) { if (node instanceof AST_Seq) {
walk(node.expressions[node.expressions.length - 1]); walk(node.cdr);
return true; return true;
} }
if (node instanceof AST_String) { if (node instanceof AST_String) {
add(node.value, keep); add(node.value, ignore);
return true; return true;
} }
if (node instanceof AST_Conditional) { if (node instanceof AST_Conditional) {
@@ -246,9 +247,8 @@ function mangle_properties(ast, options) {
function mangleStrings(node) { function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){ return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) { if (node instanceof AST_Seq) {
var last = node.expressions.length - 1; node.cdr = mangleStrings(node.cdr);
node.expressions[last] = mangleStrings(node.expressions[last]);
} }
else if (node instanceof AST_String) { else if (node instanceof AST_String) {
node.value = mangle(node.value); node.value = mangle(node.value);
@@ -260,4 +260,5 @@ function mangle_properties(ast, options) {
return node; return node;
})); }));
} }
} }

View File

@@ -76,7 +76,7 @@ SymbolDef.prototype = {
else if (!this.mangled_name && !this.unmangleable(options)) { else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope; var s = this.scope;
var sym = this.orig[0]; var sym = this.orig[0];
if (options.ie8 && sym instanceof AST_SymbolLambda) if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope; s = s.parent_scope;
var def; var def;
if (this.defun && (def = this.defun.variables.get(this.name))) { if (this.defun && (def = this.defun.variables.get(this.name))) {
@@ -93,7 +93,7 @@ SymbolDef.prototype = {
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, { options = defaults(options, {
cache: null, cache: null,
ie8: false, screw_ie8: true,
}); });
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
@@ -156,7 +156,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// later. // later.
(node.scope = defun.parent_scope).def_function(node); (node.scope = defun.parent_scope).def_function(node);
} }
else if (node instanceof AST_SymbolVar) { else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
defun.def_variable(node); defun.def_variable(node);
if (defun !== scope) { if (defun !== scope) {
node.mark_enclosed(options); node.mark_enclosed(options);
@@ -183,8 +184,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(tw); self.walk(tw);
// pass 2: find back references and eval // pass 2: find back references and eval
self.globals = new Dictionary(); var func = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
if (node instanceof AST_LoopControl && node.label) { if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node); node.label.thedef.references.push(node);
return true; return true;
@@ -211,7 +220,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(tw); self.walk(tw);
// pass 3: fix up any scoping issue with IE8 // pass 3: fix up any scoping issue with IE8
if (options.ie8) { if (!options.screw_ie8) {
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;
@@ -259,7 +268,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({ this.def_variable(new AST_SymbolVar({
name: "arguments", name: "arguments",
start: this.start, start: this.start,
end: this.end end: this.end
@@ -316,8 +325,8 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
if (!is_identifier(m)) continue; // skip over "do" if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling. // shadow a name excepted from mangling.
if (options.reserved.indexOf(m) >= 0) continue; if (options.except.indexOf(m) >= 0) continue;
// we must ensure that the mangled name does not shadow a name // we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in // from some parent scope that is referenced in this or in
@@ -352,8 +361,15 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options); return this.definition().unmangleable(options);
}); });
// property accessors are not mangleable
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
return true;
});
// labels are always mangleable // labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", return_false); AST_Label.DEFMETHOD("unmangleable", function(){
return false;
});
AST_Symbol.DEFMETHOD("unreferenced", function(){ AST_Symbol.DEFMETHOD("unreferenced", function(){
return this.definition().references.length == 0 return this.definition().references.length == 0
@@ -364,9 +380,13 @@ AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared; return this.definition().undeclared;
}); });
AST_LabelRef.DEFMETHOD("undeclared", return_false); AST_LabelRef.DEFMETHOD("undeclared", function(){
return false;
});
AST_Label.DEFMETHOD("undeclared", return_false); AST_Label.DEFMETHOD("undeclared", function(){
return false;
});
AST_Symbol.DEFMETHOD("definition", function(){ AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef; return this.thedef;
@@ -379,9 +399,10 @@ AST_Symbol.DEFMETHOD("global", function(){
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, { return defaults(options, {
eval : false, eval : false,
ie8 : false, except : [],
keep_fnames : false, keep_fnames : false,
reserved : [], screw_ie8 : true,
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false, toplevel : false,
}); });
}); });
@@ -390,7 +411,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options); options = this._default_mangler_options(options);
// Never mangle arguments // Never mangle arguments
options.reserved.push('arguments'); options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired // We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's // into the code generator will display the mangled name if it's
@@ -401,7 +422,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (options.cache) { if (options.cache) {
this.globals.each(function(symbol){ this.globals.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) { if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol); to_mangle.push(symbol);
} }
}); });
@@ -418,7 +439,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
var p = tw.parent(), a = []; var p = tw.parent(), a = [];
node.variables.each(function(symbol){ node.variables.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) { if (options.except.indexOf(symbol.name) < 0) {
a.push(symbol); a.push(symbol);
} }
}); });
@@ -431,7 +452,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name; node.mangled_name = name;
return true; return true;
} }
if (!options.ie8 && node instanceof AST_SymbolCatch) { if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition()); to_mangle.push(node.definition());
return; return;
} }
@@ -471,6 +492,8 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
} }
else if (node instanceof AST_Var) else if (node instanceof AST_Var)
base54.consider("var"); base54.consider("var");
else if (node instanceof AST_Const)
base54.consider("const");
else if (node instanceof AST_Lambda) else if (node instanceof AST_Lambda)
base54.consider("function"); base54.consider("function");
else if (node instanceof AST_For) else if (node instanceof AST_For)
@@ -550,3 +573,89 @@ var base54 = (function() {
}; };
return base54; return base54;
})(); })();
AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
options = defaults(options, {
assign_to_global : true,
eval : true,
func_arguments : true,
nested_defuns : true,
undeclared : false, // this makes a lot of noise
unreferenced : true,
});
var tw = new TreeWalker(function(node){
if (options.undeclared
&& node instanceof AST_SymbolRef
&& node.undeclared())
{
// XXX: this also warns about JS standard names,
// i.e. Object, Array, parseInt etc. Should add a list of
// exceptions.
AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.assign_to_global)
{
var sym = null;
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)
sym = node.left;
else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)
sym = node.init;
if (sym
&& (sym.undeclared()
|| (sym.global() && sym.scope !== sym.definition().scope))) {
AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
name: sym.name,
file: sym.start.file,
line: sym.start.line,
col: sym.start.col
});
}
}
if (options.eval
&& node instanceof AST_SymbolRef
&& node.undeclared()
&& node.name == "eval") {
AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
}
if (options.unreferenced
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
&& !(node instanceof AST_SymbolCatch)
&& node.unreferenced()) {
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
type: node instanceof AST_Label ? "Label" : "Symbol",
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.func_arguments
&& node instanceof AST_Lambda
&& node.uses_arguments) {
AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
name: node.name ? node.name.name : "anonymous",
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.nested_defuns
&& node instanceof AST_Defun
&& !(tw.parent() instanceof AST_Scope)) {
AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
name: node.name.name,
type: tw.parent().TYPE,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
});
this.walk(tw);
});

View File

@@ -174,8 +174,9 @@ TreeTransformer.prototype = new TreeWalker;
self.args = do_list(self.args, tw); self.args = do_list(self.args, tw);
}); });
_(AST_Sequence, function(self, tw){ _(AST_Seq, function(self, tw){
self.expressions = do_list(self.expressions, tw); self.car = self.car.transform(tw);
self.cdr = self.cdr.transform(tw);
}); });
_(AST_Dot, function(self, tw){ _(AST_Dot, function(self, tw){

View File

@@ -346,7 +346,7 @@ function first_in_statement(stack) {
for (var i = 0, p; p = stack.parent(i); i++) { for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node) if (p instanceof AST_Statement && p.body === node)
return true; return true;
if ((p instanceof AST_Sequence && p.expressions[0] === node) || if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) || (p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) || (p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) || (p instanceof AST_Sub && p.expression === node ) ||

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": "3.0.13", "version": "2.8.23",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -29,16 +29,26 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.9.0", "source-map": "~0.5.1",
"source-map": "~0.5.1" "yargs": "~3.10.0"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~5.0.3", "acorn": "~0.6.0",
"mocha": "~2.3.4", "escodegen": "~1.3.3",
"semver": "~5.3.0" "esfuzz": "~0.3.1",
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
}, },
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
}, },
"keywords": ["uglify", "uglify-js", "minify", "minifier", "es5"] "keywords": ["uglify", "uglify-js", "minify", "minifier"]
} }

View File

@@ -7,9 +7,9 @@ var createHash = require("crypto").createHash;
var fork = require("child_process").fork; var fork = require("child_process").fork;
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc", "warnings=false");
} }
args.push("--timings"); args.push("--stats");
var urls = [ var urls = [
"https://code.jquery.com/jquery-3.2.1.js", "https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js", "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
@@ -30,6 +30,11 @@ function done() {
console.log(); console.log();
console.log(url); console.log(url);
console.log(info.log); console.log(info.log);
var elapsed = 0;
info.log.replace(/: ([0-9]+\.[0-9]{3})s/g, function(match, time) {
elapsed += parseFloat(time);
});
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log("Original:", info.input, "bytes"); console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes"); console.log("Uglified:", info.output, "bytes");
console.log("SHA1 sum:", info.sha1); console.log("SHA1 sum:", info.sha1);

67
test/compress/angular-inject.js vendored Normal file
View File

@@ -0,0 +1,67 @@
ng_inject_defun: {
options = {
angular: true
};
input: {
/*@ngInject*/
function Controller(dependency) {
return dependency;
}
}
expect: {
function Controller(dependency) {
return dependency;
}
Controller.$inject=['dependency']
}
}
ng_inject_assignment: {
options = {
angular: true
};
input: {
/*@ngInject*/
var Controller = function(dependency) {
return dependency;
}
}
expect: {
var Controller = function(dependency) {
return dependency;
}
Controller.$inject=['dependency']
}
}
ng_inject_inline: {
options = {
angular: true
};
input: {
angular.module('a').
factory('b',
/*@ngInject*/
function(dependency) {
return dependency;
}).
directive('c',
/*@ngInject*/
function(anotherDependency) {
return anotherDependency;
})
}
expect: {
angular.module('a').
factory('b',[
'dependency',
function(dependency) {
return dependency;
}]).
directive('c',[
'anotherDependency',
function(anotherDependency) {
return anotherDependency;
}])
}
}

View File

@@ -2,7 +2,7 @@ ascii_only_true: {
options = {} options = {}
beautify = { beautify = {
ascii_only : true, ascii_only : true,
ie8 : false, screw_ie8 : true,
beautify : false, beautify : false,
} }
input: { input: {
@@ -20,7 +20,7 @@ ascii_only_false: {
options = {} options = {}
beautify = { beautify = {
ascii_only : false, ascii_only : false,
ie8 : false, screw_ie8 : true,
beautify : false, beautify : false,
} }
input: { input: {
@@ -33,3 +33,4 @@ ascii_only_false: {
} }
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}' expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
} }

View File

@@ -2,7 +2,7 @@ collapse_vars_side_effects_1: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -68,10 +68,11 @@ collapse_vars_side_effects_1: {
log(x, s.charAt(i++), y, 7); log(x, s.charAt(i++), y, 7);
} }
function f4() { function f4() {
var i = 10, var log = console.log.bind(console),
i = 10,
x = i += 2, x = i += 2,
y = i += 3; y = i += 3;
console.log.bind(console)(x, i += 4, y, i); log(x, i += 4, y, i);
} }
f1(), f2(), f3(), f4(); f1(), f2(), f3(), f4();
} }
@@ -150,7 +151,7 @@ collapse_vars_issue_721: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
define(["require", "exports", 'handlebars'], function (require, exports, hb) { define(["require", "exports", 'handlebars'], function (require, exports, hb) {
@@ -216,7 +217,7 @@ collapse_vars_properties: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1(obj) { function f1(obj) {
@@ -243,7 +244,7 @@ collapse_vars_if: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -293,7 +294,7 @@ collapse_vars_while: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:false, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:false, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1(y) { function f1(y) {
@@ -392,9 +393,9 @@ collapse_vars_do_while: {
} }
function f3(y) { function f3(y) {
function fn(n) { console.log(n); } function fn(n) { console.log(n); }
var a = 2, x = 7; var a = 2;
do { do {
fn(a = x); fn(a = 7);
break; break;
} while (y); } while (y);
} }
@@ -467,9 +468,8 @@ collapse_vars_do_while_drop_assign: {
} }
function f3(y) { function f3(y) {
function fn(n) { console.log(n); } function fn(n) { console.log(n); }
var x = 7;
do { do {
fn(x); fn(7);
break; break;
} while (y); } while (y);
} }
@@ -583,8 +583,8 @@ collapse_vars_assignment: {
return a = a; return a = a;
} }
function f1(c) { function f1(c) {
var a = 3 / c; const a = 3 / c;
var b = 1 - a; const b = 1 - a;
return b; return b;
} }
function f2(c) { function f2(c) {
@@ -670,8 +670,8 @@ collapse_vars_lvalues: {
function f4(x) { var a = (x -= 3); return x + a; } function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; } function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; }
function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); } function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); }
function f7(x) { var w = e1(); return (w = x) - (e2() - x); } function f7(x) { var w = e1(), v = e2(), c = v - x; return (w = x) - c; }
function f8(x) { var w = e1(); return (w = x) - (e2() - x); } function f8(x) { var w = e1(), v = e2(); return (w = x) - (v - x); }
function f9(x) { var w = e1(); return e2() - x - (w = x); } function f9(x) { var w = e1(); return e2() - x - (w = x); }
} }
} }
@@ -700,10 +700,10 @@ collapse_vars_lvalues_drop_assign: {
function f2(x) { var z = x, a = ++z; return z += a; } function f2(x) { var z = x, a = ++z; return z += a; }
function f3(x) { var a = (x -= 3); return x + a; } function f3(x) { var a = (x -= 3); return x + a; }
function f4(x) { var a = (x -= 3); return x + a; } function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { e1(); var v = e2(), c = v = --x; return x - c; } function f5(x) { var v = (e1(), e2()), c = v = --x; return x - c; }
function f6(x) { e1(), e2(); return --x - x; } function f6(x) { e1(), e2(); return --x - x; }
function f7(x) { e1(); return x - (e2() - x); } function f7(x) { var v = (e1(), e2()), c = v - x; return x - c; }
function f8(x) { e1(); return x - (e2() - x); } function f8(x) { var v = (e1(), e2()); return x - (v - x); }
function f9(x) { e1(); return e2() - x - x; } function f9(x) { e1(); return e2() - x - x; }
} }
} }
@@ -712,7 +712,7 @@ collapse_vars_misc1: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f0(o, a, h) { function f0(o, a, h) {
@@ -724,10 +724,10 @@ collapse_vars_misc1: {
return t; return t;
} }
function f1(x) { var y = 5 - x; return y; } function f1(x) { var y = 5 - x; return y; }
function f2(x) { var z = foo(), y = z / (5 - x); return y; } function f2(x) { const z = foo(), y = z / (5 - x); return y; }
function f3(x) { var z = foo(), y = (5 - x) / z; return y; } function f3(x) { var z = foo(), y = (5 - x) / z; return y; }
function f4(x) { var z = foo(), y = (5 - u) / z; return y; } function f4(x) { var z = foo(), y = (5 - u) / z; return y; }
function f5(x) { var z = foo(), y = (5 - window.x) / z; return y; } function f5(x) { const z = foo(), y = (5 - window.x) / z; return y; }
function f6() { var b = window.a * window.z; return b && zap(); } function f6() { var b = window.a * window.z; return b && zap(); }
function f7() { var b = window.a * window.z; return b + b; } function f7() { var b = window.a * window.z; return b + b; }
function f8() { var b = window.a * window.z; var c = b + 5; return b + c; } function f8() { var b = window.a * window.z; var c = b + 5; return b + c; }
@@ -744,7 +744,7 @@ collapse_vars_misc1: {
function f2(x) { return foo() / (5 - x) } function f2(x) { return foo() / (5 - x) }
function f3(x) { return (5 - x) / foo() } function f3(x) { return (5 - x) / foo() }
function f4(x) { var z = foo(); return (5 - u) / z } function f4(x) { var z = foo(); return (5 - u) / z }
function f5(x) { var z = foo(); return (5 - window.x) / z } function f5(x) { const z = foo(); return (5 - window.x) / z }
function f6() { return window.a * window.z && zap() } function f6() { return window.a * window.z && zap() }
function f7() { var b = window.a * window.z; return b + b } function f7() { var b = window.a * window.z; return b + b }
function f8() { var b = window.a * window.z; return b + (b + 5) } function f8() { var b = window.a * window.z; return b + (b + 5) }
@@ -789,7 +789,7 @@ collapse_vars_repeated: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -812,17 +812,19 @@ collapse_vars_repeated: {
} }
expect: { expect: {
function f1() { function f1() {
return -3; return -3
} }
function f2(x) { function f2(x) {
return x; return x
} }
(function(x){ (function(x){
console.log("GOOD!!"); var a = "GOOD" + x, e = "BAD", e = a;
})(), console.log(e + "!");
})("!"),
(function(x){ (function(x){
console.log("GOOD!!"); var a = "GOOD" + x, e = "BAD" + x, e = a;
})(); console.log(e + "!");
})("!");
} }
expect_stdout: true expect_stdout: true
} }
@@ -831,7 +833,7 @@ collapse_vars_closures: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function constant_vars_can_be_replaced_in_any_scope() { function constant_vars_can_be_replaced_in_any_scope() {
@@ -921,7 +923,7 @@ collapse_vars_try: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -1046,9 +1048,10 @@ collapse_vars_object: {
} }
expect: { expect: {
function f0(x, y) { function f0(x, y) {
var z = x + y;
return { return {
get b() { return 7; }, get b() { return 7; },
r: x + y r: z
}; };
} }
function f1(x, y) { function f1(x, y) {
@@ -1118,7 +1121,7 @@ collapse_vars_constants: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1(x) { function f1(x) {
@@ -1156,7 +1159,7 @@ collapse_vars_arguments: {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
toplevel:true, reduce_vars:true toplevel:true
} }
input: { input: {
var outer = function() { var outer = function() {
@@ -1283,7 +1286,6 @@ collapse_vars_regexp: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
reduce_vars: true,
} }
input: { input: {
function f1() { function f1() {
@@ -1317,8 +1319,8 @@ collapse_vars_regexp: {
}; };
} }
(function(){ (function(){
var result, rx = /ab*/g; var result, s = "acdabcdeabbb", rx = /ab*/g;
while (result = rx.exec("acdabcdeabbb")) while (result = rx.exec(s))
console.log(result[0]); console.log(result[0]);
})(); })();
} }
@@ -1342,10 +1344,7 @@ issue_1537: {
issue_1562: { issue_1562: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true, toplevel: true,
unused: true,
} }
input: { input: {
var v = 1, B = 2; var v = 1, B = 2;
@@ -1364,11 +1363,14 @@ issue_1562: {
var v = 1; var v = 1;
for (v in objs) f(2); for (v in objs) f(2);
while(5) bar(10); var x = 3;
while(x + 2) bar(10);
do bar(20); while(6); var y = 4;
do bar(20); while(y + 2);
for (; f(7) ;) bar(30); var z = 5;
for (; f(z + 2) ;) bar(30);
} }
} }
@@ -1590,599 +1592,3 @@ var_side_effects_3: {
} }
expect_stdout: true expect_stdout: true
} }
reduce_vars_assign: {
options = {
collapse_vars: true,
reduce_vars: true,
}
input: {
!function() {
var a = 1;
a = [].length,
console.log(a);
}();
}
expect: {
!function() {
var a = 1;
a = [].length,
console.log(a);
}();
}
expect_stdout: "0"
}
iife_1: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var log = function(x) {
console.log(x);
}, foo = bar();
log(foo);
}
expect: {
(function(x) {
console.log(x);
})(bar());
}
}
iife_2: {
options = {
collapse_vars: true,
reduce_vars: false,
toplevel: true,
unused: false,
}
input: {
var foo = bar();
!function(x) {
console.log(x);
}(foo);
}
expect: {
!function(x) {
console.log(x);
}(bar());
}
}
var_defs: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
var f1 = function(x, y) {
var a, b, r = x + y, q = r * r, z = q - r, a = z, b = 7;
console.log(a + b);
};
f1("1", 0);
}
expect: {
var f1 = function(x, y) {
var r = x + y, a = r * r - r, b = 7;
console.log(a + b);
};
f1("1", 0);
}
expect_stdout: "97"
}
assignment: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f() {
var a;
a = x;
return a;
}
}
expect: {
function f() {
return x;
}
}
}
for_init: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(x, y) {
var a = x;
var b = y;
for (a; b;);
}
}
expect: {
function f(x, y) {
var b = y;
for (x; b;);
}
}
}
switch_case: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(x, y, z) {
var a = x();
var b = y();
var c = z;
switch (a) {
default: d();
case b: e();
case c: f();
}
}
}
expect: {
function f(x, y, z) {
var c = z;
switch (x()) {
default: d();
case y(): e();
case c: f();
}
}
}
}
issue_27: {
options = {
collapse_vars: true,
unused: true,
}
input: {
(function(jQuery) {
var $;
$ = jQuery;
$("body").addClass("foo");
})(jQuery);
}
expect: {
(function(jQuery) {
jQuery("body").addClass("foo");
})(jQuery);
}
}
modified: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f1(b) {
var a = b;
return b + a;
}
function f2(b) {
var a = b;
return b++ + a;
}
function f3(b) {
var a = b++;
return b + a;
}
function f4(b) {
var a = b++;
return b++ + a;
}
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f1(1), f2(1), f3(1), f4(1), f5(1));
}
expect: {
function f1(b) {
return b + b;
}
function f2(b) {
var a = b;
return b++ + a;
}
function f3(b) {
var a = b++;
return b + a;
}
function f4(b) {
var a = b++;
return b++ + a;
}
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f1(1), f2(1), f3(1), f4(1), f5(1));
}
expect_stdout: "2 2 3 3 2"
}
issue_1858: {
options = {
collapse_vars: true,
pure_getters: true,
unused: true,
}
input: {
console.log(function(x) {
var a = {}, b = a.b = x;
return a.b + b;
}(1));
}
expect: {
console.log(function(x) {
var a = {}, b = a.b = x;
return a.b + b;
}(1));
}
expect_stdout: "2"
}
anonymous_function: {
options = {
collapse_vars: true,
}
input: {
console.log(function f(a) {
f ^= 0;
return f * a;
}(1));
}
expect: {
console.log(function f(a) {
f ^= 0;
return f * a;
}(1));
}
expect_stdout: true
}
side_effects_property: {
options = {
collapse_vars: true,
}
input: {
var a = [];
var b = 0;
a[b++] = function() { return 42;};
var c = a[b++]();
console.log(c);
}
expect: {
var a = [];
var b = 0;
a[b++] = function() { return 42;};
var c = a[b++]();
console.log(c);
}
expect_stdout: true
}
undeclared: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(x, y) {
var a;
a = x;
b = y;
return b + a;
}
}
expect: {
function f(x, y) {
var a;
a = x;
b = y;
return b + a;
}
}
}
ref_scope: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function() {
var a = 1, b = 2, c = 3;
var a = c++, b = b /= a;
return function() {
return a;
}() + b;
}());
}
expect: {
console.log(function() {
var a = 1, b = 2, c = 3;
b = b /= a = c++;
return function() {
return a;
}() + b;
}());
}
expect_stdout: true
}
chained_1: {
options = {
collapse_vars: true,
}
input: {
var a = 2;
var a = 3 / a;
console.log(a);
}
expect: {
var a = 3 / (a = 2);
console.log(a);
}
expect_stdout: true
}
chained_2: {
options = {
collapse_vars: true,
}
input: {
var a;
var a = 2;
a = 3 / a;
console.log(a);
}
expect: {
var a;
a = 3 / (a = 2);
console.log(a);
}
expect_stdout: true
}
chained_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect_stdout: "2"
}
boolean_binary_1: {
options = {
collapse_vars: true,
}
input: {
var a = 1;
a++;
(function() {} || a || 3).toString();
console.log(a);
}
expect: {
var a = 1;
a++;
(function() {} || a || 3).toString();
console.log(a);
}
expect_stdout: true
}
boolean_binary_2: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
c += 1;
(function() {
c = 1 + c;
} || 9).toString();
console.log(c);
}
expect: {
var c = 0;
c += 1;
(function() {
c = 1 + c;
} || 9).toString();
console.log(c);
}
expect_stdout: true
}
inner_lvalues: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a, b = 10;
var a = (--b || a || 3).toString(), c = --b + -a;
console.log(null, a, b);
}
expect: {
var a, b = 10;
var a = (--b || a || 3).toString(), c = --b + -a;
console.log(null, a, b);
}
expect_stdout: true
}
double_def: {
options = {
collapse_vars: true,
}
input: {
var a = x, a = a && y;
a();
}
expect: {
var a = x;
(a = a && y)();
}
}
toplevel_single_reference: {
options = {
collapse_vars: true,
}
input: {
var a;
for (var b in x) {
var a = b;
b(a);
}
}
expect: {
var a;
for (var b in x)
b(a = b);
}
}
unused_orig: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
var a = 1;
console.log(function(b) {
var a;
var c = b;
for (var d in c) {
var a = c[0];
return --b + a;
}
try {
} catch (e) {
--b + a;
}
a && a.NaN;
}([2]), a);
}
expect: {
var a = 1;
console.log(function(b) {
var c = b;
for (var d in c) {
var a = c[0];
return --b + a;
}
a && a.NaN;
}([2]), a);
}
expect_stdout: "3 1"
}
issue_315: {
options = {
collapse_vars: true,
evaluate: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
console.log(function(s) {
var w, _i, _len, _ref, _results;
_ref = s.trim().split(" ");
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
w = _ref[_i];
_results.push(w.toLowerCase());
}
return _results;
}("test"));
}
expect: {
console.log(function() {
var w, _i, _len, _ref, _results;
for (_results = [], _i = 0, _len = (_ref = "test".trim().split(" ")).length; _i < _len ; _i++)
w = _ref[_i], _results.push(w.toLowerCase());
return _results;
}());
}
expect_stdout: true
}
lvalues_def: {
options = {
collapse_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = 0, b = 1;
var a = b++, b = +function() {}();
a && a[a++];
console.log(a, b);
}
expect: {
var a = 0, b = 1;
var a = b++, b = +void 0;
a && a[a++];
console.log(a, b);
}
expect_stdout: true
}
compound_assignment: {
options = {
collapse_vars: true,
}
input: {
var a;
a = 1;
a += a + 2;
console.log(a);
}
expect: {
var a;
a = 1;
a += a + 2;
console.log(a);
}
expect_stdout: "4"
}

View File

@@ -979,12 +979,12 @@ delete_conditional_1: {
console.log(delete (1 ? 0 / 0 : x)); console.log(delete (1 ? 0 / 0 : x));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -1006,12 +1006,12 @@ delete_conditional_2: {
console.log(delete (0 ? x : 0 / 0)); console.log(delete (0 ? x : 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((Infinity, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }

166
test/compress/const.js Normal file
View File

@@ -0,0 +1,166 @@
issue_1191: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(rot) {
const rotTol = 5;
if (rot < -rotTol || rot > rotTol)
bar();
baz();
}
}
expect: {
function foo(rot) {
(rot < -5 || rot > 5) && bar();
baz();
}
}
}
issue_1194: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function f1() {const a = "X"; return a + a;}
function f2() {const aa = "X"; return aa + aa;}
function f3() {const aaa = "X"; return aaa + aaa;}
}
expect: {
function f1(){return"XX"}
function f2(){return"XX"}
function f3(){return"XX"}
}
}
issue_1396: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(a) {
const VALUE = 1;
console.log(2 | VALUE);
console.log(VALUE + 1);
console.log(VALUE);
console.log(a & VALUE);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
const CONSTANT = "abc";
console.log(CONSTANT + CONSTANT + CONSTANT + CONSTANT + CONSTANT);
}
}
expect: {
function foo(a) {
console.log(3);
console.log(2);
console.log(1);
console.log(1 & a);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
console.log("abcabcabcabcabc");
}
}
}
unused_regexp_literal: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
}
input: {
function f(){ var a = /b/; }
}
expect: {
function f(){}
}
}
regexp_literal_not_const: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
(function(){
var result;
const s = 'acdabcdeabbb';
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec(s)) {
console.log(result[0]);
}
})();
}
expect: {
(function() {
var result;
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
})();
}
expect_stdout: true
}

View File

@@ -31,7 +31,7 @@ dead_code_2_should_warn: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw new Error("foo"); throw "foo";
// completely discarding the `if` would introduce some // completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2 // bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope. // we copy any declarations to the upper scope.
@@ -46,60 +46,16 @@ dead_code_2_should_warn: {
})(); })();
} }
} }
f();
} }
expect: { expect: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw new Error("foo"); throw "foo";
var x; var x;
function g(){}; function g(){};
} }
f();
} }
expect_stdout: true
node_version: "<=4"
}
dead_code_2_should_warn_strict: {
options = {
dead_code: true
};
input: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
f();
}
expect: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
}
expect_stdout: true
node_version: ">=4"
} }
dead_code_constant_boolean_should_warn_more: { dead_code_constant_boolean_should_warn_more: {
@@ -122,7 +78,6 @@ dead_code_constant_boolean_should_warn_more: {
foo(); foo();
var moo; var moo;
} }
bar();
} }
expect: { expect: {
var foo; var foo;
@@ -131,46 +86,133 @@ dead_code_constant_boolean_should_warn_more: {
// as for the for, it should keep: // as for the for, it should keep:
var x = 10, y; var x = 10, y;
var moo; var moo;
bar();
} }
expect_stdout: true expect_stdout: true
node_version: "<=4"
} }
dead_code_constant_boolean_should_warn_more_strict: { dead_code_const_declaration: {
options = { options = {
dead_code : true, dead_code : true,
loops : true, loops : true,
booleans : true, booleans : true,
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
side_effects : true, reduce_vars : true,
}; };
input: { input: {
"use strict"; var unused;
while (!((foo && bar) || (x + "0"))) { const CONST_FOO = false;
if (CONST_FOO) {
console.log("unreachable"); console.log("unreachable");
var foo; var moo;
function bar() {} function bar() {}
} }
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
bar();
} }
expect: { expect: {
"use strict"; var unused;
var foo; const CONST_FOO = !1;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo; var moo;
bar(); function bar() {}
}
expect_stdout: true
}
dead_code_const_annotation: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused;
/** @const */ var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
var moo;
function bar() {}
}
expect_stdout: true
}
dead_code_const_annotation_regex: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused;
// @constraint this shouldn't be a constant
var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("reachable");
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
CONST_FOO_ANN && console.log('reachable');
}
expect_stdout: true
}
dead_code_const_annotation_complex_scope: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused_var;
/** @const */ var test = 'test';
// @const
var CONST_FOO_ANN = false;
var unused_var_2;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
if (test === 'test') {
var beef = 'good';
/** @const */ var meat = 'beef';
var pork = 'bad';
if (meat === 'pork') {
console.log('also unreachable');
} else if (pork === 'good') {
console.log('reached, not const');
}
}
}
expect: {
var unused_var;
var test = 'test';
var CONST_FOO_ANN = !1;
var unused_var_2;
var moo;
function bar() {}
var beef = 'good';
var meat = 'beef';
var pork = 'bad';
} }
expect_stdout: true expect_stdout: true
node_version: ">=4"
} }
try_catch_finally: { try_catch_finally: {
@@ -214,19 +256,3 @@ try_catch_finally: {
"1", "1",
] ]
} }
accessor: {
options = {
side_effects: true,
}
input: {
({
get a() {},
set a(v){
this.b = 2;
},
b: 1
});
}
expect: {}
}

View File

@@ -649,6 +649,37 @@ drop_value: {
} }
} }
const_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
const b = 2;
return 1 + b;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
expect: {
function f() {
return 3;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
}
issue_1539: { issue_1539: {
options = { options = {
cascade: true, cascade: true,
@@ -785,6 +816,10 @@ issue_1709: {
var x = 1; var x = 1;
return x; return x;
}(), }(),
function y() {
const y = 2;
return y;
}(),
function z() { function z() {
function z() {} function z() {}
return z; return z;
@@ -797,6 +832,10 @@ issue_1709: {
var x = 1; var x = 1;
return x; return x;
}(), }(),
function() {
const y = 2;
return y;
}(),
function() { function() {
function z() {} function z() {}
return z; return z;
@@ -896,8 +935,7 @@ issue_1715_3: {
try { try {
console; console;
} catch (a) { } catch (a) {
var a; var a = x();
x();
} }
} }
f(); f();
@@ -992,33 +1030,6 @@ delete_assign_2: {
expect_stdout: true expect_stdout: true
} }
drop_var: {
options = {
toplevel: true,
unused: true,
}
input: {
var a;
console.log(a, b);
var a = 1, b = 2;
console.log(a, b);
var a = 3;
console.log(a, b);
}
expect: {
console.log(a, b);
var a = 1, b = 2;
console.log(a, b);
a = 3;
console.log(a, b);
}
expect_stdout: [
"undefined undefined",
"1 2",
"3 2",
]
}
issue_1830_1: { issue_1830_1: {
options = { options = {
unused: true, unused: true,
@@ -1053,58 +1064,3 @@ issue_1830_2: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_1838: {
options = {
join_vars: true,
loops: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
function f() {
var b = a;
while (c);
}
}
expect_exact: [
"function f() {",
" for (a; c; ) ;",
"}",
]
}
var_catch_toplevel: {
options = {
conditionals: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {
a--;
try {
a++;
} catch(a) {
if (a) var a;
var a = 10;
}
}
f();
}
expect: {
!function() {
a--;
try {
a++;
} catch(a) {
var a;
}
}();
}
}

View File

@@ -645,17 +645,16 @@ call_args: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
toplevel: true,
} }
input: { input: {
var a = 1; const a = 1;
console.log(a); console.log(a);
+function(a) { +function(a) {
return a; return a;
}(a); }(a);
} }
expect: { expect: {
var a = 1; const a = 1;
console.log(1); console.log(1);
+(1, 1); +(1, 1);
} }
@@ -667,17 +666,17 @@ call_args_drop_param: {
evaluate: true, evaluate: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
var a = 1; const a = 1;
console.log(a); console.log(a);
+function(a) { +function(a) {
return a; return a;
}(a, b); }(a, b);
} }
expect: { expect: {
const a = 1;
console.log(1); console.log(1);
+(b, 1); +(b, 1);
} }
@@ -923,12 +922,12 @@ delete_binary_1: {
console.log(delete (true && (0 / 0))); console.log(delete (true && (0 / 0)));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -949,12 +948,12 @@ delete_binary_2: {
console.log(delete (false || (0 / 0))); console.log(delete (false || (0 / 0)));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((Infinity, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -990,50 +989,3 @@ Infinity_NaN_undefined_LHS: {
"}", "}",
] ]
} }
issue_1964_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "b"
}

View File

@@ -112,8 +112,9 @@ issue_1841_1: {
expect: { expect: {
var b = 10; var b = 10;
!function() { !function() {
for (var key in "hi") for (var key in "hi") {
b = 42; b = 42;
}
}(--b); }(--b);
console.log(b); console.log(b);
} }
@@ -138,110 +139,11 @@ issue_1841_2: {
expect: { expect: {
var b = 10; var b = 10;
!function(arg) { !function(arg) {
for (var key in "hi") for (var key in "hi") {
arg.baz, b = 42; arg.baz, b = 42;
}
}(--b); }(--b);
console.log(b); console.log(b);
} }
expect_exact: "42" expect_exact: "42"
} }
function_returning_constant_literal: {
options = {
reduce_vars: true,
unsafe: true,
toplevel: true,
evaluate: true,
cascade: true,
unused: true,
}
input: {
function greeter() {
return { message: 'Hello there' };
}
var greeting = greeter();
console.log(greeting.message);
}
expect: {
console.log("Hello there");
}
expect_stdout: "Hello there"
}
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
function f() {}
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'function' 'function'",
"2 'function' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'function' 'function'",
]
node_version: "<=4"
}
hoist_funs_strict: {
options = {
hoist_funs: true,
}
input: {
"use strict";
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
"use strict";
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'undefined' 'function'",
]
node_version: ">=4"
}

View File

@@ -120,7 +120,7 @@ mixed: {
properties: true, properties: true,
} }
input: { input: {
var FOO = { BAR: 0 }; const FOO = { BAR: 0 };
console.log(FOO.BAR); console.log(FOO.BAR);
console.log(++CONFIG.DEBUG); console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE); console.log(++CONFIG.VALUE);
@@ -130,7 +130,7 @@ mixed: {
console.log(CONFIG); console.log(CONFIG);
} }
expect: { expect: {
var FOO = { BAR: 0 }; const FOO = { BAR: 0 };
console.log("moo"); console.log("moo");
console.log(++CONFIG.DEBUG); console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE); console.log(++CONFIG.VALUE);
@@ -160,17 +160,3 @@ issue_1801: {
console.log(!0); console.log(!0);
} }
} }
issue_1986: {
options = {
global_defs: {
"@alert": "console.log",
},
}
input: {
alert(42);
}
expect: {
console.log(42);
}
}

View File

@@ -302,27 +302,3 @@ issue_1437_conditionals: {
} }
} }
} }
issue_512: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
}
input: {
function a() {
if (b()) {
c();
return;
}
throw e;
}
}
expect: {
function a() {
if (!b()) throw e;
c();
}
}
}

View File

@@ -116,137 +116,3 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
] ]
} }
non_hoisted_function_after_return_strict: {
options = {
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
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
console.log(foo(0), foo(1));
}
expect_stdout: "8 7"
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]"
]
}
non_hoisted_function_after_return_2a_strict: {
options = {
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, warnings: "verbose"
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) {
return 7 - x;
}
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]",
]
}
non_hoisted_function_after_return_2b_strict: {
options = {
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
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
]
}

View File

@@ -1,3 +1,16 @@
const_declaration: {
options = {
evaluate: true
};
input: {
const goog = goog || {};
}
expect: {
const goog = goog || {};
}
}
const_pragma: { const_pragma: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -1,91 +1,90 @@
multiple_functions: { multiple_functions: {
options = { options = { if_return: true, hoist_funs: false };
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
function g() {} function g() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
function g() {}
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ); if ( window );
function f() {}
function g() {}
} )(); } )();
} }
} }
single_function: { single_function: {
options = { options = { if_return: true, hoist_funs: false };
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
if ( window );
function f() {} function f() {}
if ( window );
} )(); } )();
} }
} }
deeply_nested: { deeply_nested: {
options = { options = { if_return: true, hoist_funs: false };
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
function g() {} function g() {}
if ( !document ) { if ( !document ) {
return; return;
} }
function h() {} function h() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
function g() {}
function h() {}
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ) if ( window )
if (document); if (document);
function f() {}
function g() {}
function h() {}
} )(); } )();
} }
} }
not_hoisted_when_already_nested: { not_hoisted_when_already_nested: {
options = { options = { if_return: true, hoist_funs: false };
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
if ( foo ) function f() {} if ( foo ) function f() {}
} )(); } )();
} }
expect: { expect: {
@@ -95,70 +94,3 @@ not_hoisted_when_already_nested: {
} )(); } )();
} }
} }
defun_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}
defun_hoist_funs: {
options = {
hoist_funs: true,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
function g() {}
function h() {}
if (window);
}
}
}
defun_else_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (window) function g() {}
else return;
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}

View File

@@ -1,6 +1,6 @@
issue_1321_no_debug: { issue_1321_no_debug: {
mangle_props = { mangle_props = {
keep_quoted: true ignore_quoted: true
} }
input: { input: {
var x = {}; var x = {};
@@ -19,7 +19,7 @@ issue_1321_no_debug: {
issue_1321_debug: { issue_1321_debug: {
mangle_props = { mangle_props = {
keep_quoted: true, ignore_quoted: true,
debug: "" debug: ""
} }
input: { input: {
@@ -39,7 +39,7 @@ issue_1321_debug: {
issue_1321_with_quoted: { issue_1321_with_quoted: {
mangle_props = { mangle_props = {
keep_quoted: false ignore_quoted: false
} }
input: { input: {
var x = {}; var x = {};

View File

@@ -23,7 +23,7 @@ typeof_eq_undefined: {
typeof_eq_undefined_ie8: { typeof_eq_undefined_ie8: {
options = { options = {
comparisons: true, comparisons: true,
ie8: true, screw_ie8: false
} }
input: { input: {
var a = typeof b != "undefined"; var a = typeof b != "undefined";

View File

@@ -1,9 +1,9 @@
screw_ie8: { screw_ie8: {
options = { options = {
ie8: false, screw_ie8: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
try { throw "foo"; } catch (x) { console.log(x); } try { throw "foo"; } catch (x) { console.log(x); }
@@ -16,10 +16,10 @@ screw_ie8: {
support_ie8: { support_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
try { throw "foo"; } catch (x) { console.log(x); } try { throw "foo"; } catch (x) { console.log(x); }
@@ -85,3 +85,15 @@ unsafe_undefined: {
} }
expect_stdout: true expect_stdout: true
} }
runtime_error: {
input: {
const a = 1;
console.log(a++);
}
expect: {
const a = 1;
console.log(a++);
}
expect_stdout: true
}

View File

@@ -18,7 +18,9 @@ chained_evaluation_1: {
expect: { expect: {
(function() { (function() {
(function() { (function() {
f(1).bar = 1; var c;
c = f(1);
c.bar = 1;
})(); })();
})(); })();
} }
@@ -44,8 +46,9 @@ chained_evaluation_2: {
expect: { expect: {
(function() { (function() {
(function() { (function() {
var b = "long piece of string"; var c, b = "long piece of string";
f(b).bar = b; c = f(b);
c.bar = b;
})(); })();
})(); })();
} }

View File

@@ -1,10 +1,10 @@
mangle_catch: { mangle_catch: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -22,11 +22,11 @@ mangle_catch: {
mangle_catch_ie8: { mangle_catch_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -44,11 +44,11 @@ mangle_catch_ie8: {
mangle_catch_var: { mangle_catch_var: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -66,11 +66,11 @@ mangle_catch_var: {
mangle_catch_var_ie8: { mangle_catch_var_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -88,11 +88,11 @@ mangle_catch_var_ie8: {
mangle_catch_toplevel: { mangle_catch_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -110,11 +110,11 @@ mangle_catch_toplevel: {
mangle_catch_ie8_toplevel: { mangle_catch_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -132,11 +132,11 @@ mangle_catch_ie8_toplevel: {
mangle_catch_var_toplevel: { mangle_catch_var_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -154,11 +154,11 @@ mangle_catch_var_toplevel: {
mangle_catch_var_ie8_toplevel: { mangle_catch_var_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -176,11 +176,11 @@ mangle_catch_var_ie8_toplevel: {
mangle_catch_redef_1: { mangle_catch_redef_1: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -198,11 +198,11 @@ mangle_catch_redef_1: {
mangle_catch_redef_1_ie8: { mangle_catch_redef_1_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -220,11 +220,11 @@ mangle_catch_redef_1_ie8: {
mangle_catch_redef_1_toplevel: { mangle_catch_redef_1_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -242,11 +242,11 @@ mangle_catch_redef_1_toplevel: {
mangle_catch_redef_1_ie8_toplevel: { mangle_catch_redef_1_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -264,11 +264,11 @@ mangle_catch_redef_1_ie8_toplevel: {
mangle_catch_redef_2: { mangle_catch_redef_2: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -285,11 +285,11 @@ mangle_catch_redef_2: {
mangle_catch_redef_2_ie8: { mangle_catch_redef_2_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -306,11 +306,11 @@ mangle_catch_redef_2_ie8: {
mangle_catch_redef_2_toplevel: { mangle_catch_redef_2_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -327,11 +327,11 @@ mangle_catch_redef_2_toplevel: {
mangle_catch_redef_2_ie8_toplevel: { mangle_catch_redef_2_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {

View File

@@ -1,6 +1,6 @@
function_iife_catch: { function_iife_catch: {
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
function f(n) { function f(n) {
@@ -21,7 +21,7 @@ function_iife_catch: {
function_iife_catch_ie8: { function_iife_catch_ie8: {
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
function f(n) { function f(n) {
@@ -42,7 +42,7 @@ function_iife_catch_ie8: {
function_catch_catch: { function_catch_catch: {
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
var o = 0; var o = 0;
@@ -70,7 +70,7 @@ function_catch_catch: {
function_catch_catch_ie8: { function_catch_catch_ie8: {
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
var o = 0; var o = 0;

View File

@@ -10,6 +10,10 @@ unary_prefix: {
return x; return x;
}()); }());
} }
expect_exact: "console.log(-2/3);" expect: {
console.log(function() {
return -2 / 3;
}());
}
expect_stdout: true expect_stdout: true
} }

View File

@@ -1,31 +0,0 @@
operator: {
input: {
a. //comment
typeof
}
expect_exact: "a.typeof;"
}
name: {
input: {
a. //comment
b
}
expect_exact: "a.b;"
}
keyword: {
input: {
a. //comment
default
}
expect_exact: "a.default;"
}
atom: {
input: {
a. //comment
true
}
expect_exact: "a.true;"
}

View File

@@ -38,7 +38,7 @@ mixed: {
} }
} }
input: { input: {
var ENV = 3; const ENV = 3;
var FOO = 4; var FOO = 4;
f(ENV * 10); f(ENV * 10);
--FOO; --FOO;
@@ -49,7 +49,7 @@ mixed: {
x = DEBUG; x = DEBUG;
} }
expect: { expect: {
var ENV = 3; const ENV = 3;
var FOO = 4; var FOO = 4;
f(10); f(10);
--FOO; --FOO;
@@ -60,7 +60,7 @@ mixed: {
x = 0; x = 0;
} }
expect_warnings: [ expect_warnings: [
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,12]', 'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]', 'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]', 'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]', 'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',

View File

@@ -159,7 +159,7 @@ negate_iife_4: {
})(); })();
} }
expect: { expect: {
!function(){ return t }() ? console.log(false) : console.log(true), function(){ (function(){ return t })() ? console.log(true) : console.log(false), function(){
console.log("something"); console.log("something");
}(); }();
} }
@@ -183,7 +183,7 @@ negate_iife_5: {
})(); })();
} }
expect: { expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){ (function(){ return t })() ? foo(true) : bar(false), function(){
console.log("something"); console.log("something");
}(); }();
} }
@@ -207,7 +207,7 @@ negate_iife_5_off: {
})(); })();
} }
expect: { expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){ (function(){ return t })() ? foo(true) : bar(false), function(){
console.log("something"); console.log("something");
}(); }();
} }

View File

@@ -146,6 +146,50 @@ parse_do_while_without_semicolon: {
} }
} }
keep_collapse_const_in_own_block_scope: {
options = {
join_vars: true,
loops: true
}
input: {
var i=2;
const c=5;
while(i--)
console.log(i);
console.log(c);
}
expect: {
var i=2;
const c=5;
for(;i--;)
console.log(i);
console.log(c);
}
expect_stdout: true
}
keep_collapse_const_in_own_block_scope_2: {
options = {
join_vars: true,
loops: true
}
input: {
const c=5;
var i=2; // Moves to loop, while it did not in previous test
while(i--)
console.log(i);
console.log(c);
}
expect: {
const c=5;
for(var i=2;i--;)
console.log(i);
console.log(c);
}
expect_stdout: true
}
evaluate: { evaluate: {
options = { options = {
loops: true, loops: true,
@@ -201,7 +245,7 @@ issue_1532: {
issue_186: { issue_186: {
beautify = { beautify = {
beautify: false, beautify: false,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -220,7 +264,7 @@ issue_186: {
issue_186_ie8: { issue_186_ie8: {
beautify = { beautify = {
beautify: false, beautify: false,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;
@@ -239,7 +283,7 @@ issue_186_ie8: {
issue_186_beautify: { issue_186_beautify: {
beautify = { beautify = {
beautify: true, beautify: true,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -266,7 +310,7 @@ issue_186_beautify: {
issue_186_beautify_ie8: { issue_186_beautify_ie8: {
beautify = { beautify = {
beautify: true, beautify: true,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;
@@ -296,7 +340,7 @@ issue_186_bracketize: {
beautify = { beautify = {
beautify: false, beautify: false,
bracketize: true, bracketize: true,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -316,7 +360,7 @@ issue_186_bracketize_ie8: {
beautify = { beautify = {
beautify: false, beautify: false,
bracketize: true, bracketize: true,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;
@@ -336,7 +380,7 @@ issue_186_beautify_bracketize: {
beautify = { beautify = {
beautify: true, beautify: true,
bracketize: true, bracketize: true,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -368,7 +412,7 @@ issue_186_beautify_bracketize_ie8: {
beautify = { beautify = {
beautify: true, beautify: true,
bracketize: true, bracketize: true,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;

View File

@@ -25,9 +25,11 @@ negate_iife_2: {
negate_iife: true negate_iife: true
}; };
input: { input: {
(function(){ return {} })().x = 10; // should not transform this one
}
expect: {
(function(){ return {} })().x = 10; (function(){ return {} })().x = 10;
} }
expect_exact: "({}).x=10;"
} }
negate_iife_2_side_effects: { negate_iife_2_side_effects: {
@@ -36,9 +38,11 @@ negate_iife_2_side_effects: {
side_effects: true, side_effects: true,
} }
input: { input: {
(function(){ return {} })().x = 10; // should not transform this one
}
expect: {
(function(){ return {} })().x = 10; (function(){ return {} })().x = 10;
} }
expect_exact: "({}).x=10;"
} }
negate_iife_3: { negate_iife_3: {

View File

@@ -1,12 +0,0 @@
eval_let: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: ""
node_version: ">=6"
}

View File

@@ -13,7 +13,7 @@ keep_properties: {
dot_properties: { dot_properties: {
options = { options = {
properties: true, properties: true,
ie8: true, screw_ie8: false
}; };
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
@@ -36,7 +36,7 @@ dot_properties: {
dot_properties_es5: { dot_properties_es5: {
options = { options = {
properties: true, properties: true,
ie8: false, screw_ie8: true
}; };
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
@@ -125,7 +125,7 @@ evaluate_string_length: {
mangle_properties: { mangle_properties: {
mangle_props = { mangle_props = {
keep_quoted: false ignore_quoted: false
}; };
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
@@ -148,7 +148,7 @@ mangle_unquoted_properties: {
properties: false properties: false
} }
mangle_props = { mangle_props = {
keep_quoted: true ignore_quoted: true
} }
beautify = { beautify = {
beautify: false, beautify: false,
@@ -233,12 +233,12 @@ mangle_debug_suffix: {
} }
} }
mangle_debug_suffix_keep_quoted: { mangle_debug_suffix_ignore_quoted: {
options = { options = {
properties: false properties: false
} }
mangle_props = { mangle_props = {
keep_quoted: true, ignore_quoted: true,
debug: "XYZ", debug: "XYZ",
reserved: [] reserved: []
} }

View File

@@ -119,62 +119,3 @@ chained: {
a.b.c; a.b.c;
} }
} }
impure_getter_1: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect: {
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect_stdout: "1"
}
impure_getter_2: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
// will produce incorrect output because getter is not pure
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect: {}
}

View File

@@ -41,20 +41,22 @@ reduce_vars: {
var A = 1; var A = 1;
(function() { (function() {
console.log(-3); console.log(-3);
console.log(A - 5); console.log(-4);
})(); })();
(function f1() { (function f1() {
var a = 2; var a = 2;
console.log(a - 5); console.log(-3);
eval("console.log(a);"); eval("console.log(a);");
})(); })();
(function f2(eval) { (function f2(eval) {
var a = 2; var a = 2;
console.log(a - 5); console.log(-3);
eval("console.log(a);"); eval("console.log(a);");
})(eval); })(eval);
"yes"; (function() {
console.log(A + 1); return "yes";
})();
console.log(2);
} }
expect_stdout: true expect_stdout: true
} }
@@ -64,7 +66,7 @@ modified: {
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
reduce_vars : true, reduce_vars : true,
unused : true, unused : true
} }
input: { input: {
function f0() { function f0() {
@@ -134,11 +136,12 @@ modified: {
} }
function f2() { function f2() {
3; var b = 2;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4); console.log(4);
console.log(6); console.log(1 + b + 3);
console.log(4);
console.log(7);
} }
function f3() { function f3() {
@@ -387,11 +390,12 @@ passes: {
} }
expect: { expect: {
function f() { function f() {
3; var b = 2;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4); console.log(4);
console.log(6); console.log(1 + b + 3);
console.log(4);
console.log(7);
} }
} }
} }
@@ -584,7 +588,7 @@ inner_var_label: {
} }
} }
inner_var_for_1: { inner_var_for: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
@@ -613,29 +617,6 @@ inner_var_for_1: {
} }
} }
inner_var_for_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = 1;
for (var b = 1; --b;) var a = 2;
console.log(a);
}();
}
expect: {
!function() {
a = 1;
for (var b = 1; --b;) var a = 2;
console.log(a);
}();
}
expect_stdout: "1"
}
inner_var_for_in_1: { inner_var_for_in_1: {
options = { options = {
evaluate: true, evaluate: true,
@@ -1673,7 +1654,7 @@ redefine_arguments_1: {
return typeof arguments; return typeof arguments;
} }
function g() { function g() {
return "number"; return"number";
} }
function h(x) { function h(x) {
var arguments = x; var arguments = x;
@@ -1712,7 +1693,9 @@ redefine_arguments_2: {
console.log(function() { console.log(function() {
var arguments; var arguments;
return typeof arguments; return typeof arguments;
}(), "number", function(x) { }(), function() {
return"number";
}(), function(x) {
var arguments = x; var arguments = x;
return typeof arguments; return typeof arguments;
}()); }());
@@ -1749,10 +1732,7 @@ redefine_arguments_3: {
console.log(function() { console.log(function() {
var arguments; var arguments;
return typeof arguments; return typeof arguments;
}(), "number", function(x) { }(), "number", "undefined");
var arguments = x;
return typeof arguments;
}());
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -1824,7 +1804,9 @@ redefine_farg_2: {
console.log(function(a) { console.log(function(a) {
var a; var a;
return typeof a; return typeof a;
}([]), "number",function(a, b) { }([]), function() {
return "number";
}(),function(a, b) {
var a = b; var a = b;
return typeof a; return typeof a;
}([])); }([]));
@@ -1861,7 +1843,10 @@ redefine_farg_3: {
console.log(function(a) { console.log(function(a) {
var a; var a;
return typeof a; return typeof a;
}([]), "number", "undefined"); }([]), "number", function(a) {
var a = void 0;
return typeof a;
}([]));
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -1981,6 +1966,7 @@ pure_getters_2: {
var a = a && a.b; var a = a && a.b;
} }
expect: { expect: {
var a;
var a = a && a.b; var a = a && a.b;
} }
} }
@@ -2025,176 +2011,14 @@ catch_var: {
expect_stdout: "true" expect_stdout: "true"
} }
var_assign_1: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
a = 2;
console.log(a);
}();
}
expect: {
!function() {
console.log(2);
}();
}
expect_stdout: "2"
}
var_assign_2: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
if (a = 2) console.log(a);
}();
}
expect: {
!function() {
if (2) console.log(2);
}();
}
expect_stdout: "2"
}
var_assign_3: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
while (a = 2);
console.log(a);
}();
}
expect: {
!function() {
var a;
while (a = 2);
console.log(a);
}();
}
}
var_assign_4: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function a() {
a = 2;
console.log(a);
}();
}
expect: {
!function a() {
a = 2,
console.log(a);
}();
}
}
var_assign_5: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
!function(b) {
a = 2;
console.log(a, b);
}(a);
}();
}
expect: {
!function() {
var a;
!function(b) {
a = 2,
console.log(a, b);
}(a);
}();
}
expect_stdout: "2 undefined"
}
var_assign_6: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = function(){}(a = 1);
console.log(a);
}();
}
expect: {
!function() {
var a = function(){}(a = 1);
console.log(a);
}();
}
expect_stdout: "undefined"
}
immutable: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = "test";
console.log(a.indexOf("e"));
}();
}
expect: {
!function() {
console.log("test".indexOf("e"));
}();
}
expect_stdout: "1"
}
issue_1814_1: { issue_1814_1: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
var a = 42; const a = 42;
!function() { !function() {
var b = a; var b = a;
!function(a) { !function(a) {
@@ -2203,6 +2027,7 @@ issue_1814_1: {
}(); }();
} }
expect: { expect: {
const a = 42;
!function() { !function() {
!function(a) { !function(a) {
console.log(a++, 42); console.log(a++, 42);
@@ -2216,11 +2041,10 @@ issue_1814_2: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
var a = "32"; const a = "32";
!function() { !function() {
var b = a + 1; var b = a + 1;
!function(a) { !function(a) {
@@ -2229,6 +2053,7 @@ issue_1814_2: {
}(); }();
} }
expect: { expect: {
const a = "32";
!function() { !function() {
!function(a) { !function(a) {
console.log(a++, "321"); console.log(a++, "321");
@@ -2269,173 +2094,6 @@ try_abort: {
expect_stdout: "1 undefined" expect_stdout: "1 undefined"
} }
boolean_binary_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a;
void 0 && (a = 1);
console.log(a);
}();
}
expect: {
!function() {
var a;
void 0;
console.log(a);
}();
}
expect_stdout: "undefined"
}
cond_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a;
void 0 ? (a = 1) : 0;
console.log(a);
}();
}
expect: {
!function() {
var a;
void 0 ? (a = 1) : 0;
console.log(a);
}();
}
expect_stdout: "undefined"
}
iife_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = 1, b = 0;
!function() {
b++;
return;
a = 2;
}();
console.log(a);
}();
}
expect: {
!function() {
var a = 1, b = 0;
!function() {
b++;
return;
a = 2;
}();
console.log(a);
}();
}
expect_stdout: "1"
}
issue_1850_1: {
options = {
reduce_vars: true,
toplevel: false,
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect_stdout: true
}
issue_1850_2: {
options = {
reduce_vars: true,
toplevel: "funcs",
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
var a = 1;
(function() {
console.log(a, a, a);
})();
}
expect_stdout: true
}
issue_1850_3: {
options = {
reduce_vars: true,
toplevel: "vars",
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect_stdout: true
}
issue_1850_4: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
var a = 1;
(function() {
console.log(a, a, a);
})();
}
expect_stdout: true
}
issue_1865: { issue_1865: {
options = { options = {
evaluate: true, evaluate: true,
@@ -2464,76 +2122,3 @@ issue_1865: {
} }
expect_stdout: true expect_stdout: true
} }
issue_1922_1: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
arguments[0] = 2;
return a;
}(1));
}
expect: {
console.log(function(a) {
arguments[0] = 2;
return a;
}(1));
}
expect_stdout: "2"
}
issue_1922_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
var a;
eval("a = 1");
return a;
}(1));
}
expect: {
console.log(function() {
var a;
eval("a = 1");
return a;
}(1));
}
expect_stdout: "1"
}
accessor: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
console.log({
get a() {
a = 2;
return a;
},
b: 1
}.b, a);
}
expect: {
var a = 1;
console.log({
get a() {
a = 2;
return a;
},
b: 1
}.b, a);
}
expect_stdout: "1 1"
}

View File

@@ -1,9 +1,9 @@
do_screw: { do_screw: {
options = { options = {
ie8: false, screw_ie8: true,
} }
beautify = { beautify = {
ie8: false, screw_ie8: true,
ascii_only: true, ascii_only: true,
} }
input: { input: {
@@ -14,10 +14,10 @@ do_screw: {
dont_screw: { dont_screw: {
options = { options = {
ie8: true, screw_ie8: false,
} }
beautify = { beautify = {
ie8: true, screw_ie8: false,
ascii_only: true, ascii_only: true,
} }
input: { input: {
@@ -28,7 +28,7 @@ dont_screw: {
do_screw_constants: { do_screw_constants: {
options = { options = {
ie8: false, screw_ie8: true,
} }
input: { input: {
f(undefined, Infinity); f(undefined, Infinity);
@@ -38,7 +38,7 @@ do_screw_constants: {
dont_screw_constants: { dont_screw_constants: {
options = { options = {
ie8: true, screw_ie8: false,
} }
input: { input: {
f(undefined, Infinity); f(undefined, Infinity);
@@ -47,15 +47,9 @@ dont_screw_constants: {
} }
do_screw_try_catch: { do_screw_try_catch: {
options = { options = { screw_ie8: true };
ie8: false, mangle = { screw_ie8: true };
} beautify = { screw_ie8: true };
mangle = {
ie8: false,
}
beautify = {
ie8: false,
}
input: { input: {
good = function(e){ good = function(e){
return function(error){ return function(error){
@@ -81,15 +75,9 @@ do_screw_try_catch: {
} }
dont_screw_try_catch: { dont_screw_try_catch: {
options = { options = { screw_ie8: false };
ie8: true, mangle = { screw_ie8: false };
} beautify = { screw_ie8: false };
mangle = {
ie8: true,
}
beautify = {
ie8: true,
}
input: { input: {
bad = function(e){ bad = function(e){
return function(error){ return function(error){
@@ -115,15 +103,9 @@ dont_screw_try_catch: {
} }
do_screw_try_catch_undefined: { do_screw_try_catch_undefined: {
options = { options = { screw_ie8: true };
ie8: false, mangle = { screw_ie8: true };
} beautify = { screw_ie8: true };
mangle = {
ie8: false,
}
beautify = {
ie8: false,
}
input: { input: {
function a(b){ function a(b){
try { try {
@@ -150,15 +132,9 @@ do_screw_try_catch_undefined: {
} }
dont_screw_try_catch_undefined: { dont_screw_try_catch_undefined: {
options = { options = { screw_ie8: false };
ie8: true, mangle = { screw_ie8: false };
} beautify = { screw_ie8: false };
mangle = {
ie8: true,
}
beautify = {
ie8: true,
}
input: { input: {
function a(b){ function a(b){
try { try {
@@ -188,11 +164,11 @@ reduce_vars: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
ie8: true, screw_ie8: false,
unused: true, unused: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
function f() { function f() {
@@ -220,10 +196,10 @@ reduce_vars: {
issue_1586_1: { issue_1586_1: {
options = { options = {
ie8: true, screw_ie8: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
function f() { function f() {
@@ -239,10 +215,10 @@ issue_1586_1: {
issue_1586_2: { issue_1586_2: {
options = { options = {
ie8: false, screw_ie8: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
function f() { function f() {

View File

@@ -460,7 +460,7 @@ issue_1758: {
console.log(function(c) { console.log(function(c) {
var undefined = 42; var undefined = 42;
return function() { return function() {
return c--, c--, void c.toString(); return c--, c--, c.toString(), void 0;
}(); }();
}()); }());
} }
@@ -481,12 +481,12 @@ delete_seq_1: {
console.log(delete (1, 0 / 0)); console.log(delete (1, 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((0 / 0, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -505,12 +505,12 @@ delete_seq_2: {
console.log(delete (1, 2, 0 / 0)); console.log(delete (1, 2, 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((0 / 0, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -530,12 +530,12 @@ delete_seq_3: {
console.log(delete (1, 2, 0 / 0)); console.log(delete (1, 2, 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((Infinity, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((0 / 0, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -606,107 +606,7 @@ delete_seq_6: {
} }
expect: { expect: {
var a; var a;
console.log(!0); console.log((a, !0));
} }
expect_stdout: true expect_stdout: true
} }
side_effects: {
options = {
sequences: true,
side_effects: true,
}
input: {
0, a(), 1, b(), 2, c(), 3;
}
expect: {
a(), b(), c();
}
}
side_effects_cascade_1: {
options = {
cascade: true,
conditionals: true,
sequences: true,
side_effects: true,
}
input: {
function f(a, b) {
a -= 42;
if (a < 0) a = 0;
b.a = a;
}
}
expect: {
function f(a, b) {
(a -= 42) < 0 && (a = 0), b.a = a;
}
}
}
side_effects_cascade_2: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
b = a,
b;
}
}
expect: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
b = a;
}
}
}
side_effects_cascade_3: {
options = {
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
function f(a, b) {
"foo" ^ (b += a),
b ? false : (b = a) ? -1 : (b -= a) - (b ^= a),
a-- || !a,
a;
}
}
expect: {
function f(a, b) {
!(b += a) && ((b = a) || (b -= a, b ^= a)),
--a;
}
}
}
issue_27: {
options = {
cascade: true,
passes: 2,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(jQuery) {
var $;
$ = jQuery;
$("body").addClass("foo");
})(jQuery);
}
expect: {
(function(jQuery) {
jQuery("body").addClass("foo");
})(jQuery);
}
}

View File

@@ -8,12 +8,3 @@ octal_escape_sequence: {
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30"; var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
} }
} }
issue_1929: {
input: {
function f(s) {
return s.split(/[\\/]/);
}
}
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
}

View File

@@ -1,13 +0,0 @@
exports["Compressor"] = Compressor;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
exports["parse"] = parse;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;

View File

@@ -1 +0,0 @@
++null

View File

@@ -1,14 +0,0 @@
function f(x) {
delete 42;
delete (0, x);
delete null;
delete x;
}
function g(x) {
"use strict";
delete 42;
delete (0, x);
delete null;
delete x;
}

View File

@@ -1 +0,0 @@
a.=

View File

@@ -1 +0,0 @@
%.a;

View File

@@ -1 +0,0 @@
a./();

View File

@@ -1 +0,0 @@
if (0) else 1;

View File

@@ -1,6 +0,0 @@
function f(arguments) {
}
function g(arguments) {
"use strict";
}

View File

@@ -1,6 +0,0 @@
function arguments() {
}
function eval() {
"use strict";
}

View File

@@ -1,6 +0,0 @@
!function eval() {
}();
!function arguments() {
"use strict";
}();

View File

@@ -1 +0,0 @@
console.log({%: 1});

View File

@@ -1 +0,0 @@
return 42;

View File

@@ -1,8 +0,0 @@
function f() {
try {} catch (eval) {}
}
function g() {
"use strict";
try {} catch (eval) {}
}

View File

@@ -1,8 +0,0 @@
function f() {
var eval;
}
function g() {
"use strict";
var eval;
}

View File

@@ -1,5 +0,0 @@
function test(callback) {
'aaaaaaaaaaaaaaaa';
callback(err, data);
callback(err, data);
}

View File

@@ -1,5 +0,0 @@
function test(a){
"aaaaaaaaaaaaaaaa"
;a(err,data),a(err,data)
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9

View File

@@ -1,2 +1,2 @@
new function(){console.log(3)}; new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxHQUFyQyxZQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19

View File

@@ -12,9 +12,9 @@ if (typeof phantom == "undefined") {
}); });
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc", "warnings=false");
} }
args.push("--timings"); args.push("--stats");
var child_process = require("child_process"); var child_process = require("child_process");
try { try {
require("phantomjs-prebuilt"); require("phantomjs-prebuilt");

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("Accessor tokens", function() { describe("Accessor tokens", function() {
@@ -16,7 +16,7 @@ describe("Accessor tokens", function() {
assert.equal(node.start.pos, 12); assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46); assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolAccessor); assert(node.key instanceof UglifyJS.AST_SymbolRef);
assert.equal(node.key.start.pos, 16); assert.equal(node.key.start.pos, 16);
assert.equal(node.key.end.endpos, 22); assert.equal(node.key.end.endpos, 22);

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("arguments", function() { describe("arguments", function() {

View File

@@ -2,10 +2,6 @@ var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
function read(path) {
return readFileSync(path, "utf8");
}
describe("bin/uglifyjs", function () { describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) { it("should produce a functional build when using --self", function (done) {
@@ -19,14 +15,12 @@ describe("bin/uglifyjs", function () {
eval(stdout); eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object'); assert.strictEqual(typeof WrappedUglifyJS, 'object');
var result = WrappedUglifyJS.minify("foo([true,,2+3]);"); assert.strictEqual(true, WrappedUglifyJS.parse('foo;') instanceof WrappedUglifyJS.AST_Node);
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done(); done();
}); });
}); });
it("Should be able to filter comments correctly with `--comments all`", function (done) { it("Should be able to filter comments correctly with `--comment all`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all'; var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
@@ -56,18 +50,18 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should append source map to output when using --source-map url=inline", function (done) { it("Should append source map to output when using --source-map-inline", function (done) {
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline"; var command = uglifyjscmd + ' test/input/issue-1323/sample.js --source-map-inline';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n"); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
done(); done();
}); });
}); });
it("should not append source map to output when not using --source-map url=inline", function (done) { it("should not append source map to output when not using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js'; var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
@@ -143,7 +137,7 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js")); assert.strictEqual(stdout, readFileSync("test/input/issue-1482/default.js", "utf8"));
done(); done();
}); });
}); });
@@ -153,59 +147,55 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js")); assert.strictEqual(stdout, readFileSync("test/input/issue-1482/bracketize.js", "utf8"));
done(); done();
}); });
}); });
it("Should process inline source map", function(done) { it("Should process inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js -mc toplevel --source-map content=inline,url=inline"; var command = uglifyjscmd + ' test/input/issue-520/input.js -mc toplevel --in-source-map inline --source-map-inline';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-520/output.js")); assert.strictEqual(stdout, readFileSync("test/input/issue-520/output.js", "utf8"));
done(); done();
}); });
}); });
it("Should warn for missing inline source map", function(done) { it("Should warn for missing inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline"; var command = uglifyjscmd + ' test/input/issue-1323/sample.js --in-source-map inline';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, [ assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
"var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n"); assert.strictEqual(stderr, "WARN: inline source map not found\n");
done(); done();
}); });
}); });
it("Should fail with multiple input and inline source map", function(done) { it("Should fail with multiple input and inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js test/input/issue-520/output.js --source-map content=inline,url=inline"; var command = uglifyjscmd + ' test/input/issue-520/input.js test/input/issue-520/output.js --in-source-map inline --source-map-inline';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr.split(/\n/)[0], "ERROR: inline source map only works with singular input"); assert.strictEqual(stderr, "ERROR: Inline source map only works with singular input\n");
done(); done();
}); });
}); });
it("Should fail with acorn and inline source map", function(done) { it("Should fail with acorn and inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p acorn"; var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --acorn';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done(); done();
}); });
}); });
it("Should fail with SpiderMonkey and inline source map", function(done) { it("Should fail with SpiderMonkey and inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p spidermonkey"; var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --spidermonkey';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done(); done();
}); });
}); });
@@ -218,7 +208,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12");
assert.strictEqual(lines[1], "function f(a{}"); assert.strictEqual(lines[1], "function f(a{}");
assert.strictEqual(lines[2], " ^"); assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: Unexpected token punc «{», expected punc «,»"); assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»");
done(); done();
}); });
}); });
@@ -231,7 +221,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12");
assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);"); assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);");
assert.strictEqual(lines[2], "\t\t \t ^"); assert.strictEqual(lines[2], "\t\t \t ^");
assert.strictEqual(lines[3], "ERROR: Invalid syntax: 0abc"); assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc");
done(); done();
}); });
}); });
@@ -244,7 +234,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0");
assert.strictEqual(lines[1], "foo, bar("); assert.strictEqual(lines[1], "foo, bar(");
assert.strictEqual(lines[2], " ^"); assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)"); assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done(); done();
}); });
}); });
@@ -257,7 +247,17 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0"); 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[1], "for (var i = 0; i < 1; i++) ");
assert.strictEqual(lines[2], " ^"); assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)"); assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
it("Should support hyphen as shorthand", function(done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep-fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done(); done();
}); });
}); });
@@ -271,7 +271,7 @@ describe("bin/uglifyjs", function () {
"Parse error at test/input/invalid/assign_1.js:1,18", "Parse error at test/input/invalid/assign_1.js:1,18",
"console.log(1 || 5--);", "console.log(1 || 5--);",
" ^", " ^",
"ERROR: Invalid use of -- operator" "SyntaxError: Invalid use of -- operator"
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -286,7 +286,7 @@ describe("bin/uglifyjs", function () {
"Parse error at test/input/invalid/assign_2.js:1,32", "Parse error at test/input/invalid/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));", "console.log(2 || (Math.random() /= 2));",
" ^", " ^",
"ERROR: Invalid assignment" "SyntaxError: Invalid assignment"
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -298,252 +298,12 @@ describe("bin/uglifyjs", function () {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_3.js:1,17", "Parse error at test/input/invalid/assign_3.js:1,18",
"console.log(3 || ++this);", "console.log(3 || ++this);",
" ^", " ^",
"ERROR: Invalid use of ++ operator" "SyntaxError: Invalid use of ++ operator"
].join("\n")); ].join("\n"));
done(); done();
}); });
}); });
it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_4.js:1,0",
"++null",
"^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_1.js:1,2",
"a.=",
" ^",
"ERROR: Unexpected token: operator (=)"
].join("\n"));
done();
});
});
it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_2.js:1,0",
"%.a;",
"^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_3.js:1,2",
"a./();",
" ^",
"ERROR: Unexpected token: operator (/)"
].join("\n"));
done();
});
});
it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/object.js:1,13",
"console.log({%: 1});",
" ^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/delete.js:13,11",
" delete x;",
" ^",
"ERROR: Calling delete on expression not allowed in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (function g(arguments))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/function_1.js:4,11",
"function g(arguments) {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (function eval())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/function_2.js:4,9",
"function eval() {",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (iife arguments())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/function_3.js:4,10",
"!function arguments() {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (catch(eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/try.js:7,18",
" try {} catch (eval) {}",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (var eval)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/var.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/var.js:7,8",
" var eval;",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/else.js:1,7",
"if (0) else 1;",
" ^",
"ERROR: Unexpected token: keyword (else)"
].join("\n"));
done();
});
});
it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/return.js:1,0",
"return 42;",
"^",
"ERROR: 'return' outside of function"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
uglifyjscmd,
"test/input/issue-1236/simple.js",
"--source-map",
'content="' + read_map() + '",url=inline'
].join(" ");
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, [
'"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiYUFBQSxJQUFJQSxJQUFNLFNBQU5BLElBQU1DLEdBQUEsTUFBSyxPQUFTQSxHQUN4QkMsUUFBUUMsSUFBSUgsSUFBSSJ9",
""
].join("\n"));
done();
});
function read_map() {
var map = JSON.parse(read("./test/input/issue-1236/simple.js.map"));
delete map.sourcesContent;
return JSON.stringify(map).replace(/"/g, '\\"');
}
});
it("Should dump AST as JSON", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
exec(command, function (err, stdout) {
if (err) throw err;
var ast = JSON.parse(stdout);
assert.strictEqual(ast._class, "AST_Toplevel");
assert.ok(Array.isArray(ast.body));
done();
});
});
it("Should print supported options on invalid option syntax", function(done) {
var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
}); });

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("comment filters", function() { describe("comment filters", function() {
@@ -75,6 +75,7 @@ describe("comment filters", function() {
it("Should handle shebang and preamble correctly", function() { it("Should handle shebang and preamble correctly", function() {
var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", { var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", {
fromString: true,
output: { preamble: "/* Build */" } output: { preamble: "/* Build */" }
}).code; }).code;
assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;"); assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;");
@@ -82,6 +83,7 @@ describe("comment filters", function() {
it("Should handle preamble without shebang correctly", function() { it("Should handle preamble without shebang correctly", function() {
var code = UglifyJS.minify("var x = 10;", { var code = UglifyJS.minify("var x = 10;", {
fromString: true,
output: { preamble: "/* Build */" } output: { preamble: "/* Build */" }
}).code; }).code;
assert.strictEqual(code, "/* Build */\nvar x=10;"); assert.strictEqual(code, "/* Build */\nvar x=10;");

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("Comment", function() { describe("Comment", function() {
it("Should recognize eol of single line comments", function() { it("Should recognize eol of single line comments", function() {
@@ -20,7 +20,7 @@ describe("Comment", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.throws(function() { assert.throws(function() {
uglify.parse(tests[i]); uglify.parse(tests[i], {fromString: true})
}, fail, tests[i]); }, fail, tests[i]);
} }
}); });
@@ -43,7 +43,7 @@ describe("Comment", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.throws(function() { assert.throws(function() {
uglify.parse(tests[i]); uglify.parse(tests[i], {fromString: true})
}, fail, tests[i]); }, fail, tests[i]);
} }
}); });

View File

@@ -6,7 +6,9 @@ describe("comment before constant", function() {
it("Should test comment before constant is retained and output after mangle.", function() { it("Should test comment before constant is retained and output after mangle.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true,
compress: { collapse_vars: false, reduce_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {},
output: { comments: true }, output: { comments: true },
}); });
assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}'); assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}');
@@ -14,9 +16,12 @@ describe("comment before constant", function() {
it("Should test code works when comments disabled.", function() { it("Should test code works when comments disabled.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true,
compress: { collapse_vars: false, reduce_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {},
output: { comments: false }, output: { comments: false },
}); });
assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); assert.strictEqual(result.code, 'function f(){var n=!1;return n}');
}); });
}); });

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("Directives", function() { describe("Directives", function() {
it ("Should allow tokenizer to store directives state", function() { it ("Should allow tokenizer to store directives state", function() {
@@ -197,7 +197,7 @@ describe("Directives", function() {
assert.strictEqual( assert.strictEqual(
uglify.minify( uglify.minify(
'"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');', '"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');',
{output: {beautify: true, quote_style: 3}, compress: false} {fromString: true, output: {beautify: true, quote_style: 3}, compress: false}
).code, ).code,
'"use strict";\n\n\'use strict\';\n\n"use strict";\n\n"use strict";\n\n;\'use strict\';\n\nconsole.log(\'use strict\');' '"use strict";\n\n\'use strict\';\n\n"use strict";\n\n"use strict";\n\n;\'use strict\';\n\nconsole.log(\'use strict\');'
); );
@@ -225,7 +225,7 @@ describe("Directives", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0], {compress: false, mangle: false}).code, uglify.minify(tests[i][0], {fromString: true, quote_style: 3, compress: false, mangle: false}).code,
tests[i][1], tests[i][1],
tests[i][0] tests[i][0]
); );
@@ -234,7 +234,7 @@ describe("Directives", function() {
it("Should add double semicolon when relying on automatic semicolon insertion", function() { it("Should add double semicolon when relying on automatic semicolon insertion", function() {
var code = uglify.minify('"use strict";"use\\x20strict";', var code = uglify.minify('"use strict";"use\\x20strict";',
{output: {semicolons: false}, compress: false} {fromString: true, output: {semicolons: false}, compress: false}
).code; ).code;
assert.strictEqual(code, '"use strict";;"use strict"\n'); assert.strictEqual(code, '"use strict";;"use strict"\n');
}); });
@@ -340,7 +340,7 @@ describe("Directives", function() {
]; ];
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0], {output:{quote_style: tests[i][1]}, compress: false}).code, uglify.minify(tests[i][0], {fromString: true, output:{quote_style: tests[i][1]}, compress: false}).code,
tests[i][2], tests[i][2],
tests[i][0] + " using mode " + tests[i][1] tests[i][0] + " using mode " + tests[i][1]
); );
@@ -351,28 +351,18 @@ describe("Directives", function() {
var tests = [ var tests = [
[ [
'"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");', '"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");',
'"use strict";"use foo";doSomething("foo");', '"use strict";"use foo";doSomething("foo");'
'function f(){ "use strict" }',
'function f(){ "use asm" }',
'function f(){ "use nondirective" }',
'function f(){ ;"use strict" }',
'function f(){ "use \n"; }',
], ],
[ [
// Nothing gets optimised in the compressor because "use asm" is the first statement // Nothing gets optimised in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;', '"use asm";"use\\x20strict";1+1;',
'"use asm";;"use strict";1+1;', // Yet, the parser noticed that "use strict" wasn't a directive '"use asm";;"use strict";1+1;' // Yet, the parser noticed that "use strict" wasn't a directive
'function f(){"use strict"}',
'function f(){"use asm"}',
'function f(){"use nondirective"}',
'function f(){}',
'function f(){}',
] ]
]; ];
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0]).code, uglify.minify(tests[i][0], {fromString: true, compress: {collapse_vars: true, side_effects: true}}).code,
tests[i][1], tests[i][1],
tests[i][0] tests[i][0]
); );

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("Getters and setters", function() { describe("Getters and setters", function() {
@@ -71,7 +71,7 @@ describe("Getters and setters", function() {
var fail = function(data) { var fail = function(data) {
return function (e) { return function (e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Unexpected token: operator (" + data.operator + ")"; e.message === "Invalid getter/setter name: " + data.operator;
}; };
}; };

View File

@@ -1,80 +1,58 @@
var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var exec = require("child_process").exec;
var path = require("path"); var path = require("path");
var readFileSync = require("fs").readFileSync;
describe("bin/uglifyjs with input file globs", function() { describe("minify() with input file globs", function() {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; it("minify() with one input file glob string.", function() {
it("bin/uglifyjs with one input file extension glob.", function(done) { var result = Uglify.minify("test/input/issue-1242/foo.*");
var command = uglifyjscmd + ' "test/input/issue-1242/foo.*" -cm'; assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);');
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);\n');
done();
}); });
it("minify() with an array of one input file glob.", function() {
var result = Uglify.minify([
"test/input/issue-1242/b*.es5",
]);
assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}');
}); });
it("bin/uglifyjs with one input file name glob.", function(done) { it("minify() with an array of multiple input file globs.", function() {
var command = uglifyjscmd + ' "test/input/issue-1242/b*.es5" -cm'; var result = Uglify.minify([
"test/input/issue-1242/???.es5",
exec(command, function(err, stdout) { "test/input/issue-1242/*.js",
if (err) throw err; ], {
compress: { toplevel: true }
assert.strictEqual(stdout, 'function bar(n){return 3*n}function baz(n){return n/2}\n');
done();
}); });
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){print("Foo:",2*n)}(11);');
}); });
it("bin/uglifyjs with multiple input file globs.", function(done) { it("should throw with non-matching glob string", function() {
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel'; var glob = "test/input/issue-1242/blah.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
exec(command, function(err, stdout) { assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
if (err) throw err; assert.throws(function() {
Uglify.minify(glob);
assert.strictEqual(stdout, '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){print("Foo:",2*n)}(11);\n'); }, "should throw file not found");
done();
}); });
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 throw with non-matching glob string", function(done) { it("should handle special characters in glob string", function() {
var command = uglifyjscmd + ' "test/input/issue-1242/blah.*"'; var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
assert.strictEqual(result.code, "console.log(x);");
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.ok(/^ERROR: ENOENT/.test(stderr));
done();
}); });
}); it("should handle array of glob strings - matching and otherwise", function() {
it('"?" in glob string should not match "/"', function(done) {
var command = uglifyjscmd + ' "test/input?issue-1242/foo.*"';
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.ok(/^ERROR: ENOENT/.test(stderr));
done();
});
});
it("should handle special characters in glob string", function(done) {
var command = uglifyjscmd + ' "test/input/issue-1632/^{*}[???](*)+$.??" -cm';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(x);\n");
done();
});
});
it("should handle array of glob strings - matching and otherwise", function(done) {
var dir = "test/input/issue-1242"; var dir = "test/input/issue-1242";
var command = uglifyjscmd + ' "' + [ var matches = Uglify.simple_glob([
path.join(dir, "b*.es5"), path.join(dir, "b*.es5"),
path.join(dir, "z*.es5"), path.join(dir, "z*.es5"),
path.join(dir, "*.js") path.join(dir, "*.js"),
].join('" "') + '"'; ]);
assert.strictEqual(matches.length, 4);
exec(command, function(err, stdout, stderr) { assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
assert.ok(err); assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
assert.ok(/^ERROR: ENOENT.*?z\*\.es5/.test(stderr)); assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
done(); assert.strictEqual(matches[3], path.join(dir, "qux.js"));
});
}); });
}); });

View File

@@ -8,7 +8,12 @@ describe("Huge number of comments.", function() {
for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; } for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; }
for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; } for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; }
js += "x; }"; js += "x; }";
var result = Uglify.minify(js, { mangle: false }); var result = Uglify.minify(js, {
fromString: true,
mangle: false,
compress: {}
});
assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}"); assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}");
}); });
}); });

View File

@@ -25,9 +25,9 @@ describe("input sourcemaps", function() {
transpilemap = sourceMap || getMap(); transpilemap = sourceMap || getMap();
var result = Uglify.minify(transpiled, { var result = Uglify.minify(transpiled, {
sourceMap: { fromString: true,
content: transpilemap inSourceMap: transpilemap,
} outSourceMap: true
}); });
map = new SourceMapConsumer(result.map); map = new SourceMapConsumer(result.map);

View File

@@ -11,7 +11,7 @@ describe("let", function() {
s += "var v" + i + "=0;"; s += "var v" + i + "=0;";
} }
s += '}'; s += '}';
var result = Uglify.minify(s, {compress: false}); var result = Uglify.minify(s, {fromString: true, compress: false});
// Verify that select keywords and reserved keywords not produced // Verify that select keywords and reserved keywords not produced
assert.strictEqual(result.code.indexOf("var let="), -1); assert.strictEqual(result.code.indexOf("var let="), -1);

View File

@@ -1,10 +1,11 @@
var Uglify = require("../node"); var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("line-endings", function() { describe("line-endings", function() {
var options = { var options = {
compress: false, fromString: true,
mangle: false, mangle: false,
compress: false,
output: { output: {
beautify: false, beautify: false,
comments: /^!/, comments: /^!/,

View File

@@ -6,41 +6,43 @@ describe("Input file as map", function() {
var jsMap = { var jsMap = {
'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' '/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'
}; };
var result = Uglify.minify(jsMap, {sourceMap: true}); var result = Uglify.minify(jsMap, {fromString: true, outSourceMap: true});
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};');
assert.deepEqual(map.sources, ['/scripts/foo.js']); assert.deepEqual(map.sources, ['/scripts/foo.js']);
assert.strictEqual(map.file, undefined); assert.strictEqual(map.file, undefined);
result = Uglify.minify(jsMap); result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js'});
assert.strictEqual(result.map, undefined); assert.strictEqual(result.map, null);
result = Uglify.minify(jsMap, {sourceMap: {filename: 'out.js'}}); result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js', outSourceMap: true});
map = JSON.parse(result.map); map = JSON.parse(result.map);
assert.strictEqual(map.file, 'out.js'); assert.strictEqual(map.file, 'out.js');
}); });
it("Should accept array of strings", function() { it("Should accept array of objects and strings", function() {
var jsSeq = [ var jsSeq = [
'var foo = {"x": 1, y: 2, \'z\': 3};', {'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'},
'var bar = 15;' 'var bar = 15;'
]; ];
var result = Uglify.minify(jsSeq, {sourceMap: true}); var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true});
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;'); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;');
assert.deepEqual(map.sources, ['0', '1']); assert.strictEqual(map.sources[0], '/scripts/foo.js');
}); });
it("Should correctly include source", function() { it("Should correctly include source", function() {
var jsMap = { var jsSeq = [
'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' {'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'},
}; 'var bar = 15;'
var result = Uglify.minify(jsMap, {sourceMap: {includeSources: true}}); ];
var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true, sourceMapIncludeSources: true});
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;');
assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};']); assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};', 'var bar = 15;']);
}); });
}); });

View File

@@ -2,14 +2,10 @@ var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
function read(path) {
return readFileSync(path, "utf8");
}
describe("minify", function() { describe("minify", function() {
it("Should test basic sanity of minify with default options", function() { it("Should test basic sanity of minify with default options", function() {
var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }'; var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }';
var result = Uglify.minify(js); var result = Uglify.minify(js, {fromString: true});
assert.strictEqual(result.code, 'function foo(n){return n?3:7}'); assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
}); });
@@ -17,7 +13,7 @@ describe("minify", function() {
it("Should preserve quotes in object literals", function() { it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
output: { fromString: true, output: {
keep_quoted_props: true keep_quoted_props: true
}}); }});
assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};'); assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};');
@@ -26,7 +22,7 @@ describe("minify", function() {
it("Should preserve quote styles when quote_style is 3", function() { it("Should preserve quote styles when quote_style is 3", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
output: { fromString: true, output: {
keep_quoted_props: true, keep_quoted_props: true,
quote_style: 3 quote_style: 3
}}); }});
@@ -36,7 +32,7 @@ describe("minify", function() {
it("Should not preserve quotes in object literals when disabled", function() { it("Should not preserve quotes in object literals when disabled", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
output: { fromString: true, output: {
keep_quoted_props: false, keep_quoted_props: false,
quote_style: 3 quote_style: 3
}}); }});
@@ -48,13 +44,12 @@ describe("minify", function() {
it("Shouldn't mangle quoted properties", function() { it("Shouldn't mangle quoted properties", function() {
var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};'; var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true,
compress: { compress: {
properties: false properties: false
}, },
mangle: { mangleProperties: {
properties: { ignore_quoted: true
keep_quoted: true
}
}, },
output: { output: {
keep_quoted_props: true, keep_quoted_props: true,
@@ -68,12 +63,10 @@ describe("minify", function() {
describe("inSourceMap", function() { describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() { it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), { var result = Uglify.minify('./test/input/issue-1236/simple.js', {
sourceMap: { outSourceMap: "simple.min.js.map",
content: read("./test/input/issue-1236/simple.js.map"), inSourceMap: "./test/input/issue-1236/simple.js.map",
filename: "simple.min.js", sourceMapIncludeSources: true
includeSources: true
}
}); });
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
@@ -84,12 +77,10 @@ describe("minify", function() {
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
}); });
it("Should process inline source map", function() { it("Should process inline source map", function() {
var code = Uglify.minify(read("./test/input/issue-520/input.js"), { var code = Uglify.minify("./test/input/issue-520/input.js", {
compress: { toplevel: true }, compress: { toplevel: true },
sourceMap: { inSourceMap: "inline",
content: "inline", sourceMapInline: true
url: "inline"
}
}).code + "\n"; }).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8")); assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
}); });
@@ -100,11 +91,9 @@ describe("minify", function() {
warnings.push(txt); warnings.push(txt);
}; };
try { try {
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), { var result = Uglify.minify("./test/input/issue-1323/sample.js", {
inSourceMap: "inline",
mangle: false, mangle: false,
sourceMap: {
content: "inline"
}
}); });
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();"); assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
assert.strictEqual(warnings.length, 1); assert.strictEqual(warnings.length, 1);
@@ -114,54 +103,50 @@ describe("minify", function() {
} }
}); });
it("Should fail with multiple input and inline source map", function() { it("Should fail with multiple input and inline source map", function() {
var result = Uglify.minify([ assert.throws(function() {
read("./test/input/issue-520/input.js"), Uglify.minify([
read("./test/input/issue-520/output.js") "./test/input/issue-520/input.js",
"./test/input/issue-520/output.js"
], { ], {
sourceMap: { inSourceMap: "inline",
content: "inline", sourceMapInline: true
url: "inline" });
} });
});
it("Should fail with SpiderMonkey and inline source map", function() {
assert.throws(function() {
Uglify.minify("./test/input/issue-520/input.js", {
inSourceMap: "inline",
sourceMapInline: true,
spidermonkey: true
});
}); });
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
}); });
}); });
describe("sourceMapInline", function() { describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() { it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', { var result = Uglify.minify('var a = function(foo) { return foo; };', {
sourceMap: { fromString: true,
url: "inline" sourceMapInline: true
}
}); });
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" + assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0="); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIj8iXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
}); });
it("should not append source map to output js when sourceMapInline is not enabled", function() { it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };'); var result = Uglify.minify('var a = function(foo) { return foo; };', {
fromString: true
});
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};"); assert.strictEqual(code, "var a=function(n){return n};");
}); });
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
}); });
describe("#__PURE__", function() { describe("#__PURE__", function() {
it("should drop #__PURE__ hint after use", function() { it("should drop #__PURE__ hint after use", function() {
var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', { var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', {
fromString: true,
output: { output: {
comments: "all", comments: "all",
beautify: false, beautify: false,
@@ -172,6 +157,7 @@ describe("minify", function() {
}); });
it("should not drop #__PURE__ hint if function is retained", function() { it("should not drop #__PURE__ hint if function is retained", function() {
var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", { var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", {
fromString: true,
output: { output: {
comments: "all", comments: "all",
beautify: false, beautify: false,
@@ -183,29 +169,27 @@ describe("minify", function() {
}); });
describe("JS_Parse_Error", function() { describe("JS_Parse_Error", function() {
it("should return syntax error", function() { it("should throw syntax error", function() {
var result = Uglify.minify("function f(a{}"); assert.throws(function() {
var err = result.error; Uglify.minify("function f(a{}", { fromString: true });
}, function(err) {
assert.ok(err instanceof Error); assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»"); assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»");
assert.strictEqual(err.filename, "0"); assert.strictEqual(err.filename, 0);
assert.strictEqual(err.line, 1); assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12); assert.strictEqual(err.col, 12);
return true;
});
}); });
}); });
describe("global_defs", function() { describe("Compressor", function() {
it("should throw for non-trivial expressions", function() { it("should be backward compatible with ast.transform(compressor)", function() {
var result = Uglify.minify("alert(42);", { var ast = Uglify.parse("function f(a){for(var i=0;i<a;i++)console.log(i)}");
compress: { ast.figure_out_scope();
global_defs: { ast = ast.transform(Uglify.Compressor());
"@alert": "debugger" assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i<a;i++)console.log(i)}");
}
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
});
}); });
})
}); });

View File

@@ -34,6 +34,7 @@ describe("New", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i], { uglify.minify(tests[i], {
fromString: true,
output: {beautify: true}, output: {beautify: true},
compress: false, compress: false,
mangle: false mangle: false
@@ -75,6 +76,7 @@ describe("New", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i], { uglify.minify(tests[i], {
fromString: true,
output: {beautify: false}, output: {beautify: false},
compress: false, compress: false,
mangle: false mangle: false

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("Number literals", function () { describe("Number literals", function () {
it("Should not allow legacy octal literals in strict mode", function() { it("Should not allow legacy octal literals in strict mode", function() {

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require("../../");
var assert = require("assert"); var assert = require("assert");
describe("operator", function() { describe("operator", function() {

View File

@@ -42,8 +42,8 @@ describe("test/jetstream.js", function() {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done); run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
}); });
[ [
"-mc", "-mc warnings=false",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto,warnings=false",
].forEach(function(options) { ].forEach(function(options) {
it("Should pass with options " + options, function(done) { it("Should pass with options " + options, function(done) {
var args = options.split(/ /); var args = options.split(/ /);

View File

@@ -13,7 +13,9 @@ describe("screw-ie8", function () {
}\ }\
console.log('undefined is ' + undefined);\ console.log('undefined is ' + undefined);\
return b === undefined;\ return b === undefined;\
};" };", {
fromString: true
}
).code, ).code,
'function a(o){try{throw"Stuff"}catch(o){console.log("caught: "+o)}return console.log("undefined is "+void 0),void 0===o}' 'function a(o){try{throw"Stuff"}catch(o){console.log("caught: "+o)}return console.log("undefined is "+void 0),void 0===o}'
); );

View File

@@ -1,23 +1,32 @@
var assert = require("assert"); var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var uglify = require("../node"); var uglify = require("../../");
describe("spidermonkey export/import sanity test", function() { describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) { it("should produce a functional build when using --self with spidermonkey", function (done) {
this.timeout(30000); this.timeout(20000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " + var command = uglifyjs + " --self -cm --wrap SpiderUglify --dump-spidermonkey-ast | " +
uglifyjs + " -p spidermonkey -cm"; uglifyjs + " --spidermonkey -cm";
exec(command, function(err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
eval(stdout); eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object"); assert.strictEqual(typeof SpiderUglify, "object");
var result = SpiderUglify.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined); var ast = SpiderUglify.parse("foo([true,,2+3]);");
assert.strictEqual(result.code, "foo([!0,,5]);"); assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
ast.figure_out_scope();
ast = SpiderUglify.Compressor({}).compress(ast);
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
var stream = SpiderUglify.OutputStream({});
ast.print(stream);
var code = stream.toString();
assert.strictEqual(code, "foo([!0,,5]);");
done(); done();
}); });

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("String literals", function() { describe("String literals", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("With", function() { describe("With", function() {
it("Should throw syntaxError when using with statement in strict mode", function() { it("Should throw syntaxError when using with statement in strict mode", function() {

View File

@@ -1,73 +1,103 @@
// Testing UglifyJS <-> SpiderMonkey AST conversion // Testing UglifyJS <-> SpiderMonkey AST conversion
"use strict"; // through generative testing.
var acorn = require("acorn"); var UglifyJS = require(".."),
var ufuzz = require("./ufuzz"); escodegen = require("escodegen"),
var UglifyJS = require(".."); esfuzz = require("esfuzz"),
estraverse = require("estraverse"),
prefix = "\r ";
function try_beautify(code) { // Normalizes input AST for UglifyJS in order to get correct comparison.
var beautified = UglifyJS.minify(code, {
compress: false, function normalizeInput(ast) {
mangle: false, return estraverse.replace(ast, {
output: { enter: function(node, parent) {
beautify: true, switch (node.type) {
bracketize: true // Internally mark all the properties with semi-standard type "Property".
case "ObjectExpression":
node.properties.forEach(function (property) {
property.type = "Property";
});
break;
// Since UglifyJS doesn"t recognize different types of property keys,
// decision on SpiderMonkey node type is based on check whether key
// can be valid identifier or not - so we do in input AST.
case "Property":
var key = node.key;
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) {
node.key = {
type: "Identifier",
name: key.value
};
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) {
node.key = {
type: "Literal",
value: key.name
};
}
break;
// UglifyJS internally flattens all the expression sequences - either
// to one element (if sequence contains only one element) or flat list.
case "SequenceExpression":
node.expressions = node.expressions.reduce(function flatten(list, expr) {
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]);
}, []);
if (node.expressions.length === 1) {
return node.expressions[0];
}
break;
}
} }
}); });
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
console.log(code);
} else {
console.log("// (beautified)");
console.log(beautified.code);
}
} }
function test(original, estree, description) { module.exports = function(options) {
var transformed = UglifyJS.minify(UglifyJS.AST_Node.from_mozilla_ast(estree), { console.log("--- UglifyJS <-> Mozilla AST conversion");
compress: false,
mangle: false
});
if (transformed.error || original !== transformed.code) {
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("//", description);
if (transformed.error) {
console.log(transformed.error.stack);
} else {
try_beautify(transformed.code);
}
console.log("!!!!!! Failed... round", round);
process.exit(1);
}
}
var num_iterations = ufuzz.num_iterations; for (var counter = 0; counter < options.iterations; counter++) {
for (var round = 1; round <= num_iterations; round++) { process.stdout.write(prefix + counter + "/" + options.iterations);
process.stdout.write(round + " of " + num_iterations + "\r");
var code = ufuzz.createTopLevelCode(); var ast1 = normalizeInput(esfuzz.generate({
var uglified = UglifyJS.minify(code, { maxDepth: options.maxDepth
compress: false, }));
mangle: false,
output: { var ast2 =
ast: true UglifyJS
.AST_Node
.from_mozilla_ast(ast1)
.to_mozilla_ast();
var astPair = [
{name: 'expected', value: ast1},
{name: 'actual', value: ast2}
];
var jsPair = astPair.map(function(item) {
return {
name: item.name,
value: escodegen.generate(item.value)
} }
}); });
test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
try { if (jsPair[0].value !== jsPair[1].value) {
test(uglified.code, acorn.parse(code), "acorn.parse()"); var fs = require("fs");
} catch (e) { var acorn = require("acorn");
console.log("//=============================================================");
console.log("// acorn parser failed... round", round); fs.existsSync("tmp") || fs.mkdirSync("tmp");
console.log(e);
console.log("// original code"); jsPair.forEach(function (item) {
console.log(code); var fileName = "tmp/dump_" + item.name;
var ast = acorn.parse(item.value);
fs.writeFileSync(fileName + ".js", item.value);
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2));
});
process.stdout.write("\n");
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs.");
} }
} }
console.log();
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n");
};

View File

@@ -1,6 +0,0 @@
var fs = require("fs");
new Function("MOZ_SourceMap", "exports", require("../tools/node").FILES.map(function(file) {
if (/exports\.js$/.test(file)) file = require.resolve("./exports");
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(require("source-map"), exports);

View File

@@ -1,11 +1,10 @@
#! /usr/bin/env node #! /usr/bin/env node
var U = require("./node"); var U = require("../tools/node");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
var semver = require("semver");
var tests_dir = path.dirname(module.filename); var tests_dir = path.dirname(module.filename);
var failures = 0; var failures = 0;
@@ -24,6 +23,12 @@ mocha_tests();
var run_sourcemaps_tests = require('./sourcemaps'); var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests(); run_sourcemaps_tests();
var run_ast_conversion_tests = require("./mozilla-ast");
run_ast_conversion_tests({
iterations: 1000
});
/* -----[ utils ]----- */ /* -----[ utils ]----- */
function tmpl() { function tmpl() {
@@ -165,8 +170,7 @@ function run_compress_tests() {
failed_files[file] = 1; failed_files[file] = 1;
} }
} }
if (test.expect_stdout if (test.expect_stdout) {
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code); var stdout = sandbox.run_code(input_code);
if (test.expect_stdout === true) { if (test.expect_stdout === true) {
test.expect_stdout = stdout; test.expect_stdout = stdout;
@@ -276,14 +280,7 @@ function parse_test(file) {
if (node instanceof U.AST_LabeledStatement) { if (node instanceof U.AST_LabeledStatement) {
var label = node.label; var label = node.label;
assert.ok( assert.ok(
[ ["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0,
"input",
"expect",
"expect_exact",
"expect_warnings",
"expect_stdout",
"node_version",
].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", { tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name, name: label.name,
line: label.start.line, line: label.start.line,
@@ -291,7 +288,7 @@ function parse_test(file) {
}) })
); );
var stat = node.body; var stat = node.body;
if (label.name == "expect_exact" || label.name == "node_version") { if (label.name == "expect_exact") {
test[label.name] = read_string(stat); test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") { } else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) { if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {

View File

@@ -1,17 +1,14 @@
var semver = require("semver");
var vm = require("vm"); var vm = require("vm");
function safe_log(arg, level) { function safe_log(arg) {
if (arg) switch (typeof arg) { if (arg) switch (typeof arg) {
case "function": case "function":
return arg.toString(); return arg.toString();
case "object": case "object":
if (/Error$/.test(arg.name)) return arg.toString(); if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString(); arg.constructor.toString();
if (level--) for (var key in arg) { for (var key in arg) {
if (!Object.getOwnPropertyDescriptor(arg, key).get) { arg[key] = safe_log(arg[key]);
arg[key] = safe_log(arg[key], level);
}
} }
} }
return arg; return arg;
@@ -51,9 +48,7 @@ exports.run_code = function(code) {
].join("\n"), { ].join("\n"), {
console: { console: {
log: function() { log: function() {
return console.log.apply(console, [].map.call(arguments, function(arg) { return console.log.apply(console, [].map.call(arguments, safe_log));
return safe_log(arg, 3);
}));
} }
} }
}, { timeout: 5000 }); }, { timeout: 5000 });
@@ -64,7 +59,7 @@ exports.run_code = function(code) {
process.stdout.write = original_write; process.stdout.write = original_write;
} }
}; };
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { exports.same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? 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") {
if (expected.name != actual.name) return false; if (expected.name != actual.name) return false;

View File

@@ -26,11 +26,11 @@ module.exports = function () {
} }
function source_map(js) { function source_map(js) {
return JSON.parse(UglifyJS.minify(js, { var source_map = UglifyJS.SourceMap();
compress: false, var stream = UglifyJS.OutputStream({ source_map: source_map });
mangle: false, var parsed = UglifyJS.parse(js);
sourceMap: true parsed.print(stream);
}).map); return JSON.parse(source_map.toString());
} }
// Run standalone // Run standalone

View File

@@ -48,9 +48,8 @@ var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest functio
var num_iterations = +process.argv[2] || 1/0; var num_iterations = +process.argv[2] || 1/0;
var verbose = false; // log every generated test var verbose = false; // log every generated test
var verbose_interval = false; // log every 100 generated tests var verbose_interval = false; // log every 100 generated tests
var verbose_error = false;
var use_strict = false; var use_strict = false;
var catch_redef = require.main === module;
var generate_directive = require.main === module;
for (var i = 2; i < process.argv.length; ++i) { for (var i = 2; i < process.argv.length; ++i) {
switch (process.argv[i]) { switch (process.argv[i]) {
case '-v': case '-v':
@@ -59,6 +58,9 @@ for (var i = 2; i < process.argv.length; ++i) {
case '-V': case '-V':
verbose_interval = true; verbose_interval = true;
break; break;
case '-E':
verbose_error = true;
break;
case '-t': case '-t':
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i]; MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run'); if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
@@ -77,12 +79,6 @@ for (var i = 2; i < process.argv.length; ++i) {
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list'); if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
break; break;
case '--no-catch-redef':
catch_redef = false;
break;
case '--no-directive':
generate_directive = false;
break;
case '--use-strict': case '--use-strict':
use_strict = true; use_strict = true;
break; break;
@@ -107,12 +103,11 @@ for (var i = 2; i < process.argv.length; ++i) {
console.log('<number>: generate this many cases (if used must be first arg)'); console.log('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case'); console.log('-v: print every generated test case');
console.log('-V: print every 100th generated test case'); console.log('-V: print every 100th generated test case');
console.log('-E: print generated test case with runtime error');
console.log('-t <int>: generate this many toplevels per run (more take longer)'); console.log('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)'); console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)'); console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)'); console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"'); console.log('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise'); console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated'); console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
@@ -197,33 +192,12 @@ var ASSIGNMENTS = [
'=', '=',
'=', '=',
'=', '=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'==',
'!=',
'===',
'!==',
'+=', '+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'-=', '-=',
'*=', '*=',
'/=', '/=',
@@ -233,8 +207,7 @@ var ASSIGNMENTS = [
'<<=', '<<=',
'>>=', '>>=',
'>>>=', '>>>=',
'%=', '%=' ];
];
var UNARY_SAFE = [ var UNARY_SAFE = [
'+', '+',
@@ -303,7 +276,6 @@ var TYPEOF_OUTCOMES = [
'symbol', 'symbol',
'crap' ]; 'crap' ];
var unique_vars = [];
var loops = 0; var loops = 0;
var funcs = 0; var funcs = 0;
var labels = 10000; var labels = 10000;
@@ -318,10 +290,6 @@ function strictMode() {
} }
function createTopLevelCode() { function createTopLevelCode() {
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
unique_vars.length = 0;
loops = 0;
funcs = 0;
return [ return [
strictMode(), strictMode(),
'var a = 100, b = 10, c = 0;', 'var a = 100, b = 10, c = 0;',
@@ -357,36 +325,33 @@ function createArgs() {
return args.join(', '); return args.join(', ');
} }
function filterDirective(s) {
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ';' + s[2];
return s;
}
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) { function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
if (--recurmax < 0) { return ';'; } if (--recurmax < 0) { return ';'; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var func = funcs++; var func = funcs++;
var namesLenBefore = VAR_NAMES.length; var namesLenBefore = VAR_NAMES.length;
var name; var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, noDecl);
if (inGlobal || rng(5) > 0) name = 'f' + func; if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called
else { var s = '';
unique_vars.push('a', 'b', 'c');
name = createVarName(MANDATORY, noDecl);
unique_vars.length -= 3;
}
var s = [
'function ' + name + '(' + createParams() + '){',
strictMode()
];
if (rng(5) === 0) { if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess. // functions with functions. lower the recursion to prevent a mess.
s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth)); s = [
'function ' + name + '(' + createParams() + '){',
strictMode(),
createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth),
'}',
''
].join('\n');
} else { } else {
// functions with statements // functions with statements
s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)); s = [
'function ' + name + '(' + createParams() + '){',
strictMode(),
createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'}',
''
].join('\n');
} }
s.push('}', '');
s = filterDirective(s).join('\n');
VAR_NAMES.length = namesLenBefore; VAR_NAMES.length = namesLenBefore;
@@ -394,6 +359,7 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
// avoid "function statements" (decl inside statements) // avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');'; else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
return s; return s;
} }
@@ -440,7 +406,7 @@ function getLabel(label) {
return label && " L" + label; return label && " L" + label;
} }
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) { function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
++stmtDepth; ++stmtDepth;
var loop = ++loops; var loop = ++loops;
if (--recurmax < 0) { if (--recurmax < 0) {
@@ -448,11 +414,10 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
} }
// allow to forcefully generate certain structures at first or second recursion level // allow to forcefully generate certain structures at first or second recursion level
if (target === undefined) { var target = 0;
if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE; if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE; else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)]; else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
}
switch (target) { switch (target) {
case STMT_BLOCK: case STMT_BLOCK:
@@ -495,22 +460,20 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
case STMT_VAR: case STMT_VAR:
switch (rng(3)) { switch (rng(3)) {
case 0: case 0:
unique_vars.push('c');
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
unique_vars.pop(); if (name === 'c') name = 'a';
return 'var ' + name + ';'; return 'var ' + name + ';';
case 1: case 1:
// initializer can only have one expression // initializer can only have one expression
unique_vars.push('c');
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
unique_vars.pop(); if (name === 'c') name = 'b';
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
default: default:
// initializer can only have one expression // initializer can only have one expression
unique_vars.push('c');
var n1 = createVarName(MANDATORY); var n1 = createVarName(MANDATORY);
if (n1 === 'c') n1 = 'b';
var n2 = createVarName(MANDATORY); var n2 = createVarName(MANDATORY);
unique_vars.pop(); if (n2 === 'c') n2 = 'b';
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
} }
case STMT_RETURN_ETC: case STMT_RETURN_ETC:
@@ -551,11 +514,8 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var catchName = createVarName(MANDATORY); var catchName = createVarName(MANDATORY);
var freshCatchName = VAR_NAMES.length !== nameLenBefore; var freshCatchName = VAR_NAMES.length !== nameLenBefore;
if (!catch_redef) unique_vars.push(catchName);
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
// remove catch name if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); // remove catch name
if (!catch_redef) unique_vars.pop();
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
} }
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
return s; return s;
@@ -633,9 +593,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
unique_vars.push('c');
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
unique_vars.pop(); if (name == 'c') name = 'a';
var s = []; var s = [];
switch (rng(5)) { switch (rng(5)) {
case 0: case 0:
@@ -677,7 +636,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
strictMode() strictMode()
); );
if (instantiate) for (var i = rng(4); --i >= 0;) { if (instantiate) for (var i = rng(4); --i >= 0;) {
if (rng(2)) s.push('this.' + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); if (rng(2)) s.push('this.' + getDotKey() + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
} }
s.push( s.push(
@@ -687,7 +646,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break; break;
} }
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join('\n'); return s.join('\n');
case p++: case p++:
case p++: case p++:
return createTypeofExpr(recurmax, stmtDepth, canThrow); return createTypeofExpr(recurmax, stmtDepth, canThrow);
@@ -730,19 +689,19 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
") || " + rng(10) + ").toString()[" + ") || " + rng(10) + ").toString()[" +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] "; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
case p++: case p++:
return createArrayLiteral(recurmax, stmtDepth, canThrow); return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++:
return createObjectLiteral(recurmax, stmtDepth, canThrow); return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++:
return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' + return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++: case p++:
return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' + return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++: case p++:
return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey(); return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
case p++: case p++:
return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey(); return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
case p++: case p++:
var name = getVarName(); var name = getVarName();
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
@@ -754,7 +713,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return _createExpression(recurmax, noComma, stmtDepth, canThrow); return _createExpression(recurmax, noComma, stmtDepth, canThrow);
} }
function createArrayLiteral(recurmax, stmtDepth, canThrow) { function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) {
recurmax--; recurmax--;
var arr = "["; var arr = "[";
for (var i = rng(6); --i >= 0;) { for (var i = rng(6); --i >= 0;) {
@@ -787,56 +746,18 @@ var KEYS = [
"3", "3",
].concat(SAFE_KEYS); ].concat(SAFE_KEYS);
function getDotKey(assign) { function getDotKey() {
var key; return SAFE_KEYS[rng(SAFE_KEYS.length)];
do {
key = SAFE_KEYS[rng(SAFE_KEYS.length)];
} while (assign && key == "length");
return key;
} }
function createAccessor(recurmax, stmtDepth, canThrow) { function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
var namesLenBefore = VAR_NAMES.length;
var s;
var prop1 = getDotKey();
if (rng(2) == 0) {
s = [
'get ' + prop1 + '(){',
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
'},'
];
} else {
var prop2;
do {
prop2 = getDotKey();
} while (prop1 == prop2);
s = [
'set ' + prop1 + '(' + createVarName(MANDATORY) + '){',
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'this.' + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ';',
'},'
];
}
VAR_NAMES.length = namesLenBefore;
return filterDirective(s).join('\n');
}
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
recurmax--; recurmax--;
var obj = ['({']; var obj = "({";
for (var i = rng(6); --i >= 0;) { for (var i = rng(6); --i >= 0;) {
if (rng(20) == 0) {
obj.push(createAccessor(recurmax, stmtDepth, canThrow));
} else {
var key = KEYS[rng(KEYS.length)]; var key = KEYS[rng(KEYS.length)];
obj.push(key + ':(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '),'); obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), ";
} }
} return obj + "})";
obj.push('})');
return obj.join('\n');
} }
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
@@ -866,7 +787,7 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
case 4: case 4:
assignee = getVarName(); assignee = getVarName();
expr = '(' + assignee + '.' + getDotKey(true) + createAssignment() expr = '(' + assignee + '.' + getDotKey() + createAssignment()
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
default: default:
@@ -923,65 +844,77 @@ function getVarName() {
function createVarName(maybe, dontStore) { function createVarName(maybe, dontStore) {
if (!maybe || rng(2)) { if (!maybe || rng(2)) {
var name = VAR_NAMES[rng(VAR_NAMES.length)];
var suffix = rng(3); var suffix = rng(3);
var name; if (suffix) {
do { name += '_' + suffix;
name = VAR_NAMES[rng(VAR_NAMES.length)]; if (!dontStore) VAR_NAMES.push(name);
if (suffix) name += '_' + suffix; }
} while (unique_vars.indexOf(name) >= 0);
if (suffix && !dontStore) VAR_NAMES.push(name);
return name; return name;
} }
return ''; return '';
} }
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.num_iterations = num_iterations;
return;
}
function try_beautify(code, result) { function try_beautify(code, result) {
try {
var beautified = UglifyJS.minify(code, { var beautified = UglifyJS.minify(code, {
fromString: true,
compress: false, compress: false,
mangle: false, mangle: false,
output: { output: {
beautify: true, beautify: true,
bracketize: true, bracketize: true,
}, },
}); }).code;
if (beautified.error) { if (sandbox.same_stdout(sandbox.run_code(beautified), result)) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)"); console.log("// (beautified)");
console.log(beautified.code); console.log(beautified);
return; return;
} }
} catch (e) {
console.log("// !!! beautify failed !!!");
console.log(e.stack);
}
console.log("//"); console.log("//");
console.log(code); console.log(code);
} }
var default_options = UglifyJS.default_options(); function infer_options(ctor) {
try {
ctor({ 0: 0 });
} catch (e) {
return e.defs;
}
}
var default_options = {
compress: infer_options(UglifyJS.Compressor),
mangle: {
"cache": null,
"eval": false,
"ie8": false,
"keep_fnames": false,
"toplevel": false,
},
output: infer_options(UglifyJS.OutputStream),
};
function log_suspects(minify_options, component) { function log_suspects(minify_options, component) {
var options = component in minify_options ? minify_options[component] : true; var options = component in minify_options ? minify_options[component] : true;
if (!options) return; if (!options) return;
if (typeof options != "object") options = {}; options = UglifyJS.defaults(options, default_options[component]);
var defs = default_options[component]; var suspects = Object.keys(default_options[component]).filter(function(name) {
var suspects = Object.keys(defs).filter(function(name) { if (options[name]) {
if ((name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options)); var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options)); var o = JSON.parse(JSON.stringify(options));
o[name] = false; o[name] = false;
m[component] = o; m[component] = o;
var result = UglifyJS.minify(original_code, m); try {
if (result.error) { var r = sandbox.run_code(UglifyJS.minify(original_code, m).code);
console.log("Error testing options." + component + "." + name);
console.log(result.error);
} else {
var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r); return sandbox.same_stdout(original_result, r);
} catch (e) {
console.log("Error testing options." + component + "." + name);
console.log(e);
} }
} }
}); });
@@ -1036,28 +969,37 @@ var fallback_options = [ JSON.stringify({
compress: false, compress: false,
mangle: false mangle: false
}) ]; }) ];
var minify_options = require("./ufuzz.json").map(JSON.stringify); var minify_options = require("./ufuzz.json").map(function(options) {
options.fromString = true;
return JSON.stringify(options);
});
var original_code, original_result; var original_code, original_result;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r"); process.stdout.write(round + " of " + num_iterations + "\r");
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
loops = 0;
funcs = 0;
original_code = createTopLevelCode(); original_code = createTopLevelCode();
original_result = sandbox.run_code(original_code); original_result = sandbox.run_code(original_code);
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) { (typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
uglify_code = UglifyJS.minify(original_code, JSON.parse(options)); try {
if (!uglify_code.error) { uglify_code = UglifyJS.minify(original_code, JSON.parse(options)).code;
uglify_code = uglify_code.code; } catch (e) {
uglify_code = e;
}
ok = typeof uglify_code == "string";
if (ok) {
uglify_result = sandbox.run_code(uglify_code); uglify_result = sandbox.run_code(uglify_code);
ok = sandbox.same_stdout(original_result, uglify_result); ok = sandbox.same_stdout(original_result, uglify_result);
} else { } else if (typeof original_result != "string") {
uglify_code = uglify_code.error;
if (typeof original_result != "string") {
ok = uglify_code.name == original_result.name; ok = uglify_code.name == original_result.name;
} }
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") { else if (verbose_error && typeof original_result != "string") {
console.log("//============================================================="); console.log("//=============================================================");
console.log("// original code"); console.log("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result);

View File

@@ -11,12 +11,20 @@
"compress": false "compress": false
}, },
{ {
"compress": {
"warnings": false
},
"mangle": false "mangle": false
}, },
{},
{ {
"compress": { "compress": {
"toplevel": true "warnings": false
}
},
{
"compress": {
"toplevel": true,
"warnings": false
}, },
"mangle": { "mangle": {
"toplevel": true "toplevel": true
@@ -25,7 +33,8 @@
{ {
"compress": { "compress": {
"keep_fargs": false, "keep_fargs": false,
"passes": 3 "passes": 3,
"warnings": false
} }
} }
] ]

View File

@@ -1,4 +1,5 @@
[ {
"props": [
"$&", "$&",
"$'", "$'",
"$*", "$*",
@@ -5598,4 +5599,5 @@
"zoom", "zoom",
"zoomAndPan", "zoomAndPan",
"zoomRectScreen" "zoomRectScreen"
] ]
}

View File

@@ -1,5 +1,19 @@
exports["Compressor"] = Compressor;
exports["DefaultsError"] = DefaultsError;
exports["Dictionary"] = Dictionary; exports["Dictionary"] = Dictionary;
exports["TreeWalker"] = TreeWalker; exports["JS_Parse_Error"] = JS_Parse_Error;
exports["MAP"] = MAP;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeTransformer"] = TreeTransformer; exports["TreeTransformer"] = TreeTransformer;
exports["minify"] = minify; exports["TreeWalker"] = TreeWalker;
exports["_push_uniq"] = push_uniq; exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["merge"] = merge;
exports["parse"] = parse;
exports["push_uniq"] = push_uniq;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;
exports["SymbolDef"] = SymbolDef;

Some files were not shown because too many files have changed in this diff Show More