Compare commits

...

50 Commits

Author SHA1 Message Date
Alex Lam S.L
8b4dcd8f3e v3.0.20 2017-06-25 15:05:05 +08:00
Alex Lam S.L
285401ced8 more tests for #2158 (#2160) 2017-06-25 14:21:48 +08:00
Alex Lam S.L
9db4c42380 fix cascade & collapse on property access of constants (#2158) 2017-06-24 21:30:06 +08:00
Alex Lam S.L
94e5e00c03 refactor compute_char_frequency() (#2152)
- minimise maintenance when updating AST
- maximise code sharing between `master` & `harmony`
2017-06-23 20:05:31 +08:00
Alex Lam S.L
dc6bcaa18e synchronise mangle.properties for minify() & test/compress (#2151) 2017-06-23 15:53:13 +08:00
Alex Lam S.L
d58b184835 refactor Compressor.toplevel (#2149) 2017-06-23 13:11:40 +08:00
Alex Lam S.L
b3a57ff019 minimise reduce_vars cloning overhead (#2148) 2017-06-23 06:59:53 +08:00
Alex Lam S.L
3d5bc08185 fix reduce_vars on this (#2145)
fixes #2140
2017-06-23 04:44:57 +08:00
Alex Lam S.L
0692435f01 fix for-in loop parsing (#2144) 2017-06-23 04:14:30 +08:00
Alex Lam S.L
f67a6b0e43 v3.0.19 2017-06-22 03:24:22 +08:00
Alex Lam S.L
343ea326c2 ensure mangling works if catch reuses a scope variable (#2123)
fixes #2120
2017-06-20 02:14:05 +08:00
Alex Lam S.L
1c150c632f v3.0.18 2017-06-18 15:01:20 +08:00
Alex Lam S.L
0a0f4f5591 make defensive copies when inline (#2116)
fixes #2114
2017-06-17 14:32:37 +08:00
Alex Lam S.L
931daa85bf fix loss of context in collapse_vars & cascade (#2112)
fixes #2110
2017-06-16 21:18:43 +08:00
Alex Lam S.L
00e4f7b3c1 in-place tigten_body() (#2111)
By reducing copies of `AST_Node` arrays, we should be able to reduce:
- memory pressure
- potential bugs caused by multiple references in AST
- duplicated executions of `OPT()`
2017-06-16 19:19:54 +08:00
Alex Lam S.L
11e63bc335 correctly determine scope of AST_This (#2109)
fixes #2107
2017-06-16 14:54:46 +08:00
Alex Lam S.L
33405bb24b enforce inline scope restriction (#2106)
fixes #2105
2017-06-16 03:21:38 +08:00
Alex Lam S.L
57dc4fb32f v3.0.17 2017-06-15 18:59:37 +08:00
Alex Lam S.L
b85a358deb suppress inline of this (#2103)
fixes #2101
2017-06-15 12:14:16 +08:00
Alex Lam S.L
43697958f3 avoid intermittent test time-out failures (#2100) 2017-06-15 04:47:57 +08:00
Alex Lam S.L
3f961bbba0 compute uses_arguments correctly in figure_out_scope() (#2099)
fixes #2097
2017-06-15 03:28:26 +08:00
Alex Lam S.L
0a1e523cd5 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:00:03 +08:00
Alex Lam S.L
4231f7323e v3.0.16 2017-06-14 16:45:09 +08:00
kzc
da2de350c3 add comment about quote_style and gzip (#2092) 2017-06-14 12:23:03 +08:00
Alex Lam S.L
41beae4dd7 cache web assets between CI runs (#2089)
- skip `test/jetstream.js` for `node@0.12`
2017-06-14 11:53:10 +08:00
Ziad El Khoury Hanna
82db9188ac fix CLI parsing of --source-map content (#2088)
fixes #2082
2017-06-13 16:30:46 +08:00
Alex Lam S.L
3dc9e140e4 add Node.js 8 to Travis CI (#2086)
- explicitly terminate `test/jetstream.js` upon completion
- log verbose output from `test/benchmark.js` & `test/jetstream.js`
- remove obsolete workaround for Travis CI
2017-06-13 06:21:16 +08:00
Alex Lam S.L
fed0096556 allow expect_stdout to specify Error (#2087) 2017-06-13 04:57:26 +08:00
Alex Lam S.L
2bdc8802dd fix variable accounting in inline (#2085)
fixes #2084
2017-06-13 01:40:14 +08:00
Alex Lam S.L
5ef7cb372a suppress false positives for-in loops (#2080)
fixes #2079
2017-06-10 13:55:17 +08:00
Alex Lam S.L
4ad7b1dae4 fix portability of sandbox.run_code() on Node.js 0.1x (#2078) 2017-06-10 01:08:58 +08:00
Alex Lam S.L
9186859cb7 fix non-string parameters (#2076)
`Stream.write()` is not as versatile as `console.log()`
2017-06-10 00:11:40 +08:00
Alex Lam S.L
47c0713747 report test/ufuzz.js failures in process.stderr (#2074) 2017-06-09 15:56:28 +08:00
Alex Lam S.L
293c566d6c marshal mangle[.properties].reserved from non-Array values (#2072) 2017-06-09 04:29:12 +08:00
Alex Lam S.L
9c306406f1 fix iteration over object with inherited properties (#2068)
fixes #2055
2017-06-08 03:27:03 +08:00
Alex Lam S.L
9db0695b10 fix cascade on multi-branch evaluations (#2067)
Partially reverts #2059 as this has better coverage and performance.

fixes #2062
2017-06-07 19:52:01 +08:00
Alex Lam S.L
f2af093402 fix CLI output corruption (#2061)
Using `console.error()` & `console.log()` result in inconsistent formatting across Node.js versions.

Avoid this issue by directly writing to `process.stderr` & `process.stdout` instead.

Miscellaneous
- prettify invalid option listing
2017-06-07 04:25:32 +08:00
Alex Lam S.L
b9ad53d1ab fix inline handling of AST_Call.args (#2059) 2017-06-06 22:55:25 +08:00
Alex Lam S.L
b0eab71470 implement test/jetstream.js --debug (#2058) 2017-06-06 19:28:12 +08:00
Alex Lam S.L
3493a182b2 implement function inlining (#2053)
- empty body
- single `AST_Return`
- single `AST_SimpleStatement`
- avoid `/*#__PURE__*/`

Miscellaneous
- enhance single-use function substitution

fixes #281
2017-06-06 05:49:53 +08:00
Alex Lam S.L
27c5284d3d workaround webkit parsing error (#2056)
apply `webkit` to jetstream tests
2017-06-06 04:06:42 +08:00
Alex Lam S.L
540220b91b fix AST_Function scope invariance (#2052)
improve function name hack in `run_code()`
2017-06-04 19:27:43 +08:00
Alex Lam S.L
84634da4b5 add tests for AST_SymbolAccessor (#2049) 2017-06-03 16:08:10 +08:00
Alex Lam S.L
1743621889 clean up lib/parse.js (#2047)
- remove unused definitions
- replace `array_to_hash()`
2017-06-03 14:00:59 +08:00
kzc
f330ab743a better document behavior of unsafe_Func (#2043) 2017-06-02 12:07:17 +08:00
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
44 changed files with 2224 additions and 586 deletions

View File

@@ -4,8 +4,11 @@ node_js:
- "0.12"
- "4"
- "6"
- "8"
env:
- UGLIFYJS_TEST_ALL=1
matrix:
fast_finish: true
sudo: false
cache:
directories: tmp

View File

@@ -572,7 +572,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`.
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
@@ -615,6 +616,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `if_return` -- optimizations for if/return and if/continue
- `inline` -- embed simple functions
- `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
@@ -680,17 +683,18 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Mangle options
- `reserved` - pass an array of identifiers that should be excluded from mangling
- `reserved` (default `[]`). Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` — mangle names declared in the top level scope (disabled by
default).
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
top level scope.
- `eval` — mangle names visible in scopes where eval or with are used
(disabled by default).
- `keep_fnames` (default `false`). Pass `true` to not mangle function names.
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options).
- `keep_fnames` -- default `false`. Pass `true` to not mangle
function names. Useful for code relying on `Function.prototype.name`.
See also: the `keep_fnames` [compress option](#compress-options).
- `eval` (default `false`). Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
Examples:
@@ -762,7 +766,7 @@ can pass additional arguments that control the code output:
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
more double quotes in the string itself.
more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes

View File

@@ -30,14 +30,7 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = options[option];
var padding = "";
Object.keys(defs).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(defs[name]) ];
}).forEach(function(tokens) {
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
text.push(format_object(options[option]));
text.push("");
}
return text.join("\n");
@@ -157,7 +150,7 @@ if (program.verbose) {
}
if (program.self) {
if (program.args.length) {
console.error("WARN: Ignoring input files since --self was passed");
print_error("WARN: Ignoring input files since --self was passed");
}
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) {
@@ -187,7 +180,7 @@ function convert_ast(fn) {
function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
print_error("WARN: " + msg);
};
if (program.timings) options.timings = true;
try {
@@ -216,7 +209,7 @@ function run() {
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1];
@@ -230,17 +223,17 @@ function run() {
line = line.slice(col - limit);
col = limit;
}
console.error(line.slice(0, 80));
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);
print_error("Supported options:");
print_error(format_object(ex.defs));
}
fatal(ex);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
print(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;
@@ -256,7 +249,7 @@ function run() {
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
@@ -270,7 +263,7 @@ function run() {
fs.writeFileSync(program.output + ".map", result.map);
}
} else {
console.log(result.code);
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
@@ -278,13 +271,13 @@ function run() {
}));
}
if (result.timings) for (var phase in result.timings) {
console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
print_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);
print_error(message);
process.exit(1);
}
@@ -378,10 +371,10 @@ function parse_js(flag, constants) {
function parse_source_map() {
var parse = parse_js("sourceMap", true);
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var hasContent = options && "content" in options;
var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content);
print_error("INFO: Using input source map: " + settings.content);
settings.content = read_file(settings.content, settings.content);
}
return settings;
@@ -403,3 +396,25 @@ function to_cache(key) {
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}
function format_object(obj) {
var lines = [];
var padding = "";
Object.keys(obj).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(obj[name]) ];
}).forEach(function(tokens) {
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
return lines.join("\n");
}
function print_error(msg) {
process.stderr.write(msg);
process.stderr.write("\n");
}
function print(txt) {
process.stdout.write(txt);
process.stdout.write("\n");
}

View File

@@ -63,6 +63,7 @@ function Compressor(options, false_by_default) {
hoist_vars : false,
ie8 : false,
if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default,
keep_fargs : true,
keep_fnames : false,
@@ -123,13 +124,13 @@ function Compressor(options, false_by_default) {
};
}
var toplevel = this.options["toplevel"];
if (typeof toplevel == "string") {
this.toplevel.funcs = /funcs/.test(toplevel);
this.toplevel.vars = /vars/.test(toplevel);
} else {
this.toplevel = toplevel ? return_true : return_false;
this.toplevel.funcs = this.toplevel.vars = toplevel;
}
this.toplevel = typeof toplevel == "string" ? {
funcs: /funcs/.test(toplevel),
vars: /vars/.test(toplevel)
} : {
funcs: toplevel,
vars: toplevel
};
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
this.warnings_produced = {};
@@ -138,11 +139,11 @@ function Compressor(options, false_by_default) {
Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
toplevel: function(def) {
for (var i = 0, len = def.orig.length; i < len; i++)
exposed: function(def) {
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return false;
return true;
return true;
return false;
},
compress: function(node) {
if (this.option("expression")) {
@@ -278,11 +279,11 @@ merge(Compressor.prototype, {
var reduce_vars = rescan && compressor.option("reduce_vars");
var safe_ids = Object.create(null);
var suppressor = new TreeWalker(function(node) {
if (node instanceof AST_Symbol) {
var d = node.definition();
if (node instanceof AST_SymbolRef) d.references.push(node);
d.fixed = false;
}
if (!(node instanceof AST_Symbol)) return;
var d = node.definition();
if (!d) return;
if (node instanceof AST_SymbolRef) d.references.push(node);
d.fixed = false;
});
var tw = new TreeWalker(function(node, descend) {
node._squeezed = false;
@@ -344,7 +345,7 @@ merge(Compressor.prototype, {
}
if (node instanceof AST_Defun) {
var d = node.name.definition();
if (d.global && !compressor.toplevel(d) || safe_to_read(d)) {
if (compressor.exposed(d) || safe_to_read(d)) {
d.fixed = false;
} else {
d.fixed = node;
@@ -516,7 +517,7 @@ merge(Compressor.prototype, {
def.escaped = false;
if (def.scope.uses_eval) {
def.fixed = false;
} else if (!def.global || compressor.toplevel(def)) {
} else if (!compressor.exposed(def)) {
def.fixed = undefined;
} else {
def.fixed = false;
@@ -546,8 +547,25 @@ merge(Compressor.prototype, {
return fixed();
});
AST_SymbolRef.DEFMETHOD("is_immutable", function() {
var orig = this.definition().orig;
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda;
});
function is_lhs_read_only(lhs) {
return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda;
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression;
if (lhs instanceof AST_SymbolRef) {
if (lhs.is_immutable()) return false;
lhs = lhs.fixed_value();
}
if (!lhs) return true;
if (lhs instanceof AST_RegExp) return false;
if (lhs instanceof AST_Constant) return true;
return is_lhs_read_only(lhs);
}
return false;
}
function find_variable(compressor, name) {
@@ -667,26 +685,24 @@ merge(Compressor.prototype, {
var CHANGED, max_iter = 10;
do {
CHANGED = false;
statements = eliminate_spurious_blocks(statements);
eliminate_spurious_blocks(statements);
if (compressor.option("dead_code")) {
statements = eliminate_dead_code(statements, compressor);
eliminate_dead_code(statements, compressor);
}
if (compressor.option("if_return")) {
statements = handle_if_return(statements, compressor);
handle_if_return(statements, compressor);
}
if (compressor.sequences_limit > 0) {
statements = sequencesize(statements, compressor);
sequencesize(statements, compressor);
}
if (compressor.option("join_vars")) {
statements = join_consecutive_vars(statements, compressor);
join_consecutive_vars(statements, compressor);
}
if (compressor.option("collapse_vars")) {
statements = collapse(statements, compressor);
collapse(statements, compressor);
}
} while (CHANGED && max_iter-- > 0);
return statements;
// Search from right to left for assignment-like expressions:
// - `var a = x;`
// - `a = x;`
@@ -723,6 +739,7 @@ merge(Compressor.prototype, {
// Stop immediately if these node types are encountered
var parent = tt.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_SymbolRef && node.undeclared()
@@ -748,7 +765,7 @@ merge(Compressor.prototype, {
}
if (candidate instanceof AST_VarDef) {
var def = candidate.name.definition();
if (def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
if (def.references.length == 1 && !compressor.exposed(def)) {
return maintain_this_binding(parent, node, candidate.value);
}
return make_node(AST_Assign, candidate, {
@@ -788,7 +805,6 @@ merge(Compressor.prototype, {
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
return statements;
function extract_candidates(expr) {
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
@@ -811,7 +827,7 @@ merge(Compressor.prototype, {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
if (def.orig.length > 1
|| def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
|| def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
@@ -890,59 +906,60 @@ merge(Compressor.prototype, {
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
return statements.reduce(function(a, stat){
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement) {
CHANGED = true;
a.push.apply(a, eliminate_spurious_blocks(stat.body));
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
a.push(stat);
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else {
a.push(stat);
}
return a;
}, []);
};
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
var ret = []; // Optimized statements, build from tail to front
loop: for (var i = statements.length; --i >= 0;) {
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
switch (true) {
case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
var next = statements[i + 1];
if (in_lambda && stat instanceof AST_Return && !stat.value && !next) {
CHANGED = true;
// note, ret.length is probably always zero
// because we drop unreachable code before this
// step. nevertheless, it's good to check.
continue loop;
case stat instanceof AST_If:
statements.length--;
continue;
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
@@ -951,81 +968,71 @@ merge(Compressor.prototype, {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value)
&& !value && !stat.alternative) {
CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
ret.unshift(cond);
continue loop;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0];
ret[0] = stat.transform(compressor);
continue loop;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
&& value && !stat.alternative && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, {
value: null
});
ret[0] = stat.transform(compressor);
continue loop;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
if (compressor.option("sequences")
&& i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
&& !stat.alternative) {
CHANGED = true;
ret.push(make_node(AST_Return, ret[0], {
value: null
}).transform(compressor));
ret.unshift(stat);
continue loop;
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 2, stat.transform(compressor));
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && in_lambda && value && !stat.alternative
&& (!next || next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, next ? 2 : 1, stat.transform(compressor));
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[i - 1];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& i + 2 == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
statements.push(make_node(AST_Return, next, {
value: null
}).transform(compressor));
continue;
}
ret.unshift(stat);
break;
default:
ret.unshift(stat);
break;
}
}
return ret;
function has_multiple_if_returns(statements) {
var n = 0;
@@ -1050,6 +1057,18 @@ merge(Compressor.prototype, {
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
@@ -1059,49 +1078,52 @@ merge(Compressor.prototype, {
}
return body;
}
};
}
function eliminate_dead_code(statements, compressor) {
var has_quit = false;
var orig = statements.length;
var has_quit;
var self = compressor.self();
statements = statements.reduce(function(a, stat){
if (has_quit) {
extract_declarations_from_unreachable_code(compressor, stat, a);
} else {
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if ((stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self) || (stat instanceof AST_Continue
&& loop_body(lct) === self)) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
a.push(stat);
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
a.push(stat);
statements[n++] = stat;
}
if (aborts(stat)) has_quit = true;
} else {
statements[n++] = stat;
}
return a;
}, []);
CHANGED = statements.length != orig;
return statements;
};
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return statements;
var seq = [], ret = [];
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
ret.push(make_node(AST_SimpleStatement, body, { body: body }));
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
};
statements.forEach(function(stat){
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
@@ -1109,18 +1131,18 @@ merge(Compressor.prototype, {
if (body) merge_sequence(seq, body);
} else {
push_seq();
ret.push(stat);
statements[n++] = stat;
}
});
}
push_seq();
ret = sequencesize_2(ret, compressor);
CHANGED = ret.length != statements.length;
return ret;
};
statements.length = n;
sequencesize_2(statements, compressor);
CHANGED = statements.length != len;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
ret.pop();
n--;
var left = prev.body;
if (!(left instanceof AST_Sequence)) {
left = make_node(AST_Sequence, left, {
@@ -1130,8 +1152,9 @@ merge(Compressor.prototype, {
merge_sequence(left.expressions, right);
return left.transform(compressor);
};
var ret = [], prev = null;
statements.forEach(function(stat){
var n = 0, prev;
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_For) {
try {
@@ -1144,7 +1167,7 @@ merge(Compressor.prototype, {
}
else if (!stat.init) {
stat.init = prev.body.drop_side_effect_free(compressor);
ret.pop();
n--;
}
} catch(ex) {
if (ex !== cons_seq) throw ex;
@@ -1166,15 +1189,16 @@ merge(Compressor.prototype, {
stat.expression = cons_seq(stat.expression);
}
}
ret.push(stat);
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
});
return ret;
};
}
statements.length = n;
}
function join_consecutive_vars(statements, compressor) {
var prev = null;
return statements.reduce(function(a, stat){
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
@@ -1183,35 +1207,19 @@ merge(Compressor.prototype, {
&& prev instanceof AST_Var
&& (!stat.init || stat.init.TYPE == prev.TYPE)) {
CHANGED = true;
a.pop();
if (stat.init) {
stat.init.definitions = prev.definitions.concat(stat.init.definitions);
} else {
stat.init = prev;
}
a.push(stat);
prev = stat;
statements[j] = stat;
}
else {
prev = stat;
a.push(stat);
statements[++j] = stat;
}
return a;
}, []);
};
};
function extract_functions_from_statement_array(statements) {
var funs = [];
for (var i = statements.length - 1; i >= 0; --i) {
var stat = statements[i];
if (stat instanceof AST_Defun) {
statements.splice(i, 1);
funs.unshift(stat);
}
}
return funs;
statements.length = j + 1;
};
}
function extract_declarations_from_unreachable_code(compressor, stat, target) {
@@ -1296,6 +1304,7 @@ merge(Compressor.prototype, {
def(AST_SymbolRef, function(pure_getters) {
if (this.is_undefined) return true;
if (!is_strict(pure_getters)) return false;
if (this.is_immutable()) return false;
var fixed = this.fixed_value();
return !fixed || fixed._throw_on_access(pure_getters);
});
@@ -1418,7 +1427,7 @@ merge(Compressor.prototype, {
});
if (value && typeof value == "object") {
var props = [];
for (var key in value) {
for (var key in value) if (HOP(value, key)) {
props.push(make_node(AST_ObjectKeyVal, orig, {
key: key,
value: to_node(value[key], orig)
@@ -1665,6 +1674,63 @@ merge(Compressor.prototype, {
}
throw def;
});
var object_fns = [
'constructor',
'toString',
'valueOf',
];
var native_fns = {
Array: makePredicate([
'indexOf',
'join',
'lastIndexOf',
'slice',
].concat(object_fns)),
Boolean: makePredicate(object_fns),
Number: makePredicate([
'toExponential',
'toFixed',
'toPrecision',
].concat(object_fns)),
RegExp: makePredicate([
'test',
].concat(object_fns)),
String: makePredicate([
'charAt',
'charCodeAt',
'concat',
'indexOf',
'italics',
'lastIndexOf',
'match',
'replace',
'search',
'slice',
'split',
'substr',
'substring',
'trim',
].concat(object_fns)),
};
def(AST_Call, function(compressor){
var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property;
if (key instanceof AST_Node) {
key = ev(key, compressor);
}
var val = ev(exp.expression, compressor);
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
return val[key].apply(val, this.args.map(function(arg) {
return ev(arg, compressor);
}));
}
}
throw def;
});
def(AST_New, function(compressor){
throw def;
});
})(function(node, func){
node.DEFMETHOD("_eval", func);
});
@@ -1931,12 +1997,12 @@ merge(Compressor.prototype, {
});
OPT(AST_Block, function(self, compressor){
self.body = tighten_body(self.body, compressor);
tighten_body(self.body, compressor);
return self;
});
OPT(AST_BlockStatement, function(self, compressor){
self.body = tighten_body(self.body, compressor);
tighten_body(self.body, compressor);
switch (self.body.length) {
case 1: return self.body[0];
case 0: return make_node(AST_EmptyStatement, self);
@@ -2843,7 +2909,7 @@ merge(Compressor.prototype, {
});
OPT(AST_Try, function(self, compressor){
self.body = tighten_body(self.body, compressor);
tighten_body(self.body, compressor);
if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
if (all(self.body, is_empty)) {
var body = [];
@@ -2886,33 +2952,18 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){
var exp = self.expression;
if (compressor.option("reduce_vars")
&& exp instanceof AST_SymbolRef) {
var def = exp.definition();
var fixed = exp.fixed_value();
if (fixed instanceof AST_Defun) {
def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
}
if (fixed instanceof AST_Function) {
exp = fixed;
if (compressor.option("unused")
&& def.references.length == 1
&& !(def.scope.uses_arguments
&& def.orig[0] instanceof AST_SymbolFunarg)
&& !def.scope.uses_eval
&& compressor.find_parent(AST_Scope) === def.scope) {
self.expression = exp;
}
}
}
var fn = exp;
if (compressor.option("unused")
&& exp instanceof AST_Function
&& !exp.uses_arguments
&& !exp.uses_eval) {
&& (fn instanceof AST_Function
|| compressor.option("reduce_vars")
&& fn instanceof AST_SymbolRef
&& (fn = fn.fixed_value()) instanceof AST_Function)
&& !fn.uses_arguments
&& !fn.uses_eval) {
var pos = 0, last = 0;
for (var i = 0, len = self.args.length; i < len; i++) {
var trim = i >= exp.argnames.length;
if (trim || exp.argnames[i].__unused) {
var trim = i >= fn.argnames.length;
if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor);
if (node) {
self.args[pos++] = node;
@@ -3081,7 +3132,9 @@ merge(Compressor.prototype, {
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
ast.mangle_names();
base54.reset();
ast.compute_char_frequency(mangle);
ast.mangle_names(mangle);
var fun;
ast.walk(new TreeWalker(function(node) {
if (fun) return true;
@@ -3113,12 +3166,80 @@ merge(Compressor.prototype, {
}
}
}
var stat = fn instanceof AST_Function && fn.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat.value;
if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
}
}
if (exp instanceof AST_Function) {
if (exp.body[0] instanceof AST_Return) {
var value = exp.body[0].value;
if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
if (compressor.option("inline")
&& !exp.name
&& exp.body.length == 1
&& !exp.uses_arguments
&& !exp.uses_eval
&& !self.has_pure_annotation(compressor)) {
var value;
if (stat instanceof AST_Return) {
value = stat.value.clone(true);
} else if (stat instanceof AST_SimpleStatement) {
value = make_node(AST_UnaryPrefix, stat, {
operator: "void",
expression: stat.body.clone(true)
});
}
if (value) {
var fn = exp.clone();
fn.argnames = [];
fn.body = [];
if (exp.argnames.length > 0) {
fn.body.push(make_node(AST_Var, self, {
definitions: exp.argnames.map(function(sym, i) {
var arg = self.args[i];
return make_node(AST_VarDef, sym, {
name: sym,
value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
});
})
}));
}
if (self.args.length > exp.argnames.length) {
fn.body.push(make_node(AST_SimpleStatement, self, {
body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
return node.clone(true);
}))
}));
}
fn.body.push(make_node(AST_Return, self, {
value: value
}));
var body = fn.transform(compressor).body;
if (body.length == 0) return make_node(AST_Undefined, self);
if (body.length == 1 && body[0] instanceof AST_Return) {
value = body[0].value;
if (!value) return make_node(AST_Undefined, self);
var tw = new TreeWalker(function(node) {
if (value === self) return true;
if (node instanceof AST_SymbolRef) {
var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = self;
return true;
}
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = self;
return true;
}
});
value.walk(tw);
if (value !== self) value = best_of(compressor, value, self);
} else {
value = self;
}
if (value !== self) return value;
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
@@ -3144,6 +3265,11 @@ merge(Compressor.prototype, {
&& is_iife_call(self)) {
return self.negate(compressor, true);
}
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self;
});
@@ -3218,6 +3344,7 @@ merge(Compressor.prototype, {
continue;
}
var parent = null, field;
expressions[j] = cdr = cdr.clone();
while (true) {
if (cdr.equivalent_to(left)) {
var car = expressions[i];
@@ -3246,15 +3373,18 @@ merge(Compressor.prototype, {
field = "left";
}
} else if (cdr instanceof AST_Call
&& !(left instanceof AST_PropAccess && cdr.expression.equivalent_to(left))
|| cdr instanceof AST_PropAccess
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
field = "expression";
} else if (cdr instanceof AST_Conditional) {
field = "condition";
} else {
expressions[++i] = expressions[j];
break;
}
parent = cdr;
cdr = cdr[field];
cdr = cdr[field] = cdr[field].clone();
}
}
end = i;
@@ -3774,12 +3904,22 @@ merge(Compressor.prototype, {
return make_node(AST_Infinity, self).optimize(compressor);
}
}
if (compressor.option("evaluate")
&& compressor.option("reduce_vars")
if (compressor.option("reduce_vars")
&& is_lhs(self, compressor.parent()) !== self) {
var d = self.definition();
var fixed = self.fixed_value();
if (fixed) {
if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
}
if (compressor.option("unused")
&& fixed instanceof AST_Function
&& d.references.length == 1
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope) {
return fixed.clone(true);
}
if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor);
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
@@ -3800,7 +3940,7 @@ merge(Compressor.prototype, {
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && (!d.global || compressor.toplevel(d))) {
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;

View File

@@ -86,7 +86,7 @@ function minify(files, options) {
}
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
for (var name in files) if (HOP(files, name)) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
@@ -134,7 +134,7 @@ function minify(files, options) {
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) {
} else for (var name in files) if (HOP(files, name)) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}

View File

@@ -70,6 +70,7 @@ function OutputStream(options) {
semicolons : true,
shebang : true,
source_map : null,
webkit : false,
width : 80,
wrap_iife : false,
}, true);
@@ -500,6 +501,7 @@ function OutputStream(options) {
use_asm = prev_use_asm;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);
@@ -597,6 +599,13 @@ function OutputStream(options) {
return true;
}
if (output.option('webkit')) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
return true;
}
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
@@ -1234,9 +1243,8 @@ function OutputStream(options) {
});
else output.print("{}");
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
var key = self.key;
var quote = self.quote;
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
@@ -1253,20 +1261,24 @@ function OutputStream(options) {
} else {
output.print_string(key, quote);
}
}
DEFPRINT(AST_ObjectKeyVal, function(self, output){
print_property_name(self.key, self.quote, output);
output.colon();
self.value.print(output);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
output.print("set");
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
output.print(type);
output.space();
self.key.print(output);
self.value._do_print(output, true);
print_property_name(this.key.name, this.quote, output);
this.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", output);
});
DEFPRINT(AST_ObjectGetter, function(self, output){
output.print("get");
output.space();
self.key.print(output);
self.value._do_print(output, true);
self._print_getter_setter("get", output);
});
DEFPRINT(AST_Symbol, function(self, output){
var def = self.definition();

View File

@@ -115,8 +115,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
/* -----[ Tokenizer ]----- */
// regexps adapted from http://xregexp.com/plugins/#unicode
@@ -684,9 +682,7 @@ var PRECEDENCE = (function(a, ret){
{}
);
var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
/* -----[ Parser ]----- */
@@ -1014,8 +1010,12 @@ function parse($TEXT, options) {
? (next(), var_(true))
: expression(true, true);
if (is("operator", "in")) {
if (init instanceof AST_Var && init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop");
if (init instanceof AST_Var) {
if (init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
} else if (!is_assignable(init)) {
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
}
next();
return for_in(init);
}
@@ -1222,7 +1222,6 @@ function parse($TEXT, options) {
var tok = S.token, ret;
switch (tok.type) {
case "name":
case "keyword":
ret = _make_symbol(AST_SymbolRef);
break;
case "num":
@@ -1252,13 +1251,6 @@ function parse($TEXT, options) {
break;
}
break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
}
next();
return ret;
@@ -1292,7 +1284,7 @@ function parse($TEXT, options) {
func.end = prev();
return subscripts(func, allow_calls);
}
if (ATOMIC_START_TOKEN[S.token.type]) {
if (ATOMIC_START_TOKEN(S.token.type)) {
return subscripts(as_atom_node(), allow_calls);
}
unexpected();

View File

@@ -78,7 +78,8 @@ function mangle_properties(ast, options) {
reserved: null,
});
var reserved = options.reserved || [];
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var cache = options.cache;

View File

@@ -79,7 +79,7 @@ SymbolDef.prototype = {
if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
@@ -87,6 +87,9 @@ SymbolDef.prototype = {
cache.set(this.name, this.mangled_name);
}
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
};
@@ -197,16 +200,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
}
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
}
node.thedef = sym;
node.reference(options);
return true;
}
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
});
self.walk(tw);
@@ -349,7 +361,8 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
var def = this.definition();
return !def || def.unmangleable(options);
});
// labels are always mangleable
@@ -377,13 +390,15 @@ AST_Symbol.DEFMETHOD("global", function(){
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
options = defaults(options, {
eval : false,
ie8 : false,
keep_fnames : false,
reserved : [],
toplevel : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
return options;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
@@ -446,103 +461,69 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());
else if (node instanceof AST_Return)
base54.consider("return");
else if (node instanceof AST_Throw)
base54.consider("throw");
else if (node instanceof AST_Continue)
base54.consider("continue");
else if (node instanceof AST_Break)
base54.consider("break");
else if (node instanceof AST_Debugger)
base54.consider("debugger");
else if (node instanceof AST_Directive)
base54.consider(node.value);
else if (node instanceof AST_While)
base54.consider("while");
else if (node instanceof AST_Do)
base54.consider("do while");
else if (node instanceof AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
}
else if (node instanceof AST_Var)
base54.consider("var");
else if (node instanceof AST_Lambda)
base54.consider("function");
else if (node instanceof AST_For)
base54.consider("for");
else if (node instanceof AST_ForIn)
base54.consider("for in");
else if (node instanceof AST_Switch)
base54.consider("switch");
else if (node instanceof AST_Case)
base54.consider("case");
else if (node instanceof AST_Default)
base54.consider("default");
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + node.key);
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + node.key);
else if (node instanceof AST_ObjectKeyVal)
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
};
base54.consider(this.print_to_string(), 1);
} finally {
AST_Node.prototype.print = AST_Node.prototype._print;
}
base54.sort();
function skip_string(node) {
if (node instanceof AST_String) {
base54.consider(node.value, -1);
} else if (node instanceof AST_Conditional) {
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
}
}
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
var digits = "0123456789".split("");
var chars, frequency;
function reset() {
frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
chars.forEach(function(ch){ frequency[ch] = 0 });
leading.forEach(function(ch) {
frequency[ch] = 0;
});
digits.forEach(function(ch) {
frequency[ch] = 0;
});
}
base54.consider = function(str){
base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i);
if (code in frequency) ++frequency[code];
frequency[str[i]] += delta;
}
};
function compare(a, b) {
return frequency[b] - frequency[a];
}
base54.sort = function() {
chars = mergeSort(chars, function(a, b){
if (is_digit(a) && !is_digit(b)) return 1;
if (is_digit(b) && !is_digit(a)) return -1;
return frequency[b] - frequency[a];
});
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
};
base54.reset = reset;
reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += String.fromCharCode(chars[num % base]);
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);

View File

@@ -43,13 +43,6 @@
"use strict";
function array_to_hash(a) {
var ret = Object.create(null);
for (var i = 0; i < a.length; ++i)
ret[a[i]] = true;
return ret;
};
function slice(a, start) {
return Array.prototype.slice.call(a, start || 0);
};

View File

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

View File

@@ -4,6 +4,7 @@
"use strict";
var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork;
var args = process.argv.slice(2);
if (!args.length) {
@@ -52,7 +53,8 @@ urls.forEach(function(url) {
output: 0,
log: ""
};
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
fetch(url, function(err, res) {
if (err) throw err;
var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.on("data", function(data) {
results[url].input += data.length;

View File

@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
function f3(x) {
var b = x.prop;
sideeffect1();
return b + -9;
return b + (function() { return -9; })();
}
}
}

View File

@@ -751,12 +751,12 @@ issue_1583: {
expect: {
function m(t) {
(function(e) {
t = (function() {
return (function(a) {
return a;
})(function(a) {});
})();
})();
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
}
@@ -1108,3 +1108,47 @@ var_catch_toplevel: {
}();
}
}
issue_2105: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
!void function() {
var quux = function() {
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
};
}().prop();
}
expect_stdout: "PASS"
}

View File

@@ -644,6 +644,7 @@ unsafe_prototype_function: {
call_args: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
@@ -665,6 +666,7 @@ call_args: {
call_args_drop_param: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
toplevel: true,
@@ -780,13 +782,15 @@ unsafe_charAt_noop: {
input: {
console.log(
s.charAt(0),
"string".charAt(x)
"string".charAt(x),
(typeof x).charAt()
);
}
expect: {
console.log(
s.charAt(0),
"string".charAt(x)
"string".charAt(x),
(typeof x)[0]
);
}
}
@@ -1037,3 +1041,31 @@ issue_1964_2: {
}
expect_stdout: "b"
}
array_slice_index: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([1,2,3].slice(1)[1]);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}
string_charCodeAt: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("foo".charCodeAt("bar".length));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}

View File

@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
join_vars : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
join_vars : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
}
input: {
(function(a) {
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
evaluate: true,
cascade: true,
unused: true,
inline: true,
}
input: {
function greeter() {
@@ -261,9 +265,248 @@ issue_203: {
}
expect: {
var m = {};
var fn = Function("a", "b", "b.exports=42");
var fn = Function("n", "o", "o.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
expect_stdout: "42"
}
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
!function(c) {
c = 1 + c;
var c = 0;
function f14(a_1) {
if (c = 1 + c, 0 !== 23..toString())
c = 1 + c, a_1 && (a_1[0] = 0);
}
f14();
}(-1);
}();
console.log(c);
}
expect: {
var c = 0;
!function(c) {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(-1),
console.log(c);
}
expect_stdout: "0"
}
issue_2097: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}
f(1);
}
expect: {
!function() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}(1);
}
expect_stdout: "1"
}
issue_2101: {
options = {
inline: true,
}
input: {
a = {};
console.log(function() {
return function() {
return this.a;
}();
}() === function() {
return a;
}());
}
expect: {
a = {};
console.log(function() {
return this.a;
}() === a);
}
expect_stdout: "true"
}
inner_ref: {
options = {
inline: true,
unused: true,
}
input: {
console.log(function(a) {
return function() {
return a;
}();
}(1), function(a) {
return function(a) {
return a;
}();
}(2));
}
expect: {
console.log(function(a) {
return a;
}(1), function(a) {
return a;
}());
}
expect_stdout: "1 undefined"
}
issue_2107: {
options = {
cascade: true,
collapse_vars: true,
inline: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
c++;
}(c++ + new function() {
this.a = 0;
var a = (c = c + 1) + (c = 1 + c);
return c++ + a;
}());
console.log(c);
}
expect: {
var c = 0;
c++, new function() {
this.a = 0, c = 1 + (c += 1), c++;
}(), c++, console.log(c);
}
expect_stdout: "5"
}
issue_2114_1: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
!function() {
0;
}((c += 1, c = 1 + c, function() {
var b = void (b && (b.b += (c += 1, 0)));
}()));
console.log(c);
}
expect_stdout: "2"
}
issue_2114_2: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
passes: 2,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -10,9 +10,9 @@ issue_1321_no_debug: {
}
expect: {
var x = {};
x.b = 1;
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
x.o = 1;
x["a"] = 2 * x.o;
console.log(x.o, x["a"]);
}
expect_stdout: true
}
@@ -30,9 +30,9 @@ issue_1321_debug: {
}
expect: {
var x = {};
x.a = 1;
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
x.o = 1;
x["_$foo$_"] = 2 * x.o;
console.log(x.o, x["_$foo$_"]);
}
expect_stdout: true
}
@@ -49,9 +49,9 @@ issue_1321_with_quoted: {
}
expect: {
var x = {};
x.a = 1;
x["b"] = 2 * x.a;
console.log(x.a, x["b"]);
x.o = 1;
x["x"] = 2 * x.o;
console.log(x.o, x["x"]);
}
expect_stdout: true
}

View File

@@ -82,7 +82,7 @@ numeric_literal: {
' 42: 2,',
' "42": 3,',
' 37: 4,',
' a: 5,',
' o: 5,',
' 1e42: 6,',
' b: 7,',
' "1e+42": 8',
@@ -92,7 +92,7 @@ numeric_literal: {
'',
'console.log(obj[42], obj["42"]);',
'',
'console.log(obj[37], obj["a"], obj[37], obj["37"]);',
'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
'',
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
]
@@ -173,32 +173,32 @@ identifier: {
}
expect: {
var obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
f: 6,
g: 7,
h: 8,
i: 9,
j: 10,
k: 11,
l: 12,
m: 13,
n: 14,
o: 15,
p: 16,
q: 17,
r: 18,
s: 19,
t: 20,
u: 21,
v: 22,
w: 23,
x: 24,
y: 25,
z: 26,
e: 1,
t: 2,
n: 3,
a: 4,
i: 5,
o: 6,
r: 7,
l: 8,
s: 9,
c: 10,
f: 11,
u: 12,
d: 13,
h: 14,
p: 15,
b: 16,
v: 17,
w: 18,
y: 19,
g: 20,
m: 21,
k: 22,
x: 23,
j: 24,
z: 25,
q: 26,
A: 27,
B: 28,
C: 29,
@@ -229,11 +229,11 @@ identifier: {
Z: 54,
$: 55,
_: 56,
aa: 57,
ba: 58,
ca: 59,
da: 60,
ea: 61,
ee: 57,
te: 58,
ne: 59,
ae: 60,
ie: 61,
};
}
}

View File

@@ -1,6 +1,7 @@
unary_prefix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}

483
test/compress/issue-281.js Normal file
View File

@@ -0,0 +1,483 @@
collapse_vars_constants: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f1(x) {
var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
return b + (function() { return d - a * e - c; })();
}
function f2(x) {
var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
return b + (function() { return -a * e - c; })();
}
}
expect: {
function f1(x) {
var b = x.prop, d = sideeffect1(), e = sideeffect2();
return b + (d - 4 * e - 5);
}
function f2(x) {
var b = x.prop;
sideeffect1();
return b + (-4 * sideeffect2() - 5);
}
}
}
modified: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f5(1));
}
expect: {
function f5(b) {
var a = b;
return b++ + a;
}
console.log(f5(1));
}
expect_stdout: "2"
}
ref_scope: {
options = {
collapse_vars: true,
inline: 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 a + b;
}());
}
expect_stdout: true
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
inline: true,
unsafe: false,
unused: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}(1)());
}
expect: {
var a, c;
console.log(a ? b : c ? d : void 0);
}
expect_stdout: true
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
t ? console.log(true) : console.log(false), void console.log("something");
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(void console.log("test"))()()();'
}
negate_iife_issue_1073: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
sequences: true,
unused: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new function() {
this.x = 7,
console.log(this);
}();
}
expect_stdout: true
}
issue_1288_side_effects: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
unused: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || (x = {});
y;
}
}
inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
for (b in (function() {
return x(a, b, c);
})()) {
var c = 3, d = 4;
x(a, b, c, d);
}
x(a, b, c, d);
}
}
expect: {
function f() {
var a = 1, b = 2;
for (b in x(1, b, c)) {
var c = 3, d = 4;
x(1, b, c, d);
}
x(1, b, c, d);
}
}
}
issue_1595_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
g(3);
}
}
issue_1758: {
options = {
inline: true,
sequences: true,
side_effects: true,
}
input: {
console.log(function(c) {
var undefined = 42;
return function() {
c--;
c--, c.toString();
return;
}();
}());
}
expect: {
console.log(function(c) {
var undefined = 42;
return c--, c--, void c.toString();
}());
}
expect_stdout: "undefined"
}
wrap_iife: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
}
wrap_iife_in_expression: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=bar();'
}
wrap_iife_in_return_call: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(void console.log("test"))();'
}
pure_annotation: {
options = {
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function() {
console.log("hello");
}());
}
expect_exact: ""
}
drop_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
!function() {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}
keep_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: true,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
!function(a_1) {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}

View File

@@ -1,37 +1,41 @@
dont_reuse_prop: {
mangle_props = {
regex: /asd/
};
}
input: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.a = 123;
obj.asd = 256;
console.log(obj.a);
}
expect: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.a = 123;
obj.b = 256;
console.log(obj.a);
}
expect_stdout: "123"
}
unmangleable_props_should_always_be_reserved: {
mangle_props = {
regex: /asd/
};
}
input: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.asd = 256;
obj.a = 123;
console.log(obj.a);
}
expect: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.b = 256;
obj.a = 123;
console.log(obj.a);
}
expect_stdout: "123"
}

View File

@@ -22,7 +22,8 @@ negate_iife_1_off: {
negate_iife_2: {
options = {
negate_iife: true
inline: true,
negate_iife: true,
};
input: {
(function(){ return {} })().x = 10;
@@ -32,6 +33,7 @@ negate_iife_2: {
negate_iife_2_side_effects: {
options = {
inline: true,
negate_iife: true,
side_effects: true,
}
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
}
input: {
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: false,
}
input: {

View File

@@ -1,4 +1,4 @@
eval_let: {
eval_let_6: {
input: {
eval("let a;");
console.log();
@@ -10,3 +10,29 @@ eval_let: {
expect_stdout: ""
node_version: ">=6"
}
eval_let_4: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode")
node_version: "4"
}
eval_let_0: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Unexpected identifier")
node_version: "<=0.12"
}

View File

@@ -135,11 +135,11 @@ mangle_properties: {
a['run']({color: "blue", foo: "baz"});
}
expect: {
a["a"] = "bar";
a.b = "red";
x = {c: 10};
a.d(x.c, a.a);
a['d']({b: "blue", a: "baz"});
a["o"] = "bar";
a.a = "red";
x = {r: 10};
a.b(x.r, a.o);
a['b']({a: "blue", o: "baz"});
}
}
@@ -177,16 +177,16 @@ mangle_unquoted_properties: {
function f1() {
a["foo"] = "bar";
a.color = "red";
a.b = 2;
x = {"bar": 10, c: 7};
a.c = 9;
a.o = 2;
x = {"bar": 10, f: 7};
a.f = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, c: 7};
a.c = 9;
a.b = 3;
x = {bar: 10, f: 7};
a.f = 9;
a.o = 3;
}
}
}
@@ -555,3 +555,105 @@ native_prototype: {
"".indexOf.call(e, "bar");
}
}
accessor_boolean: {
input: {
var a = 1;
var b = {
get true() {
return a;
},
set false(c) {
a = c;
}
};
console.log(b.true, b.false = 2, b.true);
}
expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);'
expect_stdout: "1 2 2"
}
accessor_get_set: {
input: {
var a = 1;
var b = {
get set() {
return a;
},
set get(c) {
a = c;
}
};
console.log(b.set, b.get = 2, b.set);
}
expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);'
expect_stdout: "1 2 2"
}
accessor_null_undefined: {
input: {
var a = 1;
var b = {
get null() {
return a;
},
set undefined(c) {
a = c;
}
};
console.log(b.null, b.undefined = 2, b.null);
}
expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);'
expect_stdout: "1 2 2"
}
accessor_number: {
input: {
var a = 1;
var b = {
get 42() {
return a;
},
set 42(c) {
a = c;
}
};
console.log(b[42], b[42] = 2, b[42]);
}
expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);'
expect_stdout: "1 2 2"
}
accessor_string: {
input: {
var a = 1;
var b = {
get "a-b"() {
return a;
},
set "a-b"(c) {
a = c;
}
};
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
}
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
expect_stdout: "1 2 2"
}
accessor_this: {
input: {
var a = 1;
var b = {
get this() {
return a;
},
set this(c) {
a = c;
}
};
console.log(b.this, b.this = 2, b.this);
}
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
expect_stdout: "1 2 2"
}

View File

@@ -178,3 +178,210 @@ impure_getter_2: {
}
expect: {}
}
issue_2110_1: {
options = {
cascade: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
return f.g = function() {
return this;
}, f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
issue_2110_2: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
f.g = function() {
return this;
};
return f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
set_immutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_3: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: true
}
set_immutable_4: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
if (a.foo += "") console.log("PASS");
else console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
set_mutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
(a.foo += "") ? console.log("PASS") : console.log("FAIL");
}();
}
expect_stdout: "PASS"
}

View File

@@ -2,6 +2,7 @@ reduce_vars: {
options = {
conditionals : true,
evaluate : true,
inline : true,
global_defs : {
C : 0
},
@@ -1032,6 +1033,7 @@ defun_inline_2: {
defun_inline_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
@@ -1054,6 +1056,7 @@ defun_inline_3: {
defun_call: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1080,6 +1083,7 @@ defun_call: {
defun_redefine: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1112,6 +1116,7 @@ defun_redefine: {
func_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1138,6 +1143,7 @@ func_inline: {
func_modified: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1311,19 +1317,47 @@ iife_func_side_effects: {
unused: true,
}
input: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) {
return b();
function y() {
console.log("FAIL");
}
return y + b();
})(x(), function() {
return y();
}, z());
}
expect: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) {
return function() {
return y();
}();
})(x(), 0, z());
console.log("FAIL");
} + b();
})(x(), function() {
return y();
}, z());
}
expect_stdout: [
"x",
"z",
"y",
]
}
issue_1595_1: {
@@ -1687,6 +1721,7 @@ redefine_arguments_1: {
redefine_arguments_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1723,6 +1758,7 @@ redefine_arguments_2: {
redefine_arguments_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -1799,6 +1835,7 @@ redefine_farg_1: {
redefine_farg_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1835,6 +1872,7 @@ redefine_farg_2: {
redefine_farg_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -2537,3 +2575,28 @@ accessor: {
}
expect_stdout: "1 1"
}
for_in_prop: {
options = {
reduce_vars: true,
}
input: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect_stdout: "1"
}

14
test/compress/sandbox.js Normal file
View File

@@ -0,0 +1,14 @@
console_log: {
input: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect_stdout: [
"%% %s",
"% %s",
]
}

View File

@@ -255,3 +255,73 @@ issue_1586_2: {
}
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
}
issue_2120_1: {
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}

View File

@@ -710,3 +710,23 @@ issue_27: {
})(jQuery);
}
}
issue_2062: {
options = {
booleans: true,
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
var a = 1;
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
console.log(a);
}
expect: {
var a = 1;
a || (a++, a--), a++, --a && a.var;
console.log(a);
}
expect_stdout: "1"
}

31
test/fetch.js Normal file
View File

@@ -0,0 +1,31 @@
var fs = require("fs");
var path = require("path");
try {
fs.mkdirSync("./tmp");
} catch (e) {
if (e.code != "EEXIST") throw e;
}
function local(url) {
return path.join("./tmp", encodeURIComponent(url));
}
function read(url) {
return fs.createReadStream(local(url));
}
module.exports = function(url, callback) {
var result = read(url);
result.on("error", function(e) {
if (e.code != "ENOENT") return callback(e);
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
if (res.statusCode !== 200) return callback(res);
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
callback(null, read(url));
}));
});
}).on("open", function() {
callback(null, result);
});
};

View File

@@ -0,0 +1,4 @@
var a, b = [1, 2];
for (1, 2, a in b) {
console.log(a, b[a]);
}

View File

@@ -0,0 +1,4 @@
var c = [1, 2];
for (var a, b in c) {
console.log(a, c[a]);
}

View File

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

View File

@@ -0,0 +1 @@
{"version": 3,"sources": ["index.js"],"mappings": ";"}

View File

@@ -3,7 +3,7 @@
"use strict";
var site = "http://browserbench.org/JetStream/";
var site = "http://browserbench.org/JetStream";
if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
@@ -11,45 +11,69 @@ if (typeof phantom == "undefined") {
stream._handle.setBlocking(true);
});
var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {
args.splice(debug, 1);
debug = true;
} else {
debug = false;
}
if (!args.length) {
args.push("-mc");
args.push("-mcb", "beautify=false,webkit");
}
args.push("--timings");
var child_process = require("child_process");
try {
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var fetch = require("./fetch");
var http = require("http");
var server = http.createServer(function(request, response) {
request.resume();
var url = decodeURIComponent(request.url.slice(1));
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
http.get(url, function(res) {
res.pipe(uglifyjs.stdin);
});
}).listen().on("listening", function() {
var phantomjs = require("phantomjs-prebuilt");
var program = phantomjs.exec(process.argv[1], server.address().port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
var url = site + request.url;
fetch(url, function(err, res) {
if (err) throw err;
response.writeHead(200, {
"Content-Type": {
css: "text/css",
js: "application/javascript",
png: "image/png"
}[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
});
if (/\.js$/.test(url)) {
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
res.pipe(uglifyjs.stdin);
} else {
res.pipe(response);
}
});
}).listen();
server.on("listening", function() {
var port = server.address().port;
if (debug) {
console.log("http://localhost:" + port + "/");
} else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
if (error) throw error;
var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
process.exit(0);
});
});
}
});
server.timeout = 0;
} else {
@@ -63,10 +87,6 @@ if (typeof phantom == "undefined") {
phantom.exit(1);
};
var url = "http://localhost:" + require("system").args[1] + "/";
page.onResourceRequested = function(requestData, networkRequest) {
if (/\.js$/.test(requestData.url))
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
}
page.onConsoleMessage = function(msg) {
if (/Error:/i.test(msg)) {
console.error(msg);
@@ -77,8 +97,8 @@ if (typeof phantom == "undefined") {
phantom.exit();
}
};
page.open(site, function(status) {
if (status != "success") phantomjs.exit(1);
page.open(url, function(status) {
if (status != "success") phantom.exit(1);
page.evaluate(function() {
JetStream.switchToQuick();
JetStream.start();

View File

@@ -9,7 +9,7 @@ function read(path) {
describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) {
this.timeout(15000);
this.timeout(30000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
@@ -77,6 +77,23 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("should not consider source map file content as source map file name (issue #2082)", function (done) {
var command = [
uglifyjscmd,
"test/input/issue-2082/sample.js",
"--source-map", "content=test/input/issue-2082/sample.js.map",
"--source-map", "url=inline",
].join(" ");
exec(command, function (err, stdout, stderr) {
if (err) throw err;
var stderrLines = stderr.split('\n');
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map');
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
done();
});
});
it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
@@ -501,6 +518,36 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_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/for-in_1.js:2,5",
"for (1, 2, a in b) {",
" ^",
"ERROR: Invalid left-hand side in for..in loop"
].join("\n"));
done();
});
});
it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_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/for-in_2.js:2,5",
"for (var a, b in c) {",
" ^",
"ERROR: Only one variable declaration allowed in for..in loop"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
uglifyjscmd,
@@ -542,7 +589,27 @@ describe("bin/uglifyjs", function () {
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);
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should work with --mangle reserved=[]", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n');
done();
});
});
it("Should work with --mangle reserved=false", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n');
done();
});
});

View File

@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
exec(command, function(err, stdout) {
if (err) throw err;
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');
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
done();
});
});

View File

@@ -2,29 +2,37 @@ var Uglify = require('../../');
var assert = require("assert");
describe("let", function() {
it("Should not produce `let` as a variable name in mangle", function(done) {
it("Should not produce reserved keywords as variable name in mangle", function(done) {
this.timeout(10000);
// Produce a lot of variables in a function and run it through mangle.
var s = '"use strict"; function foo() {';
for (var i = 0; i < 21000; ++i) {
var s = '"dddddeeeeelllllooooottttt"; function foo() {';
for (var i = 0; i < 18000; i++) {
s += "var v" + i + "=0;";
}
s += '}';
var result = Uglify.minify(s, {compress: false});
// Verify that select keywords and reserved keywords not produced
assert.strictEqual(result.code.indexOf("var let="), -1);
assert.strictEqual(result.code.indexOf("var do="), -1);
assert.strictEqual(result.code.indexOf("var var="), -1);
[
"do",
"let",
"var",
].forEach(function(name) {
assert.strictEqual(result.code.indexOf("var " + name + "="), -1);
});
// Verify that the variable names that appeared immediately before
// and after the erroneously generated `let` variable name still exist
// and after the erroneously generated variable name still exist
// to show the test generated enough symbols.
assert(result.code.indexOf("var ket=") >= 0);
assert(result.code.indexOf("var met=") >= 0);
[
"to", "eo",
"eet", "fet",
"rar", "oar",
].forEach(function(name) {
assert.ok(result.code.indexOf("var " + name + "=") >= 0);
});
done();
});
});

View File

@@ -13,6 +13,13 @@ describe("minify", function() {
assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
});
it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)";
var result = Uglify.minify(files);
assert.strictEqual(result.code, "alert(2);");
});
describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -106,7 +113,7 @@ describe("minify", function() {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
@@ -207,5 +214,17 @@ describe("minify", function() {
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
});
it("should skip inherited properties", function() {
var foo = Object.create({ skip: this });
foo.bar = 42;
var result = Uglify.minify("alert(FOO);", {
compress: {
global_defs: {
FOO: foo
}
}
});
assert.strictEqual(result.code, "alert({bar:42});");
});
});
});

View File

@@ -1,23 +1,20 @@
var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) {
var id = setInterval(function() {
process.stdout.write("\0");
}, 5 * 60 * 1000);
spawn(command, args, {
stdio: "ignore"
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
[
"-b",
"-b bracketize",
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
});
});
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000);
it("Should install phantomjs-prebuilt", function(done) {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
});
[
"-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
@@ -48,6 +43,7 @@ describe("test/jetstream.js", function() {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done);
});
});

View File

@@ -4,7 +4,7 @@ var uglify = require("../node");
describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(30000);
this.timeout(60000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +

View File

@@ -86,7 +86,6 @@ function run_compress_tests() {
log_start_file(file);
function test_case(test) {
log_test(test.name);
U.base54.reset();
var output_options = test.beautify || {};
var expect;
if (test.expect) {
@@ -101,9 +100,6 @@ function run_compress_tests() {
quote_style: 3,
keep_quoted_props: true
});
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var options = U.defaults(test.options, {
warnings: false
});
@@ -118,10 +114,16 @@ function run_compress_tests() {
var cmp = new U.Compressor(options, true);
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle) {
if (test.mangle || test.mangle_props) {
U.base54.reset();
output.compute_char_frequency(test.mangle);
}
if (test.mangle) {
output.mangle_names(test.mangle);
}
if (test.mangle_props) {
output = U.mangle_properties(output, test.mangle_props);
}
output = make_code(output, output_options);
if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
@@ -294,8 +296,22 @@ function parse_test(file) {
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
test[label.name] = stat.body.value;
var body = stat.body;
if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
} else {
test[label.name] = read_string(stat) + "\n";
}

View File

@@ -19,23 +19,25 @@ function safe_log(arg, level) {
var FUNC_TOSTRING = [
"Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 0;",
" var id = 100000;",
" return function() {",
' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";',
" var i = this.name;",
' if (typeof i != "number") {',
" i = ++id;",
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return i;",
" }",
" });",
] : [], [
" }",
' return "[Function: " + i + "]";',
" }",
"}();",
].join("\n");
]).join("\n");
exports.run_code = function(code) {
var stdout = "";
var original_write = process.stdout.write;
@@ -50,7 +52,10 @@ exports.run_code = function(code) {
"}();",
].join("\n"), {
console: {
log: function() {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));

View File

@@ -102,23 +102,23 @@ for (var i = 2; i < process.argv.length; ++i) {
case '--help':
case '-h':
case '-?':
console.log('** UglifyJS fuzzer help **');
console.log('Valid options (optional):');
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 100th generated test case');
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('-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('--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('--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('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
console.log('** UglifyJS fuzzer exiting **');
println('** UglifyJS fuzzer help **');
println('Valid options (optional):');
println('<number>: generate this many cases (if used must be first arg)');
println('-v: print every generated test case');
println('-V: print every 100th generated test case');
println('-t <int>: generate this many toplevels per run (more take longer)');
println('-r <int>: maximum recursion depth for generator (higher takes longer)');
println('-s1 <statement name>: force the first level statement to be this one (see list below)');
println('-s2 <statement name>: force the second level statement to be this one (see list below)');
println('--no-catch-redef: do not redefine catch variables');
println('--no-directive: do not generate directives');
println('--use-strict: generate "use strict"');
println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
println('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
println('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
println('** UglifyJS fuzzer exiting **');
return 0;
default:
// first arg may be a number.
@@ -941,7 +941,17 @@ if (require.main !== module) {
return;
}
function try_beautify(code, result) {
function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg);
process.stdout.write("\n");
}
function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg);
process.stderr.write("\n");
}
function try_beautify(code, result, printfn) {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
@@ -951,15 +961,15 @@ function try_beautify(code, result) {
},
});
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)");
console.log(beautified.code);
printfn("// (beautified)");
printfn(beautified.code);
return;
}
console.log("//");
console.log(code);
printfn("//");
printfn(code);
}
var default_options = UglifyJS.default_options();
@@ -977,8 +987,8 @@ function log_suspects(minify_options, component) {
m[component] = o;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
console.log("Error testing options." + component + "." + name);
console.log(result.error);
errorln("Error testing options." + component + "." + name);
errorln(result.error.stack);
} else {
var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r);
@@ -986,49 +996,49 @@ function log_suspects(minify_options, component) {
}
});
if (suspects.length > 0) {
console.log("Suspicious", component, "options:");
errorln("Suspicious " + component + " options:");
suspects.forEach(function(name) {
console.log(" " + name);
errorln(" " + name);
});
console.log();
errorln();
}
}
function log(options) {
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
console.log("//=============================================================");
if (!ok) console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original_code, original_result);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
try_beautify(original_code, original_result, errorln);
errorln();
errorln();
errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") {
console.log("// uglified code");
try_beautify(uglify_code, uglify_result);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log("uglified result:");
console.log(uglify_result);
errorln("// uglified code");
try_beautify(uglify_code, uglify_result, errorln);
errorln();
errorln();
errorln("original result:");
errorln(typeof original_result == "string" ? original_result : original_result.stack);
errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
} else {
console.log("// !!! uglify failed !!!");
console.log(uglify_code.stack);
errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack);
if (typeof original_result != "string") {
console.log();
console.log();
console.log("original stacktrace:");
console.log(original_result.stack);
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result.stack);
}
}
console.log("minify(options):");
errorln("minify(options):");
options = JSON.parse(options);
console.log(options);
console.log();
errorln(JSON.stringify(options, null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options));
console.log("!!!!!! Failed... round", round);
errorln("!!!!!! Failed... round " + round);
}
}
@@ -1058,19 +1068,19 @@ for (var round = 1; round <= num_iterations; round++) {
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") {
console.log("//=============================================================");
console.log("// original code");
try_beautify(original_code, original_result);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log();
println("//=============================================================");
println("// original code");
try_beautify(original_code, original_result, println);
println();
println();
println("original result:");
println(original_result.stack);
println();
}
if (!ok && isFinite(num_iterations)) {
console.log();
println();
process.exit(1);
}
});
}
console.log();
println();