Compare commits

...

24 Commits

Author SHA1 Message Date
Alex Lam S.L
02cc4a0d03 v3.7.0 2019-11-18 20:21:07 +08:00
Alex Lam S.L
4e06e1ca34 fix corner case in inline (#3595) 2019-11-18 15:04:55 +08:00
Alex Lam S.L
644f65feca fix corner case in unsafe_math (#3594)
fixes #3593
2019-11-18 13:44:13 +08:00
Alex Lam S.L
8504a4ea0e fix corner case in reduce_funcs (#3592) 2019-11-17 11:19:42 +08:00
Alex Lam S.L
10c1a78772 fix corner case in collapse_vars (#3591) 2019-11-17 05:24:02 +08:00
Alex Lam S.L
a6a0319f1c compress empty for-in loops (#3590) 2019-11-17 02:36:42 +08:00
Alex Lam S.L
d1b2ecec27 refine precision limits on unsafe_math (#3589) 2019-11-17 01:16:42 +08:00
Alex Lam S.L
552be61c4d introduce eager evaluation (#3587) 2019-11-16 06:10:47 +08:00
Alex Lam S.L
dcfc4aca5b minor clean-ups (#3588) 2019-11-16 00:40:22 +08:00
Alex Lam S.L
4027f87717 migrate to GitHub Actions (#3586) 2019-11-14 10:48:32 +08:00
Alex Lam S.L
910799ca99 fix corner case in switches (#3585) 2019-11-14 02:29:55 +08:00
Alex Lam S.L
4bd36dc8da enhance unused (#3584) 2019-11-13 21:44:44 +08:00
Alex Lam S.L
ab15c40770 enhance switches (#3583) 2019-11-13 20:03:48 +08:00
Alex Lam S.L
fe65ce9658 fix corner case in collapse_vars (#3582)
fixes #3581
2019-11-13 16:45:16 +08:00
Alex Lam S.L
d6fd18d0b0 enhance evaluate & inline (#3580) 2019-11-13 04:17:09 +08:00
Alex Lam S.L
0d17c5b0fa v3.6.9 2019-11-12 22:50:52 +08:00
Alex Lam S.L
5b20bad4b3 fix corner case in dead_code (#3579)
fixes #3578
2019-11-12 05:16:14 +08:00
Alex Lam S.L
765a06340f enable cache on GitHub Actions (#3570) 2019-11-10 09:06:48 +08:00
Alex Lam S.L
5045e140b1 fix corner case in conditionals (#3577)
fixes #3576
2019-11-09 00:53:15 +08:00
Alex Lam S.L
10648c9af6 enhance dead_code (#3575) 2019-11-08 13:45:28 +08:00
Alex Lam S.L
87e67ec299 fix corner case in collapse_vars (#3574)
fixes #3573
2019-11-07 20:38:03 +08:00
Alex Lam S.L
61a0dad9fe v3.6.8 2019-11-06 13:52:58 +08:00
Alex Lam S.L
3e2c51a4da enhance collapse_vars (#3572) 2019-11-05 18:15:28 +08:00
Alex Lam S.L
0e29ad5eb9 fix corner case in evaluate (#3569) 2019-11-04 13:13:48 +08:00
23 changed files with 1268 additions and 456 deletions

View File

@@ -14,13 +14,17 @@ jobs:
TYPE: ${{ matrix.script }}
steps:
- uses: actions/checkout@v1
- shell: bash
- uses: actions/cache@v1
with:
path: tmp
key: tmp ${{ matrix.script }}
- name: Perform tests
shell: bash
run: |
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh
nvs --version
nvs add node/$NODE
nvs use node/$NODE
git clone --branch v1.5.3 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh --version
nvs add $NODE
nvs use $NODE
node --version
npm --version --no-update-notifier
npm install --no-audit --no-optional --no-save --no-update-notifier

View File

@@ -12,11 +12,11 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- shell: bash
- name: Perform fuzzing
shell: bash
run: |
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh
nvs --version
git clone --branch v1.5.3 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh --version
nvs add node
nvs use node
node --version

View File

@@ -1,46 +0,0 @@
cache:
directories: tmp
language: shell
matrix:
fast_finish: true
env:
- NODE=0.10 TYPE=compress
- NODE=0.10 TYPE=mocha
- NODE=0.10 TYPE=release/benchmark
- NODE=0.10 TYPE=release/jetstream
- NODE=0.12 TYPE=compress
- NODE=0.12 TYPE=mocha
- NODE=0.12 TYPE=release/benchmark
- NODE=0.12 TYPE=release/jetstream
- NODE=4 TYPE=compress
- NODE=4 TYPE=mocha
- NODE=4 TYPE=release/benchmark
- NODE=4 TYPE=release/jetstream
- NODE=6 TYPE=compress
- NODE=6 TYPE=mocha
- NODE=6 TYPE=release/benchmark
- NODE=6 TYPE=release/jetstream
- NODE=8 TYPE=compress
- NODE=8 TYPE=mocha
- NODE=8 TYPE=release/benchmark
- NODE=8 TYPE=release/jetstream
- NODE=10 TYPE=compress
- NODE=10 TYPE=mocha
- NODE=10 TYPE=release/benchmark
- NODE=10 TYPE=release/jetstream
- NODE=latest TYPE=compress
- NODE=latest TYPE=mocha
- NODE=latest TYPE=release/benchmark
- NODE=latest TYPE=release/jetstream
before_install:
- git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
- . ~/.nvs/nvs.sh
- nvs --version
install:
- nvs add node/$NODE
- nvs use node/$NODE
- node --version
- npm --version --no-update-notifier
- npm install --no-audit --no-optional --no-save --no-update-notifier
script:
- node test/$TYPE

View File

@@ -631,7 +631,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions
- `evaluate` (default: `true`) -- Evaluate expression for shorter constant
representation. Pass `"eager"` to always replace function calls whenever
possible, or a positive integer to specify an upper bound for each individual
evaluation in number of characters.
- `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.

View File

@@ -1,74 +1,5 @@
build: off
cache:
- tmp
matrix:
fast_finish: true
environment:
matrix:
- NODE: 0.10
TYPE: compress
- NODE: 0.10
TYPE: mocha
- NODE: 0.10
TYPE: release/benchmark
- NODE: 0.10
TYPE: release/jetstream
- NODE: 0.12
TYPE: compress
- NODE: 0.12
TYPE: mocha
- NODE: 0.12
TYPE: release/benchmark
- NODE: 0.12
TYPE: release/jetstream
- NODE: 4
TYPE: compress
- NODE: 4
TYPE: mocha
- NODE: 4
TYPE: release/benchmark
- NODE: 4
TYPE: release/jetstream
- NODE: 6
TYPE: compress
- NODE: 6
TYPE: mocha
- NODE: 6
TYPE: release/benchmark
- NODE: 6
TYPE: release/jetstream
- NODE: 8
TYPE: compress
- NODE: 8
TYPE: mocha
- NODE: 8
TYPE: release/benchmark
- NODE: 8
TYPE: release/jetstream
- NODE: 10
TYPE: compress
- NODE: 10
TYPE: mocha
- NODE: 10
TYPE: release/benchmark
- NODE: 10
TYPE: release/jetstream
- NODE: latest
TYPE: compress
- NODE: latest
TYPE: mocha
- NODE: latest
TYPE: release/benchmark
- NODE: latest
TYPE: release/jetstream
install:
- git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
- nvs --version
- nvs add node/%NODE%
- nvs use node/%NODE%
- node --version
- npm --version --no-update-notifier
- npm install --no-audit --no-optional --no-save --no-update-notifier
test_script:
- node test/%TYPE%
- echo No longer in use

View File

@@ -96,6 +96,8 @@ function Compressor(options, false_by_default) {
unsafe_undefined: false,
unused : !false_by_default,
}, true);
var evaluate = this.options["evaluate"];
this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) {
if (/^@/.test(key) && HOP(global_defs, key)) {
@@ -860,13 +862,9 @@ merge(Compressor.prototype, {
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
node._squeezed = false;
node._optimized = false;
reset_flags(node);
return node.reduce_vars(tw, descend, compressor);
} : function(node) {
node._squeezed = false;
node._optimized = false;
});
} : reset_flags);
// Flow control for visiting `AST_Defun`s
tw.defun_ids = Object.create(null);
tw.defun_visited = Object.create(null);
@@ -879,6 +877,12 @@ merge(Compressor.prototype, {
// - backup & restore via `save_ids` when visiting out-of-order sections
tw.safe_ids = Object.create(null);
this.walk(tw);
function reset_flags(node) {
node._squeezed = false;
node._optimized = false;
if (node instanceof AST_Scope) delete node._var_names;
}
});
AST_Symbol.DEFMETHOD("fixed_value", function(final) {
@@ -1090,7 +1094,7 @@ merge(Compressor.prototype, {
scope = node;
break;
} else if (node instanceof AST_Try) {
in_try = true;
in_try = node;
}
} while (node = compressor.parent(level++));
}
@@ -1176,7 +1180,6 @@ merge(Compressor.prototype, {
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
var sym;
if (is_last_node(node, parent) || may_throw(node)) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
@@ -1235,7 +1238,7 @@ merge(Compressor.prototype, {
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
if (!side_effects) side_effects = value_has_side_effects(candidate);
var replace_all = replace_all_symbols();
var replace_all = replace_all_symbols(candidate);
var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
return node.has_side_effects(compressor);
} : side_effects_external : return_false;
@@ -1283,6 +1286,7 @@ merge(Compressor.prototype, {
}
branch.expression = branch.expression.transform(scanner);
if (!replace_all) break;
scan_rhs = false;
}
}
abort = true;
@@ -1312,6 +1316,7 @@ merge(Compressor.prototype, {
function in_conditional(node, parent) {
if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
if (parent instanceof AST_Case) return parent.expression !== node;
if (parent instanceof AST_Conditional) return parent.condition !== node;
return parent instanceof AST_If && parent.condition !== node;
}
@@ -1319,6 +1324,10 @@ merge(Compressor.prototype, {
function is_last_node(node, parent) {
if (node instanceof AST_Call) return true;
if (node instanceof AST_Exit) {
if (in_try) {
if (in_try.bfinally) return true;
if (in_try.bcatch && node instanceof AST_Throw) return true;
}
return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
}
if (node instanceof AST_Function) {
@@ -1477,19 +1486,43 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_PropAccess) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Sequence) {
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
}
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_Unary) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
function find_stop_unused(node, level) {
var parent = scanner.parent(level);
if (is_last_node(node, parent)) return node;
if (in_conditional(node, parent)) return node;
if (parent instanceof AST_Assign) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_PropAccess) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_VarDef) return find_stop_unused(parent, level + 1);
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
@@ -1656,16 +1689,21 @@ merge(Compressor.prototype, {
return get_rvalue(expr).has_side_effects(compressor);
}
function replace_all_symbols() {
function replace_all_symbols(expr) {
if (expr instanceof AST_Unary) return false;
if (side_effects) return false;
if (value_def) return true;
if (lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
return true;
}
if (!(lhs instanceof AST_SymbolRef)) return false;
var referenced;
if (expr instanceof AST_VarDef) {
referenced = 1;
} else if (expr.operator == "=") {
referenced = 2;
} else {
return false;
}
return false;
var def = lhs.definition();
return def.references.length - def.replaced == referenced;
}
function symbol_in_lvalues(sym, parent) {
@@ -2646,22 +2684,21 @@ merge(Compressor.prototype, {
node.DEFMETHOD("_find_defs", func);
});
function best_of_expression(ast1, ast2) {
return ast1.print_to_string().length >
ast2.print_to_string().length
? ast2 : ast1;
function best_of_expression(ast1, ast2, threshold) {
var delta = ast2.print_to_string().length - ast1.print_to_string().length;
return delta < (threshold || 0) ? ast2 : ast1;
}
function best_of_statement(ast1, ast2) {
function best_of_statement(ast1, ast2, threshold) {
return best_of_expression(make_node(AST_SimpleStatement, ast1, {
body: ast1
}), make_node(AST_SimpleStatement, ast2, {
body: ast2
})).body;
}), threshold).body;
}
function best_of(compressor, ast1, ast2) {
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
function best_of(compressor, ast1, ast2, threshold) {
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
}
function convert_to_predicate(obj) {
@@ -2670,6 +2707,13 @@ merge(Compressor.prototype, {
});
}
function try_evaluate(compressor, node) {
var ev = node.evaluate(compressor);
if (ev === node) return node;
ev = make_node_from_constant(ev, node).optimize(compressor);
return best_of(compressor, node, ev, compressor.eval_threshold);
}
var object_fns = [
"constructor",
"toString",
@@ -2881,6 +2925,7 @@ merge(Compressor.prototype, {
if (!non_converting_binary[this.operator]) depth++;
var left = this.left._eval(compressor, cached, depth);
if (left === this.left) return this;
if (this.operator == (left ? "||" : "&&")) return left;
var right = this.right._eval(compressor, cached, depth);
if (right === this.right) return this;
var result;
@@ -2914,7 +2959,8 @@ merge(Compressor.prototype, {
&& typeof result == "number"
&& (this.operator == "+" || this.operator == "-")) {
var digits = Math.max(0, decimals(left), decimals(right));
if (digits < 21) return +result.toFixed(digits);
// 53-bit significand => 15.95 decimal places
if (digits < 16) return +result.toFixed(digits);
}
return result;
@@ -3031,15 +3077,15 @@ merge(Compressor.prototype, {
});
def(AST_Call, function(compressor, cached, depth) {
var exp = this.expression;
if (exp instanceof AST_SymbolRef) {
var fn = exp.fixed_value();
if (!(fn instanceof AST_Lambda)) return this;
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
if (fn instanceof AST_Lambda) {
if (fn.evaluating) return this;
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
if (fn.body.length != 1 || !fn.is_constant_expression()) return this;
var stat = fn.body[0];
if (!(stat instanceof AST_Return)) return this;
var args = eval_args(this.args);
if (!args) return this;
if (!stat.value) return undefined;
fn.argnames.forEach(function(sym, i) {
var value = args[i];
sym.definition().references.forEach(function(node) {
@@ -3049,7 +3095,11 @@ merge(Compressor.prototype, {
cached.push(node);
});
});
return stat.value ? stat.value._eval(compressor, cached, depth) : undefined;
fn.evaluating = true;
var val = stat.value._eval(compressor, cached, depth);
delete fn.evaluating;
if (val === stat.value) return this;
return val;
} else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property;
if (key instanceof AST_Node) {
@@ -3691,10 +3741,20 @@ merge(Compressor.prototype, {
});
}
if (value) {
props.push(value);
return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) {
return prop.transform(tt);
})));
if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
value = value.drop_side_effect_free(compressor);
}
if (value) props.push(value);
switch (props.length) {
case 0:
return MAP.skip;
case 1:
return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
default:
return make_sequence(node, props.map(function(prop) {
return prop.transform(tt);
}));
}
}
}
}
@@ -3838,15 +3898,33 @@ merge(Compressor.prototype, {
});
}
}
// certain combination of unused name + side effect leads to:
// https://github.com/mishoo/UglifyJS2/issues/44
// https://github.com/mishoo/UglifyJS2/issues/1830
// https://github.com/mishoo/UglifyJS2/issues/1838
// https://github.com/mishoo/UglifyJS2/issues/3371
// that's an invalid AST.
// We fix it at this stage by moving the `var` outside the `for`.
if (node instanceof AST_For) {
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
// Certain combination of unused name + side effect leads to invalid AST:
// https://github.com/mishoo/UglifyJS2/issues/1830
// We fix it at this stage by moving the label inwards, back to the `for`.
descend(node, this);
if (node.body instanceof AST_BlockStatement) {
var block = node.body;
node.body = block.body.pop();
block.body.push(node);
return in_list ? MAP.splice(block.body) : block;
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
}, function(node, in_list) {
if (node instanceof AST_For) {
// Certain combination of unused name + side effect leads to invalid AST:
// https://github.com/mishoo/UglifyJS2/issues/44
// https://github.com/mishoo/UglifyJS2/issues/1838
// https://github.com/mishoo/UglifyJS2/issues/3371
// We fix it at this stage by moving the `var` outside the `for`.
var block;
if (node.init instanceof AST_BlockStatement) {
block = node.init;
@@ -3867,36 +3945,23 @@ merge(Compressor.prototype, {
node.init = null;
}
return !block ? node : in_list ? MAP.splice(block.body) : block;
}
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
descend(node, this);
if (node.body instanceof AST_BlockStatement) {
var block = node.body;
node.body = block.body.pop();
block.body.push(node);
return in_list ? MAP.splice(block.body) : block;
} else if (node instanceof AST_ForIn) {
if (!drop_vars || !compressor.option("loops")) return;
if (!(node.init instanceof AST_Definitions)) return;
var sym = node.init.definitions[0].name;
if (sym.definition().id in in_use_ids) return;
if (!is_empty(node.body)) return;
log(sym, "Dropping unused loop variable {name} [{file}:{line},{col}]", template(sym));
var value = node.object.drop_side_effect_free(compressor);
if (value) {
AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", template(sym));
return make_node(AST_SimpleStatement, node, {
body: value
});
}
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function log(sym, text, props) {
AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
}
function template(sym) {
return {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
col : sym.start.col
};
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
} else if (node instanceof AST_Sequence) {
if (node.expressions.length == 1) return node.expressions[0];
}
});
tt.push(compressor.parent());
@@ -3905,6 +3970,19 @@ merge(Compressor.prototype, {
fn.name = null;
});
function log(sym, text, props) {
AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
}
function template(sym) {
return {
name: sym.name,
file: sym.start.file,
line: sym.start.line,
col : sym.start.col
};
}
function verify_safe_usage(def, read, modified) {
if (def.id in in_use_ids) return;
if (read && modified) {
@@ -4792,10 +4870,12 @@ merge(Compressor.prototype, {
for (var i = 0, len = self.body.length; i < len && !exact_match; i++) {
branch = self.body[i];
if (branch instanceof AST_Default) {
if (!default_branch) {
default_branch = branch;
var prev = body[body.length - 1];
if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
eliminate_branch(branch, prev);
continue;
} else {
eliminate_branch(branch, body[body.length - 1]);
default_branch = branch;
}
} else if (!(value instanceof AST_Node)) {
var exp = branch.expression.evaluate(compressor);
@@ -4824,16 +4904,20 @@ merge(Compressor.prototype, {
body.push(branch);
}
while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
if (body.length > 0) {
body[0].body = decl.concat(body[0].body);
}
self.body = body;
while (branch = body[body.length - 1]) {
var stat = branch.body[branch.body.length - 1];
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
branch.body.pop();
if (branch.body.length || branch instanceof AST_Case
&& (default_branch || branch.expression.has_side_effects(compressor))) break;
if (is_break(stat, compressor)) branch.body.pop();
if (branch === default_branch) {
if (!is_body_empty(branch)) break;
} else if (branch.expression.has_side_effects(compressor)) {
break;
} else if (default_branch) {
if (!is_body_empty(default_branch)) break;
if (body[body.length - 2] !== default_branch) break;
default_branch.body = default_branch.body.concat(branch.body);
branch.body = [];
} else if (!is_body_empty(branch)) break;
eliminate_branch(branch);
if (body.pop() === default_branch) default_branch = null;
}
if (body.length == 0) {
@@ -4843,14 +4927,15 @@ merge(Compressor.prototype, {
}))
}).optimize(compressor);
}
body[0].body = decl.concat(body[0].body);
self.body = body;
if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
var has_break = false;
var tw = new TreeWalker(function(node) {
if (has_break
|| node instanceof AST_Lambda
|| node instanceof AST_SimpleStatement) return true;
if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
has_break = true;
if (is_break(node, tw)) has_break = true;
});
self.walk(tw);
if (!has_break) {
@@ -4869,6 +4954,20 @@ merge(Compressor.prototype, {
}
return self;
function is_break(node, tw) {
return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
}
function is_body_empty(branch) {
return all(branch.body, function(stat) {
return is_empty(stat)
|| stat instanceof AST_Defun
|| stat instanceof AST_Var && all(stat.definitions, function(var_def) {
return !var_def.value;
});
});
}
function eliminate_branch(branch, prev) {
if (prev && !aborts(prev)) {
prev.body = prev.body.concat(branch.body);
@@ -5249,15 +5348,10 @@ merge(Compressor.prototype, {
}
var stat = is_func && fn.body[0];
var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor);
if (can_inline && stat instanceof AST_Return) {
if (exp === fn && can_inline && stat instanceof AST_Return) {
var value = stat.value;
if (!value || value.is_constant_expression()) {
if (value) {
value = value.clone(true);
} else {
value = make_node(AST_Undefined, self);
}
var args = self.args.concat(value);
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).optimize(compressor);
}
}
@@ -5277,6 +5371,7 @@ merge(Compressor.prototype, {
&& !fn.contains_this()
&& can_inject_symbols()) {
fn._squeezed = true;
if (exp !== fn) fn.parent_scope = exp.scope;
return make_sequence(self, flatten_fn()).optimize(compressor);
}
if (compressor.option("side_effects")
@@ -5302,12 +5397,7 @@ 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;
return try_evaluate(compressor, self);
function return_value(stat) {
if (!stat) return make_node(AST_Undefined, self);
@@ -5494,6 +5584,11 @@ merge(Compressor.prototype, {
definitions: decls
}));
[].splice.apply(scope.body, args);
fn.enclosed.forEach(function(def) {
if (scope.var_names()[def.name]) return;
scope.enclosed.push(def);
scope.var_names()[def.name] = true;
});
return expressions;
}
});
@@ -5638,15 +5733,8 @@ merge(Compressor.prototype, {
});
}
// avoids infinite recursion of numerals
if (self.operator != "-"
|| !(e instanceof AST_Number || e instanceof AST_Infinity)) {
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;
return self.operator == "-" && (e instanceof AST_Number || e instanceof AST_Infinity)
? self : try_evaluate(compressor, self);
});
AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
@@ -6110,38 +6198,12 @@ merge(Compressor.prototype, {
&& self.left.operator != "%"
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
&& self.left.is_number(compressor)) {
if (self.left.left instanceof AST_Constant
&& (self.operator != "+"
|| self.left.left.is_boolean(compressor)
|| self.left.left.is_number(compressor))) {
self = make_node(AST_Binary, self, {
operator: self.left.operator,
left: make_node(AST_Binary, self.left, {
operator: self.operator,
left: self.left.left,
right: self.right,
start: self.left.left.start,
end: self.right.end
}),
right: self.left.right
});
if (self.left.left instanceof AST_Constant) {
var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
self = make_binary(self, self.left.operator, lhs, self.left.right);
} else if (self.left.right instanceof AST_Constant) {
var op = align(self.left.operator, self.operator);
if (op != "+"
|| self.left.right.is_boolean(compressor)
|| self.left.right.is_number(compressor)) {
self = make_node(AST_Binary, self, {
operator: self.left.operator,
left: self.left.left,
right: make_node(AST_Binary, self.left, {
operator: op,
left: self.left.right,
right: self.right,
start: self.left.right.start,
end: self.right.end
})
});
}
var rhs = make_binary(self.left, align(self.left.operator, self.operator), self.left.right, self.right, self.left.right.start, self.right.end);
self = make_binary(self, self.left.operator, self.left.left, rhs);
}
}
break;
@@ -6210,12 +6272,7 @@ merge(Compressor.prototype, {
self.right = self.right.right;
return self.transform(compressor);
}
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;
return try_evaluate(compressor, self);
function align(ref, op) {
switch (ref) {
@@ -6228,6 +6285,30 @@ merge(Compressor.prototype, {
}
}
function make_binary(orig, op, left, right, start, end) {
if (op == "+") {
if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
left = make_node(AST_UnaryPrefix, left, {
operator: "+",
expression: left
});
}
if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
right = make_node(AST_UnaryPrefix, right, {
operator: "+",
expression: right
});
}
}
return make_node(AST_Binary, orig, {
operator: op,
left: left,
right: right,
start: start,
end: end
});
}
function fuzzy_eval(node) {
if (node.truthy) return true;
if (node.falsy) return false;
@@ -6307,6 +6388,7 @@ merge(Compressor.prototype, {
} while (scope = scope.parent_scope);
}
}
if (single_use) fixed.parent_scope = self.scope;
}
if (single_use && fixed) {
def.single_use = false;
@@ -6384,11 +6466,11 @@ merge(Compressor.prototype, {
};
}
var name_length = def.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(def)) {
overhead = (name_length + 2 + value_length) / (def.references.length - def.assignments);
name_length += (name_length + 2 + value_length) / (def.references.length - def.assignments);
}
def.should_replace = value_length <= name_length + overhead ? fn : false;
var delta = value_length - Math.floor(name_length);
def.should_replace = delta < compressor.eval_threshold ? fn : false;
} else {
def.should_replace = false;
}
@@ -6512,23 +6594,30 @@ merge(Compressor.prototype, {
}
}
} else if (self.left instanceof AST_SymbolRef) {
if (self.left.is_immutable()) return strip_assignment();
var def = self.left.definition();
if (def.scope === compressor.find_parent(AST_Lambda)) {
if (self.left.is_immutable()) return strip_assignment();
var level = 0, node, parent = self;
do {
node = parent;
parent = compressor.parent(level++);
if (parent instanceof AST_Exit) {
if (in_try(level, parent)) break;
if (is_reachable(def.scope, [ def ])) break;
def.fixed = false;
return strip_assignment();
}
} while (parent instanceof AST_Binary && parent.right === node
|| parent instanceof AST_Sequence && parent.tail_node() === node
|| parent instanceof AST_UnaryPrefix);
}
var scope = def.scope.resolve();
var local = scope === compressor.find_parent(AST_Lambda);
var level = 0, node, parent = self;
do {
node = parent;
parent = compressor.parent(level++);
if (parent instanceof AST_Assign) {
if (!(parent.left instanceof AST_SymbolRef)) continue;
if (parent.left.definition() !== def) continue;
if (in_try(level, parent)) break;
def.fixed = false;
return strip_assignment();
} else if (parent instanceof AST_Exit) {
if (!local) break;
if (in_try(level, parent)) break;
if (is_reachable(scope, [ def ])) break;
def.fixed = false;
return strip_assignment();
}
} while (parent instanceof AST_Binary && parent.right === node
|| parent instanceof AST_Sequence && parent.tail_node() === node
|| parent instanceof AST_UnaryPrefix);
}
}
self = self.lift_sequences(compressor);
@@ -6568,9 +6657,9 @@ merge(Compressor.prototype, {
self.right = make_node(AST_Null, right);
var may_throw = node.may_throw(compressor);
self.right = right;
var scope = self.left.definition().scope;
var parent;
while ((parent = compressor.parent(level++)) !== scope) {
while (parent = compressor.parent(level++)) {
if (parent === scope) return false;
if (parent instanceof AST_Try) {
if (parent.bfinally) return true;
if (may_throw && parent.bcatch) return true;
@@ -6637,7 +6726,7 @@ merge(Compressor.prototype, {
&& alt_tail instanceof AST_Assign
&& seq_tail.operator == alt_tail.operator
&& seq_tail.left.equivalent_to(alt_tail.left)
&& (is_eq && !seq_tail.left.has_side_effects(compressor)
&& (is_eq && seq_tail.left instanceof AST_SymbolRef
|| !condition.has_side_effects(compressor)
&& can_shift_lhs_of_tail(consequent)
&& can_shift_lhs_of_tail(alternative))) {
@@ -6806,16 +6895,9 @@ merge(Compressor.prototype, {
}
function can_shift_lhs_of_tail(node) {
if (node === node.tail_node()) return true;
var exprs = node.expressions;
for (var i = exprs.length - 1; --i >= 0;) {
var expr = exprs[i];
if (!(expr instanceof AST_Assign) && expr.has_side_effects(compressor)
|| expr.operator != "="
|| expr.left.has_side_effects(compressor)
|| expr.right.has_side_effects(compressor)) return false;
}
return true;
return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
return !expr.has_side_effects(compressor);
});
}
function pop_lhs(node) {
@@ -6982,12 +7064,7 @@ merge(Compressor.prototype, {
});
}
}
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;
return try_evaluate(compressor, self);
function find_lambda() {
var i = 0, p;
@@ -7088,17 +7165,11 @@ merge(Compressor.prototype, {
}
var sub = self.flatten_object(self.property, compressor);
if (sub) return sub.optimize(compressor);
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;
return try_evaluate(compressor, self);
});
OPT(AST_Object, function(self, compressor) {
if (!compressor.option("objects") || compressor.has_directive("use strict")) return self;
var props = self.properties;
var keys = new Dictionary();
var values = [];
self.properties.forEach(function(prop) {

View File

@@ -252,7 +252,7 @@ function minify(files, options) {
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),
total: 1e-3 * (timings.end - timings.start)
}
};
}
if (warnings.length) {
result.warnings = warnings;

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.6.7",
"version": "3.7.0",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -1458,7 +1458,7 @@ collapse_vars_constants: {
function f3(x) {
var b = x.prop;
sideeffect1();
return b + (function() { return -9; })();
return b + -9;
}
}
}
@@ -5748,7 +5748,7 @@ issue_3215_1: {
}());
}
expect: {
console.log("number");
console.log(typeof 42);
}
expect_stdout: "number"
}
@@ -6238,7 +6238,7 @@ issue_3439_2: {
expect_stdout: "number"
}
cond_sequence_return: {
cond_sequence_return_1: {
options = {
collapse_vars: true,
}
@@ -6259,6 +6259,27 @@ cond_sequence_return: {
expect_stdout: "2"
}
cond_sequence_return_2: {
options = {
collapse_vars: true,
}
input: {
console.log(function(n) {
var c = 0;
for (var k in [0, 1])
if (c += 1, k == n) return c;
}(1));
}
expect: {
console.log(function(n) {
var c = 0;
for (var k in [0, 1])
if (c += 1, k == n) return c;
}(1));
}
expect_stdout: "2"
}
issue_3520: {
options = {
collapse_vars: true,
@@ -6387,3 +6408,144 @@ issue_3562: {
}
expect_stdout: "PASS PASS"
}
dot_throw_assign_sequence: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
try {
var b;
b[0] = (a = "PASS", 0);
a = 1 + a;
} catch (c) {
}
console.log(a);
}
expect: {
var a = "FAIL";
try {
var b;
b[0] = (a = "PASS", 0);
a = 1 + a;
} catch (c) {
}
console.log(a);
}
expect_stdout: "PASS"
}
call_assign_order: {
options = {
collapse_vars: true,
}
input: {
var a, b = 1, c = 0, log = console.log;
(function() {
a = b = "PASS";
})((b = "FAIL", c++));
log(a, b);
}
expect: {
var a, b = 1, c = 0, log = console.log;
(function() {
a = b = "PASS";
})((b = "FAIL", c++));
log(a, b);
}
expect_stdout: "PASS PASS"
}
issue_3573: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
(function(b) {
while (--b) {
b = NaN;
switch (0 / this < 0) {
case c++, false:
case c++, NaN:
}
}
})(3);
console.log(c);
}
expect: {
var c = 0;
(function(b) {
while (--b) {
b = NaN;
switch (0 / this < 0) {
case c++, false:
case c++, NaN:
}
}
})(3);
console.log(c);
}
expect_stdout: "1"
}
issue_3581_1: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS", b = "FAIL";
try {
b = "PASS";
if (a) throw 0;
b = 1 + b;
a = "FAIL";
} catch (e) {}
console.log(a, b);
}
expect: {
var a = "PASS", b = "FAIL";
try {
b = "PASS";
if (a) throw 0;
b = 1 + b;
a = "FAIL";
} catch (e) {}
console.log(a, b);
}
expect_stdout: "PASS PASS"
}
issue_3581_2: {
options = {
collapse_vars: true,
}
input: {
(function() {
var a = "PASS", b = "FAIL";
try {
b = "PASS";
if (a) return;
b = 1 + b;
a = "FAIL";
} finally {
console.log(a, b);
}
})();
}
expect: {
(function() {
var a = "PASS", b = "FAIL";
try {
b = "PASS";
if (a) return;
b = 1 + b;
a = "FAIL";
} finally {
console.log(a, b);
}
})();
}
expect_stdout: "PASS PASS"
}

View File

@@ -1489,3 +1489,29 @@ angularjs_chain: {
}
}
}
issue_3576: {
options = {
conditionals: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
var c = "FAIL";
(function(a) {
(a = -1) ? (a && (a.a = 0)) : (a && (a.a = 0));
a && a[c = "PASS"]++;
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function(a) {
a = -1, a, a.a = 0;
a, a[c = "PASS"]++;
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -892,9 +892,7 @@ issue_2860_1: {
}());
}
expect: {
console.log(function(a) {
return 1 ^ a;
}());
console.log(1);
}
expect_stdout: "1"
}
@@ -1064,3 +1062,68 @@ issue_3552: {
}
expect_stdout: "PASS"
}
unreachable_assign: {
options = {
dead_code: true,
}
input: {
console.log(A = "P" + (A = "A" + (B = "S" + (A = B = "S"))), A, B);
}
expect: {
console.log(A = "P" + "A" + (B = "S" + "S"), A, B);
}
expect_stdout: "PASS PASS SS"
}
catch_return_assign: {
options = {
dead_code: true,
}
input: {
console.log(function() {
try {
throw "FAIL";
} catch (e) {
return e = "PASS";
}
}());
}
expect: {
console.log(function() {
try {
throw "FAIL";
} catch (e) {
return "PASS";
}
}());
}
expect_stdout: "PASS"
}
issue_3578: {
options = {
dead_code: true,
}
input: {
var a = "FAIL", b, c;
try {
b = c.p = b = 0;
} catch (e) {
b += 42;
b && (a = "PASS");
}
console.log(a);
}
expect: {
var a = "FAIL", b, c;
try {
b = c.p = b = 0;
} catch (e) {
b += 42;
b && (a = "PASS");
}
console.log(a);
}
expect_stdout: "PASS"
}

View File

@@ -1019,14 +1019,21 @@ delete_assign_1: {
console.log(delete (a = 0 / 0));
}
expect: {
console.log((void 0, !0));
console.log((void 0, !0));
console.log((1 / 0, !0));
console.log((1 / 0, !0));
console.log((NaN, !0));
console.log((NaN, !0));
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
expect_stdout: [
"true",
"true",
"true",
"true",
"true",
"true",
]
}
delete_assign_2: {
@@ -1047,14 +1054,21 @@ delete_assign_2: {
console.log(delete (a = 0 / 0));
}
expect: {
console.log((void 0, !0));
console.log((void 0, !0));
console.log((Infinity, !0));
console.log((1 / 0, !0));
console.log((NaN, !0));
console.log((NaN, !0));
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
expect_stdout: [
"true",
"true",
"true",
"true",
"true",
"true",
]
}
drop_var: {
@@ -1635,7 +1649,7 @@ double_assign_2: {
}
expect: {
for (var i = 0; i < 2; i++)
void 0, a = {}, console.log(a);
a = {}, console.log(a);
var a;
}
}
@@ -1716,7 +1730,7 @@ issue_2768: {
}
expect: {
var a = "FAIL";
var c = (d = a, 0, void (d && (a = "PASS")));
var c = (d = a, void (d && (a = "PASS")));
var d;
console.log(a, typeof c);
}

View File

@@ -1876,3 +1876,184 @@ issue_3558: {
}
expect_stdout: "1 0"
}
issue_3568: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a = 0;
function f(b) {
return b && b.p;
}
console.log(f(++a + f()));
}
expect: {
var a = 0;
function f(b) {
return b && b.p;
}
console.log(NaN);
}
expect_stdout: "NaN"
}
conditional_function: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
function f(a) {
return a && "undefined" != typeof A ? A : 42;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
return a && "undefined" != typeof A ? A : 42;
}
console.log(42, f(1));
}
expect_stdout: "42 42"
}
best_of_evaluate: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function d(x, y) {
return x / y;
}
console.log(0 / 3, 1 / 64, 4 / 7, 7 / 7);
console.log(d(0, 3), d(1, 64), d(4, 7), d(7, 7));
}
expect: {
function d(x, y) {
return x / y;
}
console.log(0, 1 / 64, 4 / 7, 1);
console.log(0, .015625, d(4, 7), 1);
}
expect_stdout: [
"0 0.015625 0.5714285714285714 1",
"0 0.015625 0.5714285714285714 1",
]
}
eager_evaluate: {
options = {
evaluate: "eager",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function d(x, y) {
return x / y;
}
console.log(0 / 3, 1 / 64, 4 / 7, 7 / 7);
console.log(d(0, 3), d(1, 64), d(4, 7), d(7, 7));
}
expect: {
console.log(0, .015625, .5714285714285714, 1);
console.log(0, .015625, .5714285714285714, 1);
}
expect_stdout: [
"0 0.015625 0.5714285714285714 1",
"0 0.015625 0.5714285714285714 1",
]
}
threshold_evaluate_default: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function b(x) {
return x + x + x;
}
console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
}
expect: {
function b(x) {
return x + x + x;
}
console.log("111", 6, b(b(b("ABCDEFGHIJK"))));
}
expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
}
threshold_evaluate_30: {
options = {
evaluate: 30,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function b(x) {
return x + x + x;
}
console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
}
expect: {
function b(x) {
return x + x + x;
}
console.log("111", 6, b(b("ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK")));
}
expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
}
threshold_evaluate_100: {
options = {
evaluate: 100,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function b(x) {
return x + x + x;
}
console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
}
expect: {
function b(x) {
return x + x + x;
}
console.log("111", 6, b("ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"));
}
expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
}
threshold_evaluate_999: {
options = {
evaluate: 999,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function b(x) {
return x + x + x;
}
console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
}
expect: {
console.log("111", 6, "ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK");
}
expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
}

View File

@@ -2265,7 +2265,8 @@ issue_3054: {
return { a: !0 };
}
console.log(function(b) {
return { a: !(b = !1) };
b = !1;
return f();
}().a, f.call().a);
}
expect_stdout: "true true"
@@ -2345,7 +2346,7 @@ issue_3274: {
}
expect: {
(function() {
for (var c; void 0, (c = 1..p) != c;)
for (var c; (c = 1..p) != c;)
console.log("FAIL");
console.log("PASS");
})();
@@ -2415,7 +2416,7 @@ issue_3297_2: {
doProcessOne({
param1: param1,
param2: param2,
}, function () {
}, function() {
processBulk(bulk);
});
};
@@ -2496,7 +2497,7 @@ issue_3297_3: {
doProcessOne({
param1: param1,
param2: param2,
}, function () {
}, function() {
processBulk(bulk);
});
};
@@ -2513,18 +2514,21 @@ issue_3297_3: {
}).processBulk([1, 2, 3]);
}
expect: {
function function1(u) {
function function1(c) {
return {
processBulk: function n(r) {
var o, t = u();
r && 0 < r.length && (o = {
param1: r.shift(),
processBulk: function n(o) {
var r, t, u = c();
o && 0 < o.length && (r = {
param1: o.shift(),
param2: {
subparam1: t
subparam1: u
}
},
console.log(JSON.stringify(o)),
n(r));
t = function() {
n(o);
},
console.log(JSON.stringify(r)),
t());
}
};
}
@@ -3096,23 +3100,20 @@ issue_3400_1: {
});
}
expect: {
void console.log(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
void console.log(function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
return g;
}()()[0].p);
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
}()[0].p);
}
expect_stdout: [
"undefined",
@@ -3153,12 +3154,10 @@ issue_3400_2: {
expect: {
void console.log(function g() {
return [ 42 ].map(function(v) {
return function(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}(v);
return o = {
p: v
}, console.log(o[g]), o;
var o;
});
}()[0].p);
}
@@ -3400,3 +3399,172 @@ issue_3562: {
}
expect_stdout: "PASS"
}
hoisted_inline: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
console.log("PASS");
}
function g() {
for (var console in [ 0 ])
h();
}
function h() {
f();
}
g();
}
expect: {
function f() {
console.log("PASS");
}
(function() {
for (var console in [ 0 ])
void f();
})();
}
expect_stdout: "PASS"
}
hoisted_single_use: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
for (var r in a) g(r);
}
function g(a) {
console.log(a);
}
function h(a) {
var g = a.bar;
g();
g();
i(a);
}
function i(b) {
f(b);
}
h({
bar: function() {
console.log("foo");
}
});
}
expect: {
function f(a) {
for (var r in a) g(r);
}
function g(a) {
console.log(a);
}
(function(a) {
var g = a.bar;
g();
g();
(function(b) {
f(b);
})(a);
})({
bar: function() {
console.log("foo");
}
});
}
expect_stdout: [
"foo",
"foo",
"bar",
]
}
pr_3592_1: {
options = {
inline: true,
reduce_funcs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function problem(w) {
return g.indexOf(w);
}
function unused(x) {
return problem(x);
}
function B(problem) {
return g[problem];
}
function A(y) {
return problem(y);
}
function main(z) {
return B(A(z));
}
var g = [ "PASS" ];
console.log(main("PASS"));
}
expect: {
function problem(w) {
return g.indexOf(w);
}
function B(problem) {
return g[problem];
}
var g = [ "PASS" ];
console.log((z = "PASS", B((y = z, problem(y)))));
var z, y;
}
expect_stdout: "PASS"
}
pr_3592_2: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function problem(w) {
return g.indexOf(w);
}
function unused(x) {
return problem(x);
}
function B(problem) {
return g[problem];
}
function A(y) {
return problem(y);
}
function main(z) {
return B(A(z));
}
var g = [ "PASS" ];
console.log(main("PASS"));
}
expect: {
function problem(w) {
return g.indexOf(w);
}
var g = [ "PASS" ];
console.log((z = "PASS", function(problem) {
return g[problem];
}((y = z, problem(y)))));
var z, y;
}
expect_stdout: "PASS"
}

View File

@@ -52,3 +52,30 @@ chained_evaluation_2: {
})();
}
}
chained_evaluation_3: {
options = {
collapse_vars: true,
evaluate: 10,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = "long piece of string";
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
(function() {
f("long piece of string").bar = "long piece of string";
})();
})();
}
}

View File

@@ -689,3 +689,67 @@ step: {
}
expect_stdout: "42"
}
empty_for_in: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
for (var a in [ 1, 2, 3 ]) {
var b = a + 1;
}
}
expect: {}
expect_warnings: [
"WARN: Dropping unused variable b [test/compress/loops.js:2,16]",
"INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
]
}
empty_for_in_used: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
for (var a in [ 1, 2, 3 ]) {
var b = a + 1;
}
console.log(a);
}
expect: {
for (var a in [ 1, 2, 3 ]);
console.log(a);
}
expect_stdout: "2"
expect_warnings: [
"WARN: Dropping unused variable b [test/compress/loops.js:2,16]",
]
}
empty_for_in_side_effects: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
for (var a in {
foo: console.log("PASS")
}) {
var b = a + "bar";
}
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
expect_warnings: [
"WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
"INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
"WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]",
]
}

View File

@@ -870,7 +870,7 @@ issue_3547_3: {
a + "21",
a + "2" - 1,
a - 1,
a - "2" - 1,
a - 3,
].forEach(function(n) {
console.log(typeof n, n);
});
@@ -904,7 +904,7 @@ issue_3547_4: {
[
"3" + a + 1,
"3" + a - 1,
"3" - a + 1,
4 - a,
2 - a,
].forEach(function(n) {
console.log(typeof n, n);
@@ -917,3 +917,31 @@ issue_3547_4: {
"number 0",
]
}
unsafe_math_rounding: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
console.log(4 / -3 + 1 === 1 / -3);
}
expect: {
console.log(false);
}
expect_stdout: "false"
}
issue_3593: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
console.log((0 === this) - 1 - "1");
}
expect: {
console.log((0 === this) - 2);
}
expect_stdout: "-2"
}

View File

@@ -1427,13 +1427,13 @@ defun_inline_3: {
defun_call: {
options = {
inline: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
console.log(function f() {
return g() + h(1) - h(g(), 2, 3);
function g() {
return 4;
@@ -1441,21 +1441,17 @@ defun_call: {
function h(a) {
return a;
}
}
}());
}
expect: {
function f() {
return 4 + h(1) - h(4);
function h(a) {
return a;
}
}
console.log(1);
}
expect_stdout: "1"
}
defun_redefine: {
options = {
inline: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
@@ -1480,7 +1476,7 @@ defun_redefine: {
(function() {
return 3;
});
return 3 + 2;
return 5;
}
console.log(f());
}
@@ -1517,7 +1513,7 @@ func_inline: {
func_modified: {
options = {
inline: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
@@ -1550,7 +1546,7 @@ func_modified: {
(function() {
return 4;
});
return 1 + 2 + 4;
return 7;
}
console.log(f());
}
@@ -5516,9 +5512,7 @@ issue_2860_1: {
}());
}
expect: {
console.log(function(a) {
return 1 ^ a;
}());
console.log(1);
}
expect_stdout: "1"
}

View File

@@ -261,13 +261,13 @@ drop_default_1: {
}
input: {
switch (foo) {
case 'bar': baz();
case "bar": baz();
default:
}
}
expect: {
switch (foo) {
case 'bar': baz();
case "bar": baz();
}
}
}
@@ -279,14 +279,14 @@ drop_default_2: {
}
input: {
switch (foo) {
case 'bar': baz(); break;
case "bar": baz(); break;
default:
break;
}
}
expect: {
switch (foo) {
case 'bar': baz();
case "bar": baz();
}
}
}
@@ -298,7 +298,7 @@ keep_default: {
}
input: {
switch (foo) {
case 'bar': baz();
case "bar": baz();
default:
something();
break;
@@ -306,7 +306,7 @@ keep_default: {
}
expect: {
switch (foo) {
case 'bar': baz();
case "bar": baz();
default:
something();
}
@@ -347,25 +347,103 @@ issue_1663: {
expect_stdout: true
}
drop_case: {
drop_case_1: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
case 'bar': baz(); break;
case 'moo':
case "bar": baz(); break;
case "moo":
break;
}
}
expect: {
switch (foo) {
case 'bar': baz();
case "bar": baz();
}
}
}
drop_case_2: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
case "bar":
bar();
break;
default:
case "moo":
moo();
break;
}
}
expect: {
switch (foo) {
case "bar":
bar();
break;
default:
moo();
}
}
}
drop_case_3: {
options = {
dead_code: true,
switches: true,
}
input: {
var c = "PASS";
switch ({}.p) {
default:
case void 0:
break;
case c = "FAIL":
}
console.log(c);
}
expect: {
var c = "PASS";
switch ({}.p) {
default:
case void 0:
break;
case c = "FAIL":
}
console.log(c);
}
expect_stdout: "PASS"
}
drop_case_4: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (0) {
case [ a, typeof b ]:
default:
var a;
}
console.log("PASS");
}
expect: {
switch (0) {
case [ a, typeof b ]:
var a;
}
console.log("PASS");
}
expect_stdout: "PASS"
}
keep_case: {
options = {
dead_code: true,
@@ -373,14 +451,14 @@ keep_case: {
}
input: {
switch (foo) {
case 'bar': baz(); break;
case "bar": baz(); break;
case moo:
break;
}
}
expect: {
switch (foo) {
case 'bar': baz(); break;
case "bar": baz(); break;
case moo:
}
}
@@ -539,7 +617,7 @@ issue_1679: {
f();
console.log(a, b);
}
expect_stdout: true
expect_stdout: "99 8"
}
issue_1680_1: {
@@ -864,3 +942,89 @@ issue_1750: {
}
expect_stdout: "0 2"
}
drop_switch_1: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
default:
break;
case "bar":
break;
}
}
expect: {
foo;
}
}
drop_switch_2: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
default:
case "bar":
baz();
}
}
expect: {
foo;
baz();
}
}
drop_switch_3: {
options = {
dead_code: true,
switches: true,
}
input: {
console.log(function() {
switch (0) {
default:
return "PASS";
case 1:
}
}());
}
expect: {
console.log(function() {
switch (0) {
default:
return "PASS";
case 1:
}
}());
}
expect_stdout: "PASS"
}
drop_switch_4: {
options = {
dead_code: true,
switches: true,
}
input: {
var a = "FAIL";
switch (0) {
default:
case a:
var b = a = "PASS";
break;
}
console.log(a);
}
expect: {
var a = "FAIL";
0;
var b = a = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}

View File

@@ -166,9 +166,7 @@ duplicate_lambda_arg_name: {
}());
}
expect: {
console.log(function long_name(long_name) {
return typeof long_name;
}());
console.log("undefined");
}
expect_stdout: "undefined"
}

View File

@@ -12,7 +12,9 @@ describe("bin/uglifyjs", function() {
it("Should produce a functional build when using --self", function(done) {
this.timeout(30000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
exec(command, function(err, stdout) {
exec(command, {
maxBuffer: 1048576
}, function(err, stdout) {
if (err) throw err;
eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, "object");

View File

@@ -10,7 +10,9 @@ describe("spidermonkey export/import sanity test", function() {
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
uglifyjs + " -p spidermonkey -cm";
exec(command, function(err, stdout) {
exec(command, {
maxBuffer: 1048576
}, function(err, stdout) {
if (err) throw err;
eval(stdout);

View File

@@ -1,44 +0,0 @@
"use strict";
var child_process = require("child_process");
var https = require("https");
var url = require("url");
var period = 45 * 60 * 1000;
var wait = 2 * 60 * 1000;
if (process.argv.length > 2) {
var token = process.argv[2];
var branch = process.argv[3] || "v" + require("../../package.json").version;
var repository = encodeURIComponent(process.argv[4] || "mishoo/UglifyJS2");
var concurrency = process.argv[5] || 1;
var platform = process.argv[6] || "latest";
(function request() {
setTimeout(request, (period + wait) / concurrency);
var options = url.parse("https://api.travis-ci.org/repo/" + repository + "/requests");
options.method = "POST";
options.headers = {
"Content-Type": "application/json",
"Travis-API-Version": 3,
"Authorization": "token " + token
};
https.request(options, function(res) {
console.log("HTTP", res.statusCode);
console.log(JSON.stringify(res.headers, null, 2));
console.log();
res.setEncoding("utf8");
res.on("data", console.log);
}).on("error", console.error).end(JSON.stringify({
request: {
message: "ufuzz testing",
branch: branch,
config: {
cache: false,
env: "NODE=" + platform,
script: "node test/ufuzz/job " + period
}
}
}));
})();
} else {
console.log("Usage: test/ufuzz/travis.js <token> [branch] [repository] [concurrency] [platform]");
}