Compare commits

..

39 Commits

Author SHA1 Message Date
Alex Lam S.L
6982a0554c v3.3.17 2018-03-31 04:13:45 +00:00
Alex Lam S.L
fa3250199a mangle unused nested AST_SymbolCatch correctly (#3038)
fixes #3035
2018-03-30 16:23:09 +09:00
Alex Lam S.L
06b9894c19 handle modifications to this correctly (#3036)
fixes #3032
2018-03-30 15:07:36 +09:00
Alex Lam S.L
9f9db504d7 improve test for #3023 (#3031) 2018-03-29 23:36:40 +09:00
Alex Lam S.L
82ae95c334 improve source map granularity (#3030)
fixes #3023
2018-03-29 14:47:55 +09:00
Fábio Santos
9a5e2052c4 fix extra regex slash when going through mozilla AST I/O (#3025)
This relates to #1929, but in the context of mozilla AST input/output.
2018-03-27 03:22:01 +09:00
Alex Lam S.L
b1410be443 speed up has_parens() (#3014) 2018-03-24 04:05:28 +08:00
Alex Lam S.L
12985d86c2 fix corner case in hoist_props (#3022)
fixes #3021
2018-03-23 07:27:35 +08:00
Alex Lam S.L
49bfc6b555 improve performance (#3020)
- replace `find_if()` with `all()` wherever possible
- move ESTree-specific logic out of `figure_out_scope()`
2018-03-23 03:43:52 +08:00
Alex Lam S.L
d1c6bb8c7c fix nested inline within loop (#3019)
fixes #3018
2018-03-23 02:31:59 +08:00
Alex Lam S.L
5c169615a8 fix corner case in inline (#3017)
fixes #3016
2018-03-22 23:46:26 +08:00
Alex Lam S.L
73d77f4f64 v3.3.16 2018-03-19 06:53:51 +00:00
Alex Lam S.L
ccf0e2ef4f extend fuzzy RHS folding (#3006)
- `a = []; if (1) x();` => `if (a = []) x();`
2018-03-17 03:10:21 +08:00
Alex Lam S.L
20ca0f5906 improve truthy compression (#3009) 2018-03-16 06:12:59 +08:00
Alex Lam S.L
b29d435bb5 refactor brackets to braces (#3005) 2018-03-15 15:46:45 +08:00
Alex Lam S.L
90585e29c2 v3.3.15 2018-03-14 16:45:38 +00:00
Alex Lam S.L
d8fc281915 update dependencies (#3002)
acorn 5.5.3
commander 2.15.0

Miscellaneous
- drop unmaintained package from README
2018-03-14 15:54:41 +08:00
Alex Lam S.L
188c39e8d5 retain comments within brackets (#2999)
fixes #2998
2018-03-13 18:44:21 +08:00
Alex Lam S.L
5429234138 preserve non-constant value assignments with modifications (#2997)
fixes #2995
2018-03-13 17:35:34 +08:00
Alex Lam S.L
b9f72a4a81 handle case correctly under reduce_vars (#2993)
fixes #2992
2018-03-11 15:54:43 +08:00
Alex Lam S.L
fc6ebd04a5 preserve case when inline_script (#2991)
fixes #2989
2018-03-11 05:11:12 +08:00
Alex Lam S.L
7e00a12741 v3.3.14 2018-03-10 13:20:14 +00:00
Alex Lam S.L
10b3752b1e fix mangle of AST_SymbolLambda under ie8 (#2978)
fixes #2976
2018-03-07 17:20:38 +08:00
Alex Lam S.L
fe51a91395 handle negated constants correctly in collapse_vars (#2975)
fixes #2974
2018-03-06 00:45:58 +08:00
Alex Lam S.L
951d87ca94 v3.3.13 2018-03-04 07:38:49 +00:00
Alex Lam S.L
798fc21530 improve test/run-test.js performance (#2971)
- allow reuse of contextified sandbox
- minimise bottleneck from `vm.createContext()`
2018-03-04 04:50:00 +08:00
Alex Lam S.L
a75a046abb compress arguments[index] (#2967)
- always replace with existing parameter
- only introduce new parameter if `keep_fargs` is disabled
2018-03-02 11:22:09 +08:00
Alex Lam S.L
38f2b4579f fix value reference caching in evaluate (#2969)
fixes #2968
2018-03-02 04:04:29 +08:00
Alex Lam S.L
56e2a369d0 enhance conditionals (#2966)
- `x ? (y, w) : (z, w)` => `x ? y : z, w`
2018-02-28 23:34:48 +08:00
Alex Lam S.L
0daa199fa8 migrate safe transformations out of unsafe_comps (#2962)
fixes #2959
2018-02-28 22:02:24 +08:00
Alex Lam S.L
73e98dcda4 drop side_effects-free return values (#2965) 2018-02-28 19:59:19 +08:00
Alex Lam S.L
36bca6934d enhance collapse_vars (#2952)
- `a = b, b` => `a = b`
- `a.b = c, c()` => `(a.b = c)()`
2018-02-28 15:19:32 +08:00
Alex Lam S.L
ace5811691 drop lone "use strict" in function body (#2963)
fixes #2961
2018-02-26 15:22:52 +08:00
Dan
ba7bad0dbd show benchmark subtotal (#2960)
At the end of the benchmark, sums of the input, output and
gzip values are shown, such as:

    Subtotal

    Original: 7785102 bytes
    Uglified: 2283862 bytes
    GZipped:  662354 bytes
2018-02-26 11:46:26 +08:00
Alex Lam S.L
b8b2ac5230 improve fix for #2954 (#2958) 2018-02-26 03:14:22 +08:00
Alex Lam S.L
ea2359381b fix collapse_vars on nested exception (#2955)
fixes #2954
2018-02-25 15:39:00 +08:00
Alex Lam S.L
52de64cf16 deduplicate parenthesis around object and function literals (#2953) 2018-02-25 02:14:33 +08:00
Alex Lam S.L
455790202a minor clean-ups (#2951) 2018-02-24 02:24:47 +08:00
Alex Lam S.L
f40f5eb228 improve mangle (#2948) 2018-02-23 23:51:49 +08:00
44 changed files with 2686 additions and 636 deletions

View File

@@ -6,9 +6,8 @@ UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note:
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- `uglify-js` only supports ECMAScript 5 (ES5).
- Those wishing to minify
ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
- `uglify-js` only supports JavaScript (ECMAScript 5).
- To minify ECMAScript 2015 or above, transpile using tools like [Babel](https://babeljs.io/).
Install
-------
@@ -598,6 +597,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Compress options
- `arguments` (default: `true`) -- replace `arguments[index]` with function
parameter name whenever possible.
- `booleans` (default: `true`) -- various optimizations for boolean context,
for example `!!a ? b : c → a ? b : c`
@@ -605,8 +607,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
side effects permitting.
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
e.g. `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
e.g. `!(a <= b) → a > b`, attempts to negate binary nodes, e.g.
`a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
expressions
@@ -730,12 +732,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_comps` (default: `false`) -- compress expressions like `a <= b` assuming
none of the operands can be (coerced to) `NaN`.
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
@@ -826,7 +824,7 @@ can pass additional arguments that control the code output:
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
- `braces` (default `false`) -- always insert braces in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
@@ -838,8 +836,8 @@ can pass additional arguments that control the code output:
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `inline_script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `inline_script` (default `true`) -- escape HTML comments and the slash in
occurrences of `</script>` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.

View File

@@ -165,7 +165,7 @@ function walk_body(node, visitor) {
};
var AST_Block = DEFNODE("Block", "body", {
$documentation: "A body of statements (usually bracketed)",
$documentation: "A body of statements (usually braced)",
$propdoc: {
body: "[AST_Statement*] an array of statements"
},
@@ -916,5 +916,25 @@ TreeWalker.prototype = {
|| node instanceof AST_Break && x instanceof AST_Switch)
return x;
}
},
in_boolean_context: function() {
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
}
};

View File

@@ -48,6 +48,7 @@ function Compressor(options, false_by_default) {
return new Compressor(options, false_by_default);
TreeTransformer.call(this, this.before, this.after);
this.options = defaults(options, {
arguments : !false_by_default,
booleans : !false_by_default,
collapse_vars : !false_by_default,
comparisons : !false_by_default,
@@ -146,27 +147,6 @@ merge(Compressor.prototype, {
return true;
return false;
},
in_boolean_context: function() {
if (!this.option("booleans")) return false;
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
},
compress: function(node) {
if (this.option("expression")) {
node.process_expression(true);
@@ -516,6 +496,15 @@ merge(Compressor.prototype, {
pop(tw);
return true;
});
def(AST_Case, function(tw) {
push(tw);
this.expression.walk(tw);
pop(tw);
push(tw);
walk_body(this, tw);
pop(tw);
return true;
});
def(AST_Conditional, function(tw) {
this.condition.walk(tw);
push(tw);
@@ -526,6 +515,12 @@ merge(Compressor.prototype, {
pop(tw);
return true;
});
def(AST_Default, function(tw, descend) {
push(tw);
descend();
pop(tw);
return true;
});
def(AST_Defun, function(tw, descend, compressor) {
this.inlined = false;
var save_ids = tw.safe_ids;
@@ -623,12 +618,6 @@ merge(Compressor.prototype, {
pop(tw);
return true;
});
def(AST_SwitchBranch, function(tw, descend) {
push(tw);
descend();
pop(tw);
return true;
});
def(AST_SymbolCatch, function() {
this.definition().fixed = false;
});
@@ -777,8 +766,7 @@ merge(Compressor.prototype, {
lhs = lhs.fixed_value();
}
if (!lhs) return true;
if (lhs instanceof AST_RegExp) return false;
if (lhs instanceof AST_Constant) return true;
if (lhs.is_constant()) return true;
return is_lhs_read_only(lhs);
}
return false;
@@ -890,6 +878,11 @@ merge(Compressor.prototype, {
return x;
};
function root_expr(prop) {
while (prop instanceof AST_PropAccess) prop = prop.expression;
return prop;
}
function is_iife_call(node) {
if (node.TYPE != "Call") return false;
return node.expression instanceof AST_Function || is_iife_call(node.expression);
@@ -913,8 +906,8 @@ merge(Compressor.prototype, {
}
function tighten_body(statements, compressor) {
var scope = compressor.find_parent(AST_Scope);
var in_loop = is_in_loop();
var in_loop, in_try, scope;
find_loop_scope_try();
var CHANGED, max_iter = 10;
do {
CHANGED = false;
@@ -937,12 +930,20 @@ merge(Compressor.prototype, {
}
} while (CHANGED && max_iter-- > 0);
function is_in_loop() {
for (var node, level = 0; node = compressor.parent(level); level++) {
if (node instanceof AST_IterationStatement) return true;
if (node instanceof AST_Scope) break;
}
return false;
function find_loop_scope_try() {
var node = compressor.self(), level = 0;
do {
if (node instanceof AST_Catch || node instanceof AST_Finally) {
level++;
} else if (node instanceof AST_IterationStatement) {
in_loop = true;
} else if (node instanceof AST_Scope) {
scope = node;
break;
} else if (node instanceof AST_Try) {
in_try = true;
}
} while (node = compressor.parent(level++));
}
// Search from right to left for assignment-like expressions:
@@ -957,9 +958,8 @@ merge(Compressor.prototype, {
if (scope.uses_eval || scope.uses_with) return statements;
var args;
var candidates = [];
var in_try = compressor.self() instanceof AST_Try;
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) {
var scanner = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
@@ -987,17 +987,19 @@ merge(Compressor.prototype, {
return node;
}
// Stop only if candidate is found within conditional branches
if (!stop_if_hit && (!lhs_local || !replace_all)
if (!stop_if_hit
&& (parent instanceof AST_Binary && lazy_op(parent.operator) && parent.left !== node
|| parent instanceof AST_Conditional && parent.condition !== node
|| parent instanceof AST_If && parent.condition !== node)) {
stop_if_hit = parent;
}
// Replace variable with assignment when found
var hit_lhs, hit_rhs;
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) {
if (stop_if_hit) {
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true;
return node;
}
@@ -1049,7 +1051,8 @@ merge(Compressor.prototype, {
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name] || side_effects && may_modify(node))
&& (symbol_in_lvalues(node) || side_effects && may_modify(node))
|| node instanceof AST_This && symbol_in_lvalues(node)
|| node instanceof AST_VarDef && node.value
&& (node.name.name in lvalues || side_effects && may_modify(node.name))
|| (sym = is_lhs(node.left, node))
@@ -1104,12 +1107,15 @@ merge(Compressor.prototype, {
var stop_after = null;
var stop_if_hit = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
var rhs = get_rhs(candidate);
var side_effects = lhs && lhs.has_side_effects(compressor);
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
var scan_rhs = rhs && foldable(rhs);
if (!scan_lhs && !scan_rhs) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var side_effects = value_has_side_effects(candidate);
if (!side_effects) side_effects = value_has_side_effects(candidate);
var replace_all = replace_all_symbols();
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
@@ -1214,9 +1220,7 @@ merge(Compressor.prototype, {
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
candidates.push(hit_stack.slice());
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
@@ -1354,21 +1358,69 @@ merge(Compressor.prototype, {
}
}
function get_rhs(expr) {
if (!(candidate instanceof AST_Assign && candidate.operator == "=")) return;
return candidate.right;
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function foldable(expr) {
if (expr instanceof AST_SymbolRef) {
var value = expr.evaluate(compressor);
if (value === expr) return rhs_exact_match;
return rhs_fuzzy_match(value, rhs_exact_match);
}
if (expr instanceof AST_This) return rhs_exact_match;
if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
if (expr.is_constant()) {
return rhs_fuzzy_match(expr.evaluate(compressor), rhs_exact_match);
}
if (!(lhs instanceof AST_SymbolRef)) return false;
if (expr.has_side_effects(compressor)) return false;
var circular;
var def = lhs.definition();
expr.walk(new TreeWalker(function(node) {
if (circular) return true;
if (node instanceof AST_SymbolRef && node.definition() === def) {
circular = true;
}
}));
return !circular && rhs_exact_match;
}
function rhs_exact_match(node) {
return rhs.equivalent_to(node);
}
function rhs_fuzzy_match(value, fallback) {
return function(node, tw) {
if (tw.in_boolean_context()) {
if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
return true;
}
if (node.is_constant()) {
return !node.evaluate(compressor) == !value;
}
}
return fallback(node);
};
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (candidate instanceof AST_VarDef) {
lvalues[candidate.name.name] = lhs;
}
var tw = new TreeWalker(function(node) {
var sym = root_expr(node);
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
expr.walk(tw);
return lvalues;
}
@@ -1401,11 +1453,11 @@ merge(Compressor.prototype, {
}
function is_lhs_local(lhs) {
while (lhs instanceof AST_PropAccess) lhs = lhs.expression;
return lhs instanceof AST_SymbolRef
&& lhs.definition().scope === scope
var sym = root_expr(lhs);
return sym instanceof AST_SymbolRef
&& sym.definition().scope === scope
&& !(in_loop
&& (lhs.name in lvalues
&& (sym.name in lvalues && lvalues[sym.name] !== lhs
|| candidate instanceof AST_Unary
|| candidate instanceof AST_Assign && candidate.operator != "="));
}
@@ -1427,12 +1479,22 @@ merge(Compressor.prototype, {
return false;
}
function symbol_in_lvalues(sym) {
var lvalue = lvalues[sym.name];
if (!lvalue) return;
if (lvalue !== lhs) return true;
scan_rhs = false;
}
function may_modify(sym) {
var def = sym.definition();
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
if (def.scope !== scope) return true;
return !all(def.references, function(ref) {
return ref.scope === scope;
var s = ref.scope;
// "block" scope within AST_Catch
if (s.TYPE == "Scope") s = s.parent_scope;
return s === scope;
});
}
@@ -1981,6 +2043,28 @@ merge(Compressor.prototype, {
&& !node.expression.has_side_effects(compressor);
}
// is_truthy()
// return true if `!!node === true`
(function(def) {
def(AST_Node, return_false);
def(AST_Array, return_true);
def(AST_Assign, function() {
return this.operator == "=" && this.right.is_truthy();
});
def(AST_Lambda, return_true);
def(AST_Object, return_true);
def(AST_RegExp, return_true);
def(AST_Sequence, function() {
return this.tail_node().is_truthy();
});
def(AST_SymbolRef, function() {
var fixed = this.fixed_value();
return fixed && fixed.is_truthy();
});
})(function(node, func) {
node.DEFMETHOD("is_truthy", func);
});
// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
@@ -2314,7 +2398,11 @@ merge(Compressor.prototype, {
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
var val = this._eval(compressor, 1);
var cached = [];
var val = this._eval(compressor, cached, 1);
cached.forEach(function(node) {
delete node._eval;
});
if (!val || val instanceof RegExp) return val;
if (typeof val == "function" || typeof val == "object") return this;
return val;
@@ -2350,12 +2438,12 @@ merge(Compressor.prototype, {
}
return this;
});
def(AST_Array, function(compressor, depth) {
def(AST_Array, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) {
var element = this.elements[i];
var value = element._eval(compressor, depth);
var value = element._eval(compressor, cached, depth);
if (element === value) return this;
elements.push(value);
}
@@ -2363,7 +2451,7 @@ merge(Compressor.prototype, {
}
return this;
});
def(AST_Object, function(compressor, depth) {
def(AST_Object, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var val = {};
for (var i = 0, len = this.properties.length; i < len; i++) {
@@ -2372,14 +2460,14 @@ merge(Compressor.prototype, {
if (key instanceof AST_Symbol) {
key = key.name;
} else if (key instanceof AST_Node) {
key = key._eval(compressor, depth);
key = key._eval(compressor, cached, depth);
if (key === prop.key) return this;
}
if (typeof Object.prototype[key] === 'function') {
return this;
}
if (prop.value instanceof AST_Function) continue;
val[key] = prop.value._eval(compressor, depth);
val[key] = prop.value._eval(compressor, cached, depth);
if (val[key] === prop.value) return this;
}
return val;
@@ -2387,7 +2475,7 @@ merge(Compressor.prototype, {
return this;
});
var non_converting_unary = makePredicate("! typeof void");
def(AST_UnaryPrefix, function(compressor, depth) {
def(AST_UnaryPrefix, function(compressor, cached, depth) {
var e = this.expression;
// Function would be evaluated to an array and so typeof would
// incorrectly return 'object'. Hence making is a special case.
@@ -2399,7 +2487,7 @@ merge(Compressor.prototype, {
return typeof function(){};
}
if (!non_converting_unary(this.operator)) depth++;
e = e._eval(compressor, depth);
e = e._eval(compressor, cached, depth);
if (e === this.expression) return this;
switch (this.operator) {
case "!": return !e;
@@ -2416,11 +2504,11 @@ merge(Compressor.prototype, {
return this;
});
var non_converting_binary = makePredicate("&& || === !==");
def(AST_Binary, function(compressor, depth) {
def(AST_Binary, function(compressor, cached, depth) {
if (!non_converting_binary(this.operator)) depth++;
var left = this.left._eval(compressor, depth);
var left = this.left._eval(compressor, cached, depth);
if (left === this.left) return this;
var right = this.right._eval(compressor, depth);
var right = this.right._eval(compressor, cached, depth);
if (right === this.right) return this;
var result;
switch (this.operator) {
@@ -2454,27 +2542,28 @@ merge(Compressor.prototype, {
}
return result;
});
def(AST_Conditional, function(compressor, depth) {
var condition = this.condition._eval(compressor, depth);
def(AST_Conditional, function(compressor, cached, depth) {
var condition = this.condition._eval(compressor, cached, depth);
if (condition === this.condition) return this;
var node = condition ? this.consequent : this.alternative;
var value = node._eval(compressor, depth);
var value = node._eval(compressor, cached, depth);
return value === node ? this : value;
});
def(AST_SymbolRef, function(compressor, depth) {
def(AST_SymbolRef, function(compressor, cached, depth) {
var fixed = this.fixed_value();
if (!fixed) return this;
var value;
if (HOP(fixed, "_eval")) {
if (cached.indexOf(fixed) >= 0) {
value = fixed._eval();
} else {
this._eval = return_this;
value = fixed._eval(compressor, depth);
value = fixed._eval(compressor, cached, depth);
delete this._eval;
if (value === fixed) return this;
fixed._eval = function() {
return value;
};
cached.push(fixed);
}
if (value && typeof value == "object") {
var escaped = this.definition().escaped;
@@ -2509,11 +2598,11 @@ merge(Compressor.prototype, {
],
};
convert_to_predicate(static_values);
def(AST_PropAccess, function(compressor, depth) {
def(AST_PropAccess, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var key = this.property;
if (key instanceof AST_Node) {
key = key._eval(compressor, depth);
key = key._eval(compressor, cached, depth);
if (key === this.property) return this;
}
var exp = this.expression;
@@ -2522,7 +2611,7 @@ merge(Compressor.prototype, {
if (!(static_values[exp.name] || return_false)(key)) return this;
val = global_objs[exp.name];
} else {
val = exp._eval(compressor, depth + 1);
val = exp._eval(compressor, cached, depth + 1);
if (!val || val === exp || !HOP(val, key)) return this;
if (typeof val == "function") switch (key) {
case "name":
@@ -2537,12 +2626,12 @@ merge(Compressor.prototype, {
}
return this;
});
def(AST_Call, function(compressor, depth) {
def(AST_Call, function(compressor, cached, depth) {
var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property;
if (key instanceof AST_Node) {
key = key._eval(compressor, depth);
key = key._eval(compressor, cached, depth);
if (key === exp.property) return this;
}
var val;
@@ -2551,13 +2640,13 @@ merge(Compressor.prototype, {
if (!(static_fns[e.name] || return_false)(key)) return this;
val = global_objs[e.name];
} else {
val = e._eval(compressor, depth + 1);
val = e._eval(compressor, cached, depth + 1);
if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
}
var args = [];
for (var i = 0, len = this.args.length; i < len; i++) {
var arg = this.args[i];
var value = arg._eval(compressor, depth);
var value = arg._eval(compressor, cached, depth);
if (arg === value) return this;
args.push(value);
}
@@ -3014,6 +3103,16 @@ merge(Compressor.prototype, {
return self;
});
OPT(AST_Lambda, function(self, compressor){
tighten_body(self.body, compressor);
if (compressor.option("side_effects")
&& self.body.length == 1
&& self.body[0] === compressor.has_directive("use strict")) {
self.body.length = 0;
}
return self;
});
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
if (!compressor.option("unused")) return;
if (compressor.has_directive("use asm")) return;
@@ -3039,6 +3138,8 @@ merge(Compressor.prototype, {
var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
var fixed_ids = Object.create(null);
var value_read = Object.create(null);
var value_modified = Object.create(null);
if (self instanceof AST_Toplevel && compressor.top_retain) {
self.variables.each(function(def) {
if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
@@ -3306,6 +3407,17 @@ merge(Compressor.prototype, {
);
self.transform(tt);
function verify_safe_usage(def, read, modified) {
if (def.id in in_use_ids) return;
if (read && modified) {
in_use_ids[def.id] = true;
in_use.push(def);
} else {
value_read[def.id] = read;
value_modified[def.id] = modified;
}
}
function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef
@@ -3315,8 +3427,18 @@ merge(Compressor.prototype, {
});
if (node instanceof AST_Assign) {
node.right.walk(tw);
if (node.left === sym && !node_def.chained && sym.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
if (node.left === sym) {
if (!node_def.chained && sym.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
if (!node.write_only) {
verify_safe_usage(node_def, true, value_modified[node_def.id]);
}
} else {
var fixed = sym.fixed_value();
if (!fixed || !fixed.is_constant()) {
verify_safe_usage(node_def, value_read[node_def.id], true);
}
}
}
return true;
@@ -3404,8 +3526,9 @@ merge(Compressor.prototype, {
var defs = [];
vars.each(function(def, name){
if (self instanceof AST_Lambda
&& find_if(function(x){ return x.name == def.name.name },
self.argnames)) {
&& !all(self.argnames, function(argname) {
return argname.name != name;
})) {
vars.del(name);
} else {
def = def.clone();
@@ -3485,7 +3608,7 @@ merge(Compressor.prototype, {
AST_Scope.DEFMETHOD("make_var_name", function(prefix) {
var var_names = this.var_names();
prefix = prefix.replace(/[^a-z_$]+/ig, "_");
prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
var name = prefix;
for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
var_names[name] = true;
@@ -3502,8 +3625,9 @@ merge(Compressor.prototype, {
var sym = node.name, def, value;
if (sym.scope === self
&& (def = sym.definition()).escaped != 1
&& !def.single_use
&& !def.assignments
&& !def.direct_access
&& !def.single_use
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
@@ -3583,7 +3707,15 @@ merge(Compressor.prototype, {
if (this.expression instanceof AST_Function
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
var node = this.clone();
node.expression.process_expression(false, compressor);
var exp = node.expression;
exp.process_expression(false, compressor);
exp.walk(new TreeWalker(function(node) {
if (node instanceof AST_Return && node.value) {
node.value = node.value.drop_side_effect_free(compressor);
return true;
}
if (node instanceof AST_Scope && node !== exp) return true;
}));
return node;
}
return this;
@@ -3619,10 +3751,7 @@ merge(Compressor.prototype, {
return this;
}
this.write_only = true;
while (left instanceof AST_PropAccess) {
left = left.expression;
}
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) {
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
}
return this;
@@ -3718,7 +3847,7 @@ merge(Compressor.prototype, {
OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self;
var cond = self.condition.tail_node().evaluate(compressor);
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!(cond instanceof AST_Node)) {
if (cond) return make_node(AST_For, self, {
body: make_node(AST_BlockStatement, self.body, {
@@ -3840,9 +3969,11 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
}
if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (!cond) {
if (cond instanceof AST_Node) {
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
}
if (!cond) {
if (compressor.option("dead_code")) {
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
if (self.init instanceof AST_Statement) {
@@ -3857,6 +3988,16 @@ merge(Compressor.prototype, {
}));
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
} else if (self.condition && !(cond instanceof AST_Node)) {
self.body = make_node(AST_BlockStatement, self.body, {
body: [
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}),
self.body
]
});
self.condition = null;
}
}
return if_break_in_loop(self, compressor);
@@ -3877,7 +4018,9 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (cond instanceof AST_Node) {
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
}
if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
var body = [];
@@ -4404,7 +4547,6 @@ merge(Compressor.prototype, {
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
base54.reset();
ast.compute_char_frequency(mangle);
ast.mangle_names(mangle);
var fun;
@@ -4634,8 +4776,10 @@ merge(Compressor.prototype, {
var var_def = stat.definitions[j];
var name = var_def.name;
append_var(decls, expressions, name, var_def.value);
if (in_loop) {
var def = name.definition();
if (in_loop && all(fn.argnames, function(argname) {
return argname.name != name.name;
})) {
var def = fn.variables.get(name.name);
var sym = make_node(AST_SymbolRef, name, name);
def.references.push(sym);
expressions.splice(pos++, 0, make_node(AST_Assign, var_def, {
@@ -4762,8 +4906,10 @@ merge(Compressor.prototype, {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.in_boolean_context()) {
switch (self.operator) {
if (compressor.option("booleans")) {
if (self.operator == "!" && e.is_truthy()) {
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
} else if (compressor.in_boolean_context()) switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context
@@ -4949,7 +5095,7 @@ merge(Compressor.prototype, {
}
break;
}
if (self.operator == "+" && compressor.in_boolean_context()) {
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") {
@@ -4976,11 +5122,9 @@ merge(Compressor.prototype, {
});
self = best_of(compressor, self, negated);
}
if (compressor.option("unsafe_comps")) {
switch (self.operator) {
case "<": reverse(">"); break;
case "<=": reverse(">="); break;
}
switch (self.operator) {
case ">": reverse("<"); break;
case ">=": reverse("<="); break;
}
}
if (self.operator == "+") {
@@ -5006,7 +5150,7 @@ merge(Compressor.prototype, {
if (compressor.option("evaluate")) {
switch (self.operator) {
case "&&":
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
var ll = fuzzy_eval(self.left);
if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
@@ -5016,7 +5160,7 @@ merge(Compressor.prototype, {
}
var rr = self.right.evaluate(compressor);
if (!rr) {
if (compressor.in_boolean_context()) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
@@ -5025,7 +5169,8 @@ merge(Compressor.prototype, {
} else self.falsy = true;
} else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) {
if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
@@ -5041,7 +5186,7 @@ merge(Compressor.prototype, {
}
break;
case "||":
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
var ll = fuzzy_eval(self.left);
if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
@@ -5052,12 +5197,13 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor);
if (!rr) {
var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) {
if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
} else if (!(rr instanceof AST_Node)) {
if (compressor.in_boolean_context()) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
@@ -5279,6 +5425,13 @@ merge(Compressor.prototype, {
return best_of(compressor, ev, self);
}
return self;
function fuzzy_eval(node) {
if (node.truthy) return true;
if (node.falsy) return false;
if (node.is_truthy()) return true;
return node.evaluate(compressor);
}
});
function recursive_ref(compressor, def) {
@@ -5574,15 +5727,13 @@ merge(Compressor.prototype, {
expressions.push(self);
return make_sequence(self, expressions);
}
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent);
} else {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
}
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
} else if (!(cond instanceof AST_Node)) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
}
var negated = cond.negate(compressor, first_in_statement(compressor));
if (best_of(compressor, cond, negated) === negated) {
@@ -5664,6 +5815,18 @@ merge(Compressor.prototype, {
consequent
]).optimize(compressor);
}
// x ? (y, w) : (z, w) --> x ? y : z, w
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
return make_sequence(self, [
make_node(AST_Conditional, self, {
condition: self.condition,
consequent: pop_seq(consequent),
alternative: pop_seq(alternative)
}),
consequent.tail_node()
]).optimize(compressor);
}
// x ? y || z : z --> x && y || z
if (consequent instanceof AST_Binary
&& consequent.operator == "||"
@@ -5678,7 +5841,7 @@ merge(Compressor.prototype, {
right: alternative
}).optimize(compressor);
}
var in_bool = compressor.in_boolean_context();
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (is_true(self.consequent)) {
if (is_false(self.alternative)) {
// c ? true : false ---> !!c
@@ -5766,35 +5929,39 @@ merge(Compressor.prototype, {
}
}
}
function pop_seq(node) {
if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
value: 0
});
return make_sequence(node, node.expressions.slice(0, -1));
}
});
OPT(AST_Boolean, function(self, compressor){
if (!compressor.option("booleans")) return self;
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value
});
if (compressor.option("booleans")) {
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "=="
|| p.operator == "!=")) {
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
operator : p.operator,
value : self.value,
file : p.start.file,
line : p.start.line,
col : p.start.col,
});
return make_node(AST_Number, self, {
value: +self.value
});
}
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
operator : p.operator,
value : self.value,
file : p.start.file,
line : p.start.line,
col : p.start.col,
});
return make_node(AST_Number, self, {
value: +self.value
});
}
return self;
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
});
});
OPT(AST_Sub, function(self, compressor){
@@ -5867,6 +6034,31 @@ merge(Compressor.prototype, {
});
}
}
var fn;
if (compressor.option("arguments")
&& expr instanceof AST_SymbolRef
&& expr.name == "arguments"
&& expr.definition().orig.length == 1
&& (fn = expr.scope) instanceof AST_Lambda
&& prop instanceof AST_Number) {
var index = prop.getValue();
var argname = fn.argnames[index];
if (!argname && !compressor.option("keep_fargs")) {
while (index >= fn.argnames.length) {
argname = make_node(AST_SymbolFunarg, fn, {
name: fn.make_var_name("argument_" + fn.argnames.length),
scope: fn
});
fn.argnames.push(argname);
fn.enclosed.push(fn.def_variable(argname));
}
}
if (argname) {
var sym = make_node(AST_SymbolRef, self, argname);
sym.reference({});
return sym;
}
}
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
@@ -5978,19 +6170,6 @@ merge(Compressor.prototype, {
return self;
});
function literals_in_boolean_context(self, compressor) {
if (compressor.in_boolean_context()) {
return best_of(compressor, self, make_sequence(self, [
self,
make_node(AST_True, self)
]).optimize(compressor));
}
return self;
};
OPT(AST_Array, literals_in_boolean_context);
OPT(AST_Object, literals_in_boolean_context);
OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){
if (self.value && is_undefined(self.value, compressor)) {
self.value = null;

View File

@@ -150,7 +150,6 @@ function minify(files, options) {
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
base54.reset();
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
}

View File

@@ -180,6 +180,17 @@
end : my_end_token(M)
};
if (val === null) return new AST_Null(args);
var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags);
args.value.raw_source = rx.pattern;
return new AST_RegExp(args);
} else if (rx) {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
return new AST_RegExp(args);
}
switch (typeof val) {
case "string":
args.value = val;
@@ -189,16 +200,6 @@
return new AST_Number(args);
case "boolean":
return new (val ? AST_True : AST_False)(args);
default:
var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags).toString();
} else {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
}
return new AST_RegExp(args);
}
},
Identifier: function(M) {
@@ -410,14 +411,15 @@
});
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var value = M.value;
var flags = M.value.toString().match(/[gimuy]*$/)[0];
var value = "/" + M.value.raw_source + "/" + flags;
return {
type: "Literal",
value: value,
raw: value.toString(),
raw: value,
regex: {
pattern: value.source,
flags: value.toString().match(/[gimuy]*$/)[0]
pattern: M.value.raw_source,
flags: flags
}
};
});
@@ -564,6 +566,21 @@
FROM_MOZ_STACK = [];
var ast = from_moz(node);
FROM_MOZ_STACK = save_stack;
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_LabelRef) {
for (var level = 0, parent; parent = this.parent(level); level++) {
if (parent instanceof AST_Scope) break;
if (parent instanceof AST_LabeledStatement && parent.label.name == node.name) {
node.thedef = parent.label;
break;
}
}
if (!node.thedef) {
var s = node.start;
js_error("Undefined label " + node.name, s.file, s.line, s.col, s.pos);
}
}
}));
return ast;
};

View File

@@ -56,7 +56,7 @@ function OutputStream(options) {
options = defaults(options, {
ascii_only : false,
beautify : false,
bracketize : false,
braces : false,
comments : false,
ie8 : false,
indent_level : 4,
@@ -178,7 +178,7 @@ function OutputStream(options) {
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script) {
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
@@ -576,6 +576,7 @@ function OutputStream(options) {
indentation : function() { return indentation },
current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width },
has_parens : function() { return OUTPUT[OUTPUT.length - 1] == "(" },
newline : newline,
print : print,
space : space,
@@ -683,7 +684,7 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
PARENS(AST_Function, function(output){
if (first_in_statement(output)) {
if (!output.has_parens() && first_in_statement(output)) {
return true;
}
@@ -704,7 +705,9 @@ function OutputStream(options) {
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, first_in_statement);
PARENS(AST_Object, function(output){
return !output.has_parens() && first_in_statement(output);
});
PARENS(AST_Unary, function(output){
var p = output.parent();
@@ -883,21 +886,22 @@ function OutputStream(options) {
self.body.print(output);
output.semicolon();
});
function print_bracketed(self, output, allow_directives) {
function print_braced_empty(self, output) {
output.print("{");
output.with_indent(output.next_indent(), function() {
output.append_comments(self, true);
});
output.print("}");
}
function print_braced(self, output, allow_directives) {
if (self.body.length > 0) {
output.with_block(function() {
display_body(self.body, false, output, allow_directives);
});
} else {
output.print("{");
output.with_indent(output.next_indent(), function() {
output.append_comments(self, true);
});
output.print("}");
}
} else print_braced_empty(self, output);
};
DEFPRINT(AST_BlockStatement, function(self, output){
print_bracketed(self, output);
print_braced(self, output);
});
DEFPRINT(AST_EmptyStatement, function(self, output){
output.semicolon();
@@ -992,7 +996,7 @@ function OutputStream(options) {
});
});
output.space();
print_bracketed(self, output, true);
print_braced(self, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
@@ -1033,7 +1037,7 @@ function OutputStream(options) {
/* -----[ if ]----- */
function make_then(self, output) {
var b = self.body;
if (output.option("bracketize")
if (output.option("braces")
|| output.option("ie8") && b instanceof AST_Do)
return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single
@@ -1042,7 +1046,7 @@ function OutputStream(options) {
// IF having an ELSE clause where the THEN clause ends in an
// IF *without* an ELSE block (then the outer ELSE would refer
// to the inner IF). This function checks for this case and
// adds the block brackets if needed.
// adds the block braces if needed.
if (!b) return output.force_semicolon();
while (true) {
if (b instanceof AST_If) {
@@ -1089,7 +1093,7 @@ function OutputStream(options) {
});
output.space();
var last = self.body.length - 1;
if (last < 0) output.print("{}");
if (last < 0) print_braced_empty(self, output);
else output.with_block(function(){
self.body.forEach(function(branch, i){
output.indent(true);
@@ -1123,7 +1127,7 @@ function OutputStream(options) {
DEFPRINT(AST_Try, function(self, output){
output.print("try");
output.space();
print_bracketed(self, output);
print_braced(self, output);
if (self.bcatch) {
output.space();
self.bcatch.print(output);
@@ -1140,12 +1144,12 @@ function OutputStream(options) {
self.argname.print(output);
});
output.space();
print_bracketed(self, output);
print_braced(self, output);
});
DEFPRINT(AST_Finally, function(self, output){
output.print("finally");
output.space();
print_bracketed(self, output);
print_braced(self, output);
});
/* -----[ var/const ]----- */
@@ -1344,7 +1348,7 @@ function OutputStream(options) {
});
output.newline();
});
else output.print("{}");
else print_braced_empty(self, output);
});
function print_property_name(key, quote, output) {
@@ -1416,7 +1420,7 @@ function OutputStream(options) {
});
function force_statement(stat, output) {
if (output.option("bracketize")) {
if (output.option("braces")) {
make_block(stat, output);
} else {
if (!stat || stat instanceof AST_EmptyStatement)
@@ -1480,47 +1484,52 @@ function OutputStream(options) {
/* -----[ source map generators ]----- */
function DEFMAP(nodetype, generator) {
nodetype.DEFMETHOD("add_source_map", function(stream){
generator(this, stream);
nodetype.forEach(function(nodetype) {
nodetype.DEFMETHOD("add_source_map", generator);
});
};
}
// We could easily add info for ALL nodes, but it seems to me that
// would be quite wasteful, hence this noop in the base class.
DEFMAP(AST_Node, noop);
function basic_sourcemap_gen(self, output) {
output.add_mapping(self.start);
};
DEFMAP([
// We could easily add info for ALL nodes, but it seems to me that
// would be quite wasteful, hence this noop in the base class.
AST_Node,
// since the label symbol will mark it
AST_LabeledStatement,
AST_Toplevel,
], noop);
// XXX: I'm not exactly sure if we need it for all of these nodes,
// or if we should add even more.
DEFMAP(AST_Directive, basic_sourcemap_gen);
DEFMAP(AST_Debugger, basic_sourcemap_gen);
DEFMAP(AST_Symbol, basic_sourcemap_gen);
DEFMAP(AST_Jump, basic_sourcemap_gen);
DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
DEFMAP(AST_Lambda, basic_sourcemap_gen);
DEFMAP(AST_Switch, basic_sourcemap_gen);
DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
DEFMAP(AST_Toplevel, noop);
DEFMAP(AST_New, basic_sourcemap_gen);
DEFMAP(AST_Try, basic_sourcemap_gen);
DEFMAP(AST_Catch, basic_sourcemap_gen);
DEFMAP(AST_Finally, basic_sourcemap_gen);
DEFMAP(AST_Definitions, basic_sourcemap_gen);
DEFMAP(AST_Constant, basic_sourcemap_gen);
DEFMAP(AST_ObjectSetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectGetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectProperty, function(self, output){
output.add_mapping(self.start, self.key);
DEFMAP([
AST_Array,
AST_BlockStatement,
AST_Catch,
AST_Constant,
AST_Debugger,
AST_Definitions,
AST_Directive,
AST_Finally,
AST_Jump,
AST_Lambda,
AST_New,
AST_Object,
AST_StatementWithBody,
AST_Symbol,
AST_Switch,
AST_SwitchBranch,
AST_Try,
], function(output) {
output.add_mapping(this.start);
});
DEFMAP([
AST_ObjectGetter,
AST_ObjectSetter,
], function(output) {
output.add_mapping(this.start, this.key.name);
});
DEFMAP([ AST_ObjectProperty ], function(output) {
output.add_mapping(this.start, this.key);
});
})();

View File

@@ -969,7 +969,9 @@ function parse($TEXT, options) {
function labeled_statement() {
var label = as_symbol(AST_Label);
if (find_if(function(l){ return l.name == label.name }, S.labels)) {
if (!all(S.labels, function(l) {
return l.name != label.name;
})) {
// ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a

View File

@@ -74,17 +74,13 @@ SymbolDef.prototype = {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
} else if (!this.mangled_name && !this.unmangleable(options)) {
var def;
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
} else {
this.mangled_name = next_mangled_name(this.scope, options, this);
}
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
@@ -104,7 +100,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Catch) {
@@ -119,25 +114,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
descend();
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
@@ -168,22 +150,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
node.reference(options);
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
});
self.walk(tw);
@@ -325,56 +298,62 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init){
return symbol.thedef = def;
});
function next_mangled(scope, options) {
var ext = scope.enclosed;
out: while (true) {
var m = base54(++scope.cname);
if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling.
if (member(m, options.reserved)) continue;
// we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in
// inner scopes.
for (var i = ext.length; --i >= 0;) {
var sym = ext[i];
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
if (m == name) continue out;
}
return m;
function names_in_use(scope, options) {
var names = scope.names_in_use;
if (!names) {
scope.names_in_use = names = Object.create(scope.mangled_names || null);
scope.cname_holes = [];
scope.enclosed.forEach(function(def) {
if (def.unmangleable(options)) names[def.name] = true;
});
}
return names;
}
AST_Scope.DEFMETHOD("next_mangled", function(options){
return next_mangled(this, options);
});
AST_Toplevel.DEFMETHOD("next_mangled", function(options){
var name;
do {
name = next_mangled(this, options);
} while (member(name, this.mangled_names));
return name;
});
AST_Function.DEFMETHOD("next_mangled", function(options, def){
function next_mangled_name(scope, options, def) {
var in_use = names_in_use(scope, options);
var holes = scope.cname_holes;
var names = Object.create(null);
// #179, #326
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
// the function's mangled_name is null when keep_fnames is true
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = next_mangled(this, options);
if (!tricky_name || tricky_name != name)
return name;
if (scope instanceof AST_Function && scope.name && def.orig[0] instanceof AST_SymbolFunarg) {
var tricky_def = scope.name.definition();
// the function's mangled_name is null when keep_fnames is true
names[tricky_def.mangled_name || tricky_def.name] = true;
}
});
var scopes = [ scope ];
def.references.forEach(function(sym) {
var scope = sym.scope;
do {
if (scopes.indexOf(scope) < 0) {
for (var name in names_in_use(scope, options)) {
names[name] = true;
}
scopes.push(scope);
} else break;
} while (scope = scope.parent_scope);
});
var name;
for (var i = 0, len = holes.length; i < len; i++) {
name = base54(holes[i]);
if (names[name]) continue;
holes.splice(i, 1);
scope.names_in_use[name] = true;
return name;
}
while (true) {
name = base54(++scope.cname);
if (in_use[name] || !is_identifier(name) || member(name, options.reserved)) continue;
if (!names[name]) break;
holes.push(scope.cname);
}
scope.names_in_use[name] = true;
if (options.ie8 && def.orig[0] instanceof AST_SymbolLambda) {
names_in_use(scope.parent_scope, options)[name] = true;
}
return name;
}
AST_Symbol.DEFMETHOD("unmangleable", function(options){
var def = this.definition();
@@ -397,7 +376,7 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
function _default_mangler_options(options) {
options = defaults(options, {
eval : false,
ie8 : false,
@@ -409,28 +388,25 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
// Never mangle arguments
push_uniq(options.reserved, "arguments");
return options;
});
}
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
options = _default_mangler_options(options);
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
// the AST_SymbolDeclaration that it points to).
var lname = -1;
var to_mangle = [];
var mangled_names = this.mangled_names = [];
if (options.cache) {
this.globals.each(collect);
if (options.cache.props) {
options.cache.props.each(function(mangled_name) {
push_uniq(mangled_names, mangled_name);
});
}
if (options.cache && options.cache.props) {
var mangled_names = this.mangled_names = Object.create(null);
options.cache.props.each(function(mangled_name) {
mangled_names[mangled_name] = true;
});
}
var redefined = [];
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_LabeledStatement) {
// lname is incremented when we get to the AST_Label
@@ -440,8 +416,12 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Scope) {
node.variables.each(collect);
return;
descend();
if (options.cache && node instanceof AST_Toplevel) {
node.globals.each(mangle);
}
node.variables.each(mangle);
return true;
}
if (node instanceof AST_Label) {
var name;
@@ -449,17 +429,31 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name;
return true;
}
if (!options.ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition());
return;
if (!options.ie8 && node instanceof AST_Catch) {
var def = node.argname.definition();
var redef = def.redefined();
if (redef) {
redefined.push(def);
reference(node.argname);
def.references.forEach(reference);
}
descend();
if (!redef) mangle(def);
return true;
}
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
});
this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) });
redefined.forEach(mangle);
function collect(symbol) {
if (!member(symbol.name, options.reserved)) {
to_mangle.push(symbol);
function mangle(def) {
if (!member(def.name, options.reserved)) {
def.mangle(options);
}
}
});
@@ -490,7 +484,7 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
base54.reset();
base54.sort();
options = this._default_mangler_options(options);
options = _default_mangler_options(options);
var avoid = this.find_colliding_names(options);
var cname = 0;
this.globals.each(rename);
@@ -528,7 +522,8 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
options = _default_mangler_options(options);
base54.reset();
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);

View File

@@ -1,10 +1,9 @@
{
"name": "uglify-js",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.3.12",
"version": "3.3.17",
"engines": {
"node": ">=0.8.0"
},
@@ -24,16 +23,39 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.14.1",
"commander": "~2.15.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.4.1",
"acorn": "~5.5.3",
"mocha": "~3.5.1",
"semver": "~5.5.0"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier", "es5"]
"keywords": [
"cli",
"compress",
"compressor",
"ecma",
"ecmascript",
"es",
"es5",
"javascript",
"js",
"jsmin",
"min",
"minification",
"minifier",
"minify",
"optimize",
"optimizer",
"pack",
"packer",
"parse",
"parser",
"uglifier",
"uglify"
]
}

View File

@@ -28,6 +28,7 @@ var remaining = 2 * urls.length;
function done() {
if (!--remaining) {
var failures = [];
var sum = { input: 0, output: 0, gzip: 0 };
urls.forEach(function(url) {
var info = results[url];
console.log();
@@ -40,6 +41,9 @@ function done() {
if (info.code) {
failures.push(url);
}
sum.input += info.input;
sum.output += info.output;
sum.gzip += info.gzip;
});
if (failures.length) {
console.error("Benchmark failed:");
@@ -47,6 +51,13 @@ function done() {
console.error(url);
});
process.exit(1);
} else {
console.log();
console.log("Subtotal");
console.log();
console.log("Original:", sum.input, "bytes");
console.log("Uglified:", sum.output, "bytes");
console.log("GZipped: ", sum.gzip, "bytes");
}
}
}

119
test/compress/arguments.js Normal file
View File

@@ -0,0 +1,119 @@
replace_index: {
options = {
arguments: true,
evaluate: true,
properties: true,
}
input: {
console.log(arguments && arguments[0]);
(function() {
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
(function(a, b) {
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
(function(arguments) {
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
(function() {
var arguments;
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
}
expect: {
console.log(arguments && arguments[0]);
(function() {
console.log(arguments[1], arguments[1], arguments.foo);
})("bar", 42);
(function(a, b) {
console.log(b, b, arguments.foo);
})("bar", 42);
(function(arguments) {
console.log(arguments[1], arguments[1], arguments.foo);
})("bar", 42);
(function() {
var arguments;
console.log(arguments[1], arguments[1], arguments.foo);
})("bar", 42);
}
expect_stdout: [
"undefined",
"42 42 undefined",
"42 42 undefined",
"a a undefined",
"42 42 undefined",
]
}
replace_index_keep_fargs: {
options = {
arguments: true,
evaluate: true,
keep_fargs: false,
properties: true,
}
input: {
console.log(arguments && arguments[0]);
(function() {
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
(function(a, b) {
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
(function(arguments) {
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
(function() {
var arguments;
console.log(arguments[1], arguments["1"], arguments["foo"]);
})("bar", 42);
}
expect: {
console.log(arguments && arguments[0]);
(function(argument_0, argument_1) {
console.log(argument_1, argument_1, arguments.foo);
})("bar", 42);
(function(a, b) {
console.log(b, b, arguments.foo);
})("bar", 42);
(function(arguments) {
console.log(arguments[1], arguments[1], arguments.foo);
})("bar", 42);
(function() {
var arguments;
console.log(arguments[1], arguments[1], arguments.foo);
})("bar", 42);
}
expect_stdout: [
"undefined",
"42 42 undefined",
"42 42 undefined",
"a a undefined",
"42 42 undefined",
]
}
modified: {
options = {
arguments: true,
}
input: {
(function(a, b) {
var c = arguments[0];
var d = arguments[1];
a = "foo";
b++;
console.log(a, b, c, d, arguments[0], arguments[1]);
})("bar", 42);
}
expect: {
(function(a, b) {
var c = a;
var d = b;
a = "foo";
b++;
console.log(a, b, c, d, a, b);
})("bar", 42);
}
expect_stdout: "foo 43 bar 42 foo 43"
}

View File

@@ -913,15 +913,15 @@ collapse_vars_unary: {
return delete x;
}
function f1(n) {
return n > +!!n
return +!!n < n;
}
function f2(n) {
var k = 7;
return k--
return k--;
}
function f3(n) {
var k = 7;
return ++k
return ++k;
}
function f4(n) {
var k = 8 - n;
@@ -2219,8 +2219,8 @@ unused_orig: {
console.log(function(b) {
var c = b;
for (var d in c) {
var a = c[0];
return --b + a;
var a;
return --b + c[0];
}
a && a.NaN;
}([2]), a);
@@ -3124,7 +3124,57 @@ issue_2425_3: {
expect_stdout: "15"
}
issue_2437: {
issue_2437_1: {
options = {
collapse_vars: true,
conditionals: true,
inline: true,
join_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
function bar() {
if (xhrDesc) {
var req = new XMLHttpRequest();
var result = !!req.onreadystatechange;
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
return result;
} else {
var req = new XMLHttpRequest();
var detectFunc = function () {};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
return result;
}
}
console.log(foo());
}
expect: {
console.log(function() {
if (xhrDesc) {
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
}
var req, detectFunc = function() {};
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
return req.onreadystatechange = null, result;
}());
}
}
issue_2437_2: {
options = {
collapse_vars: true,
conditionals: true,
@@ -3161,14 +3211,12 @@ issue_2437: {
}
expect: {
!function() {
if (xhrDesc) {
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
}
var req, detectFunc = function() {};
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
if (xhrDesc)
return (req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {});
var req;
(req = new XMLHttpRequest).onreadystatechange = function(){},
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
req.onreadystatechange = null;
}();
}
@@ -4665,3 +4713,617 @@ issue_2931: {
}
expect_stdout: "undefined"
}
issue_2954_1: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS", b;
try {
do {
b = function() {
throw 0;
}();
a = "FAIL";
b && b.c;
} while (0);
} catch (e) {
}
console.log(a);
}
expect: {
var a = "PASS", b;
try {
do {
b = function() {
throw 0;
}();
a = "FAIL";
b && b.c;
} while (0);
} catch (e) {
}
console.log(a);
}
expect_stdout: "PASS"
}
issue_2954_2: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL_1", b;
try {
throw 0;
} catch (e) {
do {
b = function() {
throw new Error("PASS");
}();
a = "FAIL_2";
b && b.c;
} while (0);
}
console.log(a);
}
expect: {
var a = "FAIL_1", b;
try {
throw 0;
} catch (e) {
do {
a = "FAIL_2";
(b = function() {
throw new Error("PASS");
}()) && b.c;
} while (0);
}
console.log(a);
}
expect_stdout: Error("PASS")
}
issue_2954_3: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL_1", b;
try {
} finally {
do {
b = function() {
throw new Error("PASS");
}();
a = "FAIL_2";
b && b.c;
} while (0);
}
console.log(a);
}
expect: {
var a = "FAIL_1", b;
try {
} finally {
do {
a = "FAIL_2";
(b = function() {
throw new Error("PASS");
}()) && b.c;
} while (0);
}
console.log(a);
}
expect_stdout: Error("PASS")
}
collapse_rhs_conditional_1: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS", b = "FAIL";
b = a;
"function" == typeof f && f(a);
console.log(a, b);
}
expect: {
var a = "PASS", b = "FAIL";
b = a;
"function" == typeof f && f(a);
console.log(a, b);
}
expect_stdout: "PASS PASS"
}
collapse_rhs_conditional_2: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL", b;
while ((a = "PASS", --b) && "PASS" == b);
console.log(a, b);
}
expect: {
var a = "FAIL", b;
while ((a = "PASS", --b) && "PASS" == b);
console.log(a, b);
}
expect_stdout: "PASS NaN"
}
collapse_rhs_lhs_1: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
new function() {
this[c++] = 1;
c += 1;
}();
console.log(c);
}
expect: {
var c = 0;
new function() {
this[c++] = 1;
c += 1;
}();
console.log(c);
}
expect_stdout: "2"
}
collapse_rhs_lhs_2: {
options = {
collapse_vars: true,
}
input: {
var b = 1;
(function f(f) {
f = b;
f[b] = 0;
})();
console.log("PASS");
}
expect: {
var b = 1;
(function f(f) {
f = b;
f[b] = 0;
})();
console.log("PASS");
}
expect_stdout: "PASS"
}
collapse_rhs_side_effects: {
options = {
collapse_vars: true,
}
input: {
var a = 1, c = 0;
new function f() {
this[a-- && f()] = 1;
c += 1;
}();
console.log(c);
}
expect: {
var a = 1, c = 0;
new function f() {
this[a-- && f()] = 1;
c += 1;
}();
console.log(c);
}
expect_stdout: "2"
}
collapse_rhs_vardef: {
options = {
collapse_vars: true,
}
input: {
var a, b = 1;
a = --b + function c() {
var b;
c[--b] = 1;
}();
b |= a;
console.log(a, b);
}
expect: {
var a, b = 1;
a = --b + function c() {
var b;
c[--b] = 1;
}();
b |= a;
console.log(a, b);
}
expect_stdout: "NaN 0"
}
collapse_rhs_array: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = [];
b = [];
return [];
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
a = [];
b = [];
return [];
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "false false false"
}
collapse_rhs_boolean_1: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = !0;
b = !0;
return !0;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
return b = a = !0;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "true true true"
}
collapse_rhs_boolean_2: {
options = {
collapse_vars: true,
}
input: {
var a;
(function f1() {
a = function() {};
if (/foo/)
console.log(typeof a);
})();
console.log(function f2() {
a = [];
return !1;
}());
}
expect: {
var a;
(function f1() {
if (a = function() {})
console.log(typeof a);
})();
console.log(function f2() {
return !(a = []);
}());
}
expect_stdout: [
"function",
"false",
]
}
collapse_rhs_boolean_3: {
options = {
booleans: true,
collapse_vars: true,
conditionals: true,
}
input: {
var a, f, g, h, i, n, s, t, x, y;
if (x()) {
n = a;
} else if (y()) {
n = f();
} else if (s) {
i = false;
n = g(true);
} else if (t) {
i = false;
n = h(true);
} else {
n = [];
}
}
expect: {
var a, f, g, h, i, n, s, t, x, y;
n = x() ? a : y() ? f() : s ? g(!(i = !1)) : t ? h(!(i = !1)) : [];
}
}
collapse_rhs_function: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = function() {};
b = function() {};
return function() {};
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
a = function() {};
b = function() {};
return function() {};
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "false false false"
}
collapse_rhs_number: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = 42;
b = 42;
return 42;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
return b = a = 42;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "true true true"
}
collapse_rhs_object: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = {};
b = {};
return {};
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
a = {};
b = {};
return {};
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "false false false"
}
collapse_rhs_regexp: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = /bar/;
b = /bar/;
return /bar/;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
a = /bar/;
b = /bar/;
return /bar/;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "false false false"
}
collapse_rhs_string: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = "foo";
b = "foo";
return "foo";
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
return b = a = "foo";
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "true true true"
}
collapse_rhs_var: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = f;
b = f;
return f;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
return b = a = f;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "true true true"
}
collapse_rhs_this: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = this;
b = this;
return this;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
return b = a = this;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "true true true"
}
collapse_rhs_undefined: {
options = {
collapse_vars: true,
}
input: {
var a, b;
function f() {
a = void 0;
b = void 0;
return void 0;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect: {
var a, b;
function f() {
b = a = void 0;
return;
}
var c = f();
console.log(a === b, b === c, c === a);
}
expect_stdout: "true true true"
}
issue_2974: {
options = {
booleans: true,
collapse_vars: true,
evaluate: true,
loops: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
(function f(b) {
var a = 2;
do {
b && b[b];
b && (b.null = -4);
c++;
} while (b.null && --a > 0);
})(true);
console.log(c);
}
expect: {
var c = 0;
(function(b) {
var a = 2;
for (; b.null = -4, c++, b.null && --a > 0;);
})(!0),
console.log(c);
}
expect_stdout: "1"
}
issue_3032: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
console.log({
f: function() {
this.a = 42;
return [ this.a, !1 ];
}
}.f()[0]);
}
expect: {
console.log({
f: function() {
this.a = 42;
return [ this.a, !1 ];
}
}.f()[0]);
}
expect_stdout: "42"
}

View File

@@ -1,62 +1,42 @@
keep_comparisons: {
comparisons: {
options = {
comparisons: true,
unsafe_comps: false
}
input: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var obj1, obj2;
var result1 = obj1 <= obj2;
var result2 = obj1 < obj2;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
}
expect: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var obj1, obj2;
var result1 = obj1 <= obj2;
var result2 = obj1 < obj2;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
var result3 = obj2 <= obj1;
var result4 = obj2 < obj1;
}
}
keep_comparisons_with_unsafe_comps: {
unsafe_comps: {
options = {
comparisons: true,
unsafe_comps: true
conditionals: true,
unsafe_comps: true,
}
input: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var result1 = obj1 <= obj2;
var result2 = obj1 < obj2;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
var obj1, obj2;
obj1 <= obj2 ? f1() : g1();
obj1 < obj2 ? f2() : g2();
obj1 >= obj2 ? f3() : g3();
obj1 > obj2 ? f4() : g4();
}
expect: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var result1 = obj2 >= obj1;
var result2 = obj2 > obj1;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
var obj1, obj2;
obj2 < obj1 ? g1() : f1();
obj1 < obj2 ? f2() : g2();
obj1 < obj2 ? g3() : f3();
obj2 < obj1 ? f4() : g4();
}
}

View File

@@ -332,7 +332,7 @@ cond_7: {
x = 'foo';
x = 'foo';
x = (condition(), 20);
x = z ? 'fuji' : (condition(), 'fuji');
x = (z || condition(), 'fuji');
x = (condition(), 'foobar');
x = y ? a : b;
x = y ? 'foo' : 'fo';
@@ -703,10 +703,11 @@ ternary_boolean_alternative: {
trivial_boolean_ternary_expressions : {
options = {
booleans: true,
conditionals: true,
evaluate : true,
booleans : true
};
evaluate: true,
side_effects: true,
}
input: {
f('foo' in m ? true : false);
f('foo' in m ? false : true);

View File

@@ -1785,3 +1785,32 @@ issue_805_2: {
"bar",
]
}
issue_2995: {
options = {
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
function f(a) {
var b;
a.b = b = function() {};
b.c = "PASS";
}
var o = {};
f(o);
console.log(o.b.c);
}
expect: {
function f(a) {
var b;
a.b = b = function() {};
b.c = "PASS";
}
var o = {};
f(o);
console.log(o.b.c);
}
expect_stdout: "PASS"
}

View File

@@ -745,7 +745,7 @@ in_boolean_context: {
!b("foo"),
!b([1, 2]),
!b(/foo/),
![1, foo()],
(foo(), !1),
(foo(), !1)
);
}
@@ -1535,3 +1535,74 @@ issue_2926_2: {
}
expect_stdout: "function"
}
issue_2968: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
var c = "FAIL";
(function() {
(function(a, b) {
a <<= 0;
a && (a[(c = "PASS", 0 >>> (b += 1))] = 0);
})(42, -42);
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
b = -(a = 42),
void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0));
var a, b;
})();
console.log(c);
}
expect_stdout: "PASS"
}
truthy_conditionals: {
options = {
conditionals: true,
evaluate: true,
}
input: {
if (a = {}) x();
(b = /foo/) && y();
(c = function() {}) || z();
}
expect: {
a = {}, x();
b = /foo/, y();
c = function() {};
}
}
truthy_loops: {
options = {
evaluate: true,
loops: true,
}
input: {
while ([]) x();
do {
y();
} while(a = {});
}
expect: {
for (;;) {
[];
x();
}
for (;;) {
y();
a = {};
}
}
}

View File

@@ -2012,3 +2012,228 @@ issue_2898: {
}
expect_stdout: "2"
}
deduplicate_parenthesis: {
input: {
({}).a = b;
(({}).a = b)();
(function() {}).a = b;
((function() {}).a = b)();
}
expect_exact: "({}).a=b;({}.a=b)();(function(){}).a=b;(function(){}.a=b)();"
}
drop_lone_use_strict: {
options = {
side_effects: true,
}
input: {
function f1() {
"use strict";
}
function f2() {
"use strict";
function f3() {
"use strict";
}
}
(function f4() {
"use strict";
})();
}
expect: {
function f1() {
}
function f2() {
"use strict";
function f3() {
}
}
}
}
issue_3016_1: {
options = {
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
(function(a) {
return a[b];
var a;
})(3);
} while (0);
console.log(b);
}
expect: {
var b = 1;
do {
a = 3,
a[b];
} while(0);
var a;
console.log(b);
}
expect_stdout: "1"
}
issue_3016_2: {
options = {
dead_code: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
(function(a) {
return a[b];
try {
a = 2;
} catch (a) {
var a;
}
})(3);
} while (0);
console.log(b);
}
expect: {
var b = 1;
do {
a = 3,
a[b];
} while(0);
var a;
console.log(b);
}
expect_stdout: "1"
}
issue_3016_2_ie8: {
options = {
dead_code: true,
ie8: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
(function(a) {
return a[b];
try {
a = 2;
} catch (a) {
var a;
}
})(3);
} while (0);
console.log(b);
}
expect: {
var b = 1;
do {
a = 3,
a[b];
} while(0);
var a;
console.log(b);
}
expect_stdout: "1"
}
issue_3016_3: {
options = {
dead_code: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
console.log(function() {
return a ? "FAIL" : a = "PASS";
try {
a = 2;
} catch (a) {
var a;
}
}());
} while (b--);
}
expect: {
var b = 1;
do {
console.log((a = void 0, a ? "FAIL" : a = "PASS"));
} while(b--);
var a;
}
expect_stdout: [
"PASS",
"PASS",
]
}
issue_3016_3_ie8: {
options = {
dead_code: true,
ie8: true,
inline: true,
toplevel: true,
}
input: {
var b = 1;
do {
console.log(function() {
return a ? "FAIL" : a = "PASS";
try {
a = 2;
} catch (a) {
var a;
}
}());
} while (b--);
}
expect: {
var b = 1;
do {
console.log((a = void 0, a ? "FAIL" : a = "PASS"));
} while(b--);
var a;
}
expect_stdout: [
"PASS",
"PASS",
]
}
issue_3018: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 1, c = "PASS";
do {
(function() {
(function(a) {
a = 0 != (a && (c = "FAIL"));
})();
})();
} while (b--);
console.log(c);
}
expect: {
var b = 1, c = "PASS";
do {
a = void 0,
a = 0 != (a && (c = "FAIL"));
} while (b--);
var a;
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -239,14 +239,14 @@ name_collision_2: {
input: {
var o = {
p: 1,
0: function(x) {
"+": function(x) {
return x;
},
1: function(x) {
"-": function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5), o__$0, o__$1);
console.log(o.p === o.p, o["+"](4), o["-"](5), o__$0, o__$1);
}
expect: {
var o_p = 1,
@@ -273,14 +273,14 @@ name_collision_3: {
input: {
var o = {
p: 1,
0: function(x) {
"+": function(x) {
return x;
},
1: function(x) {
"-": function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5));
console.log(o.p === o.p, o["+"](4), o["-"](5));
}
expect: {
var o_p = 1,
@@ -686,3 +686,33 @@ undefined_key: {
}
expect_stdout: "3"
}
issue_3021: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
var a = 1, b = 2;
(function() {
b = a;
if (a++ + b--)
return 1;
return;
var b = {};
})();
console.log(a, b);
}
expect: {
var a = 1, b = 2;
(function() {
b = a;
if (a++ + b--)
return 1;
return;
var b = {};
})();
console.log(a, b);
}
expect_stdout: "2 2"
}

View File

@@ -102,12 +102,12 @@ dont_screw_try_catch: {
};
}
expect: {
bad = function(n){
return function(t){
bad = function(t){
return function(n){
try{
n()
} catch(n) {
t(n)
t()
} catch(t) {
n(t)
}
}
};
@@ -349,11 +349,11 @@ issue_2254_1: {
try {
console.log(f("PASS"));
} catch (e) {}
function f(e) {
function f(t) {
try {
throw "FAIL";
} catch (t) {
return e;
} catch (e) {
return t;
}
}
}
@@ -392,3 +392,149 @@ issue_2254_2: {
}
expect_stdout: "PASS"
}
issue_24_1: {
mangle = {
ie8: false,
}
input: {
(function(a) {
console.log(typeof function f(){} === typeof a ? "FAIL" : "PASS");
})();
}
expect: {
(function(o) {
console.log(typeof function o(){} === typeof o ? "FAIL" : "PASS");
})();
}
expect_stdout: "PASS"
}
issue_24_2: {
mangle = {
ie8: true,
}
input: {
(function(a) {
console.log(typeof function f(){} === typeof a ? "FAIL" : "PASS");
})();
}
expect: {
(function(n) {
console.log(typeof function o(){} === typeof n ? "FAIL" : "PASS");
})();
}
expect_stdout: "PASS"
}
issue_2976_1: {
mangle = {
ie8: false,
}
input: {
console.log(function f() {
var a;
return a === f ? "FAIL" : "PASS";
}());
}
expect: {
console.log(function n() {
var o;
return o === n ? "FAIL" : "PASS";
}());
}
expect_stdout: "PASS"
}
issue_2976_2: {
mangle = {
ie8: true,
}
input: {
console.log(function f() {
var a;
return a === f ? "FAIL" : "PASS";
}());
}
expect: {
console.log(function n() {
var o;
return o === n ? "FAIL" : "PASS";
}());
}
expect_stdout: "PASS"
}
issue_3035: {
mangle = {
ie8: false,
}
input: {
var c = "FAIL";
(function(a) {
try {
throw 1;
} catch (b) {
try {
throw 0;
} catch (a) {
b && (c = "PASS");
}
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function(o) {
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (o) {
t && (c = "PASS");
}
}
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3035_ie8: {
mangle = {
ie8: true,
}
input: {
var c = "FAIL";
(function(a) {
try {
throw 1;
} catch (b) {
try {
throw 0;
} catch (a) {
b && (c = "PASS");
}
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function(t) {
try {
throw 1;
} catch (o) {
try {
throw 0;
} catch (t) {
o && (c = "PASS");
}
}
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -243,7 +243,7 @@ issue_1089: {
expect: {
function x() {
var f = document.getElementById("fname");
if (f.files[0].size > 12345)
if (12345 < f.files[0].size)
return alert("alert"), f.focus(), !1;
}
}

View File

@@ -175,8 +175,8 @@ should_warn: {
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]",
"WARN: Condition always true [test/compress/issue-1261.js:141,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]",
"WARN: Condition always false [test/compress/issue-1261.js:144,8]",
]

View File

@@ -104,7 +104,7 @@ mangle_catch_toplevel: {
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);'
expect_stdout: "PASS"
}
@@ -148,7 +148,7 @@ mangle_catch_var_toplevel: {
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);'
expect_stdout: "PASS"
}
@@ -345,3 +345,95 @@ mangle_catch_redef_2_ie8_toplevel: {
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}
mangle_catch_redef_3: {
mangle = {
ie8: false,
toplevel: false,
}
input: {
var o = "PASS";
try {
throw 0;
} catch (o) {
(function() {
function f() {
o = "FAIL";
}
f(), f();
})();
}
console.log(o);
}
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_3_toplevel: {
mangle = {
ie8: false,
toplevel: true,
}
input: {
var o = "PASS";
try {
throw 0;
} catch (o) {
(function() {
function f() {
o = "FAIL";
}
f(), f();
})();
}
console.log(o);
}
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
expect_stdout: "PASS"
}
mangle_catch_redef_ie8_3: {
mangle = {
ie8: true,
toplevel: false,
}
input: {
var o = "PASS";
try {
throw 0;
} catch (o) {
(function() {
function f() {
o = "FAIL";
}
f(), f();
})();
}
console.log(o);
}
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_3_ie8_toplevel: {
mangle = {
ie8: true,
toplevel: true,
}
input: {
var o = "PASS";
try {
throw 0;
} catch (o) {
(function() {
function f() {
o = "FAIL";
}
f(), f();
})();
}
console.log(o);
}
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
expect_stdout: "PASS"
}

View File

@@ -15,7 +15,7 @@ function_iife_catch: {
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_stdout: "0 1"
}
@@ -36,7 +36,7 @@ function_iife_catch_ie8: {
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_stdout: "0 1"
}
@@ -61,7 +61,7 @@ function_catch_catch: {
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();"
expect_stdout: [
"3",
"undefined",

View File

@@ -0,0 +1,21 @@
inline_script_off: {
beautify = {
inline_script: false,
}
input: {
console.log("</sCrIpT>");
}
expect_exact: 'console.log("</sCrIpT>");'
expect_stdout: "</sCrIpT>"
}
inline_script_on: {
beautify = {
inline_script: true,
}
input: {
console.log("</sCrIpT>");
}
expect_exact: 'console.log("<\\/sCrIpT>");'
expect_stdout: "</sCrIpT>"
}

View File

@@ -294,10 +294,10 @@ issue_186_beautify_ie8: {
]
}
issue_186_bracketize: {
issue_186_braces: {
beautify = {
beautify: false,
bracketize: true,
braces: true,
ie8: false,
}
input: {
@@ -314,10 +314,10 @@ issue_186_bracketize: {
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_bracketize_ie8: {
issue_186_braces_ie8: {
beautify = {
beautify: false,
bracketize: true,
braces: true,
ie8: true,
}
input: {
@@ -334,10 +334,10 @@ issue_186_bracketize_ie8: {
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_beautify_bracketize: {
issue_186_beautify_braces: {
beautify = {
beautify: true,
bracketize: true,
braces: true,
ie8: false,
}
input: {
@@ -366,10 +366,10 @@ issue_186_beautify_bracketize: {
]
}
issue_186_beautify_bracketize_ie8: {
issue_186_beautify_braces_ie8: {
beautify = {
beautify: true,
bracketize: true,
braces: true,
ie8: true,
}
input: {

View File

@@ -67,7 +67,7 @@ negate_iife_3_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
true, console.log(true);
}
expect_stdout: true
}
@@ -110,7 +110,7 @@ negate_iife_3_off_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
true, console.log(true);
}
expect_stdout: true
}

View File

@@ -830,3 +830,334 @@ issue_2938_4: {
}
expect_stdout: "PASS"
}
collapse_vars_1_true: {
options = {
collapse_vars: true,
pure_getters: true,
unused: true,
}
input: {
function f(a, b) {
for (;;) {
var c = a.g();
var d = b.p;
if (c || d) break;
}
}
}
expect: {
function f(a, b) {
for (;;) {
if (a.g() || b.p) break;
}
}
}
}
collapse_vars_1_false: {
options = {
collapse_vars: true,
pure_getters: false,
unused: true,
}
input: {
function f(a, b) {
for (;;) {
var c = a.g();
var d = b.p;
if (c || d) break;
}
}
}
expect: {
function f(a, b) {
for (;;) {
var c = a.g();
var d = b.p;
if (c || d) break;
}
}
}
}
collapse_vars_1_strict: {
options = {
collapse_vars: true,
pure_getters: "strict",
unused: true,
}
input: {
function f(a, b) {
for (;;) {
var c = a.g();
var d = b.p;
if (c || d) break;
}
}
}
expect: {
function f(a, b) {
for (;;) {
var c = a.g();
var d = b.p;
if (c || d) break;
}
}
}
}
collapse_vars_2_true: {
options = {
collapse_vars: true,
pure_getters: true,
reduce_vars: true,
}
input: {
function f() {
function g() {}
g.a = function() {};
g.b = g.a;
return g;
}
}
expect: {
function f() {
function g() {}
g.b = g.a = function() {};
return g;
}
}
}
collapse_vars_2_false: {
options = {
collapse_vars: true,
pure_getters: false,
reduce_vars: true,
}
input: {
function f() {
function g() {}
g.a = function() {};
g.b = g.a;
return g;
}
}
expect: {
function f() {
function g() {}
g.a = function() {};
g.b = g.a;
return g;
}
}
}
collapse_vars_2_strict: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
function f() {
function g() {}
g.a = function() {};
g.b = g.a;
return g;
}
}
expect: {
function f() {
function g() {}
g.b = g.a = function() {};
return g;
}
}
}
collapse_rhs_true: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
console.log((42..length = "PASS", "PASS"));
console.log(("foo".length = "PASS", "PASS"));
console.log((false.length = "PASS", "PASS"));
console.log((function() {}.length = "PASS", "PASS"));
console.log(({
get length() {
return "FAIL";
}
}.length = "PASS", "PASS"));
}
expect: {
console.log(42..length = "PASS");
console.log("foo".length = "PASS");
console.log(false.length = "PASS");
console.log(function() {}.length = "PASS");
console.log({
get length() {
return "FAIL";
}
}.length = "PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
]
}
collapse_rhs_false: {
options = {
collapse_vars: true,
pure_getters: false,
}
input: {
console.log((42..length = "PASS", "PASS"));
console.log(("foo".length = "PASS", "PASS"));
console.log((false.length = "PASS", "PASS"));
console.log((function() {}.length = "PASS", "PASS"));
console.log(({
get length() {
return "FAIL";
}
}.length = "PASS", "PASS"));
}
expect: {
console.log(42..length = "PASS");
console.log("foo".length = "PASS");
console.log(false.length = "PASS");
console.log(function() {}.length = "PASS");
console.log({
get length() {
return "FAIL";
}
}.length = "PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
]
}
collapse_rhs_strict: {
options = {
collapse_vars: true,
pure_getters: "strict",
}
input: {
console.log((42..length = "PASS", "PASS"));
console.log(("foo".length = "PASS", "PASS"));
console.log((false.length = "PASS", "PASS"));
console.log((function() {}.length = "PASS", "PASS"));
console.log(({
get length() {
return "FAIL";
}
}.length = "PASS", "PASS"));
}
expect: {
console.log(42..length = "PASS");
console.log("foo".length = "PASS");
console.log(false.length = "PASS");
console.log(function() {}.length = "PASS");
console.log({
get length() {
return "FAIL";
}
}.length = "PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
]
}
collapse_rhs_setter: {
options = {
collapse_vars: true,
pure_getters: "strict",
}
input: {
try {
console.log(({
set length(v) {
throw "PASS";
}
}.length = "FAIL", "FAIL"));
} catch (e) {
console.log(e);
}
}
expect: {
try {
console.log({
set length(v) {
throw "PASS";
}
}.length = "FAIL");
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
}
collapse_rhs_call: {
options = {
collapse_vars: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {};
function f() {
console.log("PASS");
}
o.f = f;
f();
}
expect: {
({}.f = function() {
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
collapse_rhs_lhs: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
a.b = b, b += 2;
console.log(a.b, b);
}
f({}, 1);
}
expect: {
function f(a, b) {
a.b = b, b += 2;
console.log(a.b, b);
}
f({}, 1);
}
expect_stdout: "1 3"
}

View File

@@ -55,7 +55,7 @@ reduce_vars: {
console.log(a - 5);
eval("console.log(a);");
})(eval);
"yes";
true, "yes";
console.log(A + 1);
}
expect_stdout: true
@@ -147,7 +147,7 @@ modified: {
}
function f4() {
var b = 2, c = 3;
b = c;
1, b = c;
console.log(1 + b);
console.log(b + c);
console.log(1 + c);
@@ -715,10 +715,12 @@ passes: {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f() {
(function() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
@@ -729,17 +731,22 @@ passes: {
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
})();
}
expect: {
function f() {
3;
console.log(4);
console.log(6);
console.log(4);
(function() {
console.log(4),
console.log(6),
console.log(4),
console.log(7);
}
})();
}
expect_stdout: [
"4",
"6",
"4",
"7",
]
}
iife: {
@@ -5545,3 +5552,33 @@ issue_2919: {
}
expect_stdout: "function"
}
issue_2992: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = "PASS";
(function f(b) {
switch (0) {
case 0:
case b = 1:
b && (c = "FAIL");
}
})();
console.log(c);
}
expect: {
var c = "PASS";
(function f(b) {
switch (0) {
case 0:
case b = 1:
b && (c = "FAIL");
}
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -109,7 +109,7 @@ mangle_catch_toplevel: {
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);'
expect_stdout: "PASS"
}
@@ -155,7 +155,7 @@ mangle_catch_var_toplevel: {
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);'
expect_stdout: "PASS"
}
@@ -451,7 +451,7 @@ function_iife_catch: {
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_stdout: "0 1"
}
@@ -473,7 +473,7 @@ function_iife_catch_ie8: {
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_stdout: "0 1"
}
@@ -499,7 +499,7 @@ function_catch_catch: {
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();"
expect_stdout: [
"3",
"undefined",

View File

@@ -668,8 +668,7 @@ side_effects_cascade_2: {
}
expect: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
!(b = a) + (b += a) || (b += a),
b = a;
}
}

View File

@@ -59,7 +59,7 @@ if_else_empty: {
if ({} ? a : b); else {}
}
expect: {
!{} ? b : a;
({}), a;
}
}
@@ -104,6 +104,7 @@ if_return: {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
}
input: {
function f(w, x, y, z) {
@@ -124,7 +125,7 @@ if_return: {
if (w) {
if (y) return;
} else if (z) return;
return x == y || (x && w(), y && z(), !0);
return x == y || (x && w(), y && z()), !0;
}
}
}

View File

@@ -164,13 +164,13 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
it("Should work with `--beautify braces`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b braces';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js"));
assert.strictEqual(stdout, read("test/input/issue-1482/braces.js"));
done();
});
});

View File

@@ -139,6 +139,26 @@ describe("Comment", function() {
assert.strictEqual(result.code, code);
});
it("Should retain comments within braces", function() {
var code = [
"{/* foo */}",
"a({/* foo */});",
"while (a) {/* foo */}",
"switch (a) {/* foo */}",
"if (a) {/* foo */} else {/* bar */}",
].join("\n\n");
var result = uglify.minify(code, {
compress: false,
mangle: false,
output: {
beautify: true,
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, code);
});
it("Should correctly preserve new lines around comments", function() {
var tests = [
[

View File

@@ -46,10 +46,10 @@ describe("minify", function() {
assert.strictEqual(compressed, [
"function n(n){return 3*n}",
"function r(n){return n/2}",
"var o=console.log.bind(console);",
'function c(n){o("Foo:",2*n)}',
"var c=console.log.bind(console);",
'function o(o){c("Foo:",2*o)}',
"var a=n(3),b=r(12);",
'o("qux",a,b),c(11);',
'c("qux",a,b),o(11);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
@@ -79,10 +79,10 @@ describe("minify", function() {
assert.strictEqual(compressed, [
"function n(n){return 3*n}",
"function r(n){return n/2}",
"var o=console.log.bind(console);",
'function c(n){o("Foo:",2*n)}',
"var c=console.log.bind(console);",
'function o(o){c("Foo:",2*o)}',
"var a=n(3),b=r(12);",
'o("qux",a,b),c(11);',
'c("qux",a,b),o(11);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
@@ -186,99 +186,6 @@ describe("minify", function() {
});
});
describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), {
sourceMap: {
content: read("./test/input/issue-1236/simple.js.map"),
filename: "simple.min.js",
includeSources: true
}
});
var map = JSON.parse(result.map);
assert.equal(map.file, 'simple.min.js');
assert.equal(map.sourcesContent.length, 1);
assert.equal(map.sourcesContent[0],
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
});
it("Should process inline source map", function() {
var code = Uglify.minify(read("./test/input/issue-520/input.js"), {
compress: { toplevel: true },
sourceMap: {
content: "inline",
url: "inline"
}
}).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
});
it("Should warn for missing inline source map", function() {
var warn_function = Uglify.AST_Node.warn_function;
var warnings = [];
Uglify.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), {
mangle: false,
sourceMap: {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
Uglify.AST_Node.warn_function = warn_function;
}
});
it("Should fail with multiple input and inline source map", function() {
var result = Uglify.minify([
read("./test/input/issue-520/input.js"),
read("./test/input/issue-520/output.js")
], {
sourceMap: {
content: "inline",
url: "inline"
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
});
});
describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', {
sourceMap: {
url: "inline"
}
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
});
describe("#__PURE__", function() {
it("should drop #__PURE__ hint after use", function() {
var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', {
@@ -312,6 +219,15 @@ describe("minify", function() {
assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12);
});
it("should reject duplicated label name", function() {
var result = Uglify.minify("L:{L:{}}");
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Label L defined twice");
assert.strictEqual(err.filename, "0");
assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 4);
});
});
describe("global_defs", function() {

View File

@@ -17,7 +17,7 @@ describe("test/benchmark.js", function() {
this.timeout(10 * 60 * 1000);
[
"-b",
"-b bracketize",
"-b braces",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",

143
test/mocha/sourcemaps.js Normal file
View File

@@ -0,0 +1,143 @@
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var Uglify = require("../../");
function read(path) {
return readFileSync(path, "utf8");
}
function source_map(code) {
return JSON.parse(Uglify.minify(code, {
compress: false,
mangle: false,
sourceMap: true,
}).map);
}
describe("sourcemaps", function() {
it("Should give correct version", function() {
var map = source_map("var x = 1 + 1;");
assert.strictEqual(map.version, 3);
assert.deepEqual(map.names, [ "x" ]);
});
it("Should give correct names", function() {
var map = source_map([
"({",
" get enabled() {",
" return 3;",
" },",
" set enabled(x) {",
" ;",
" }",
"});",
].join("\n"));
assert.deepEqual(map.names, [ "enabled", "x" ]);
});
it("Should mark array/object literals", function() {
var result = Uglify.minify([
"var obj = {};",
"obj.wat([]);",
].join("\n"), {
sourceMap: true,
toplevel: true,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "({}).wat([]);");
assert.strictEqual(result.map, '{"version":3,"sources":["0"],"names":["wat"],"mappings":"CAAU,IACNA,IAAI"}');
});
describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), {
sourceMap: {
content: read("./test/input/issue-1236/simple.js.map"),
filename: "simple.min.js",
includeSources: true
}
});
var map = JSON.parse(result.map);
assert.equal(map.file, 'simple.min.js');
assert.equal(map.sourcesContent.length, 1);
assert.equal(map.sourcesContent[0],
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
});
it("Should process inline source map", function() {
var code = Uglify.minify(read("./test/input/issue-520/input.js"), {
compress: { toplevel: true },
sourceMap: {
content: "inline",
url: "inline"
}
}).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
});
it("Should warn for missing inline source map", function() {
var warn_function = Uglify.AST_Node.warn_function;
var warnings = [];
Uglify.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), {
mangle: false,
sourceMap: {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
Uglify.AST_Node.warn_function = warn_function;
}
});
it("Should fail with multiple input and inline source map", function() {
var result = Uglify.minify([
read("./test/input/issue-520/input.js"),
read("./test/input/issue-520/output.js")
], {
sourceMap: {
content: "inline",
url: "inline"
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
});
});
describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', {
sourceMap: {
url: "inline"
}
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
});
});

View File

@@ -23,6 +23,15 @@ describe("spidermonkey export/import sanity test", function() {
});
});
it("should not add unnecessary escape slashes to regexps", function() {
var input = "/[\\\\/]/;";
var ast = uglify.parse(input).to_mozilla_ast();
assert.equal(
uglify.AST_Node.from_mozilla_ast(ast).print_to_string(),
input
);
});
it("Should judge between directives and strings correctly on import", function() {
var tests = [
{

View File

@@ -11,7 +11,7 @@ function try_beautify(code) {
mangle: false,
output: {
beautify: true,
bracketize: true
braces: true
}
});
if (beautified.error) {

View File

@@ -22,9 +22,6 @@ if (failures) {
var mocha_tests = require("./mocha.js");
mocha_tests();
var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests();
/* -----[ utils ]----- */
function tmpl() {
@@ -49,16 +46,9 @@ function log_test(name) {
}
function find_test_files(dir) {
var files = fs.readdirSync(dir).filter(function(name){
return fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
});
if (process.argv.length > 2) {
var x = process.argv.slice(2);
files = files.filter(function(f){
return x.indexOf(f) >= 0;
});
}
return files;
}
function test_directory(dir) {
@@ -135,7 +125,6 @@ function run_compress_tests() {
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle) {
U.base54.reset();
output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle);
if (test.mangle.properties) {
@@ -183,7 +172,7 @@ function run_compress_tests() {
}
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code);
var stdout = sandbox.run_code(input_code, true);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
}
@@ -197,7 +186,7 @@ function run_compress_tests() {
});
return false;
}
stdout = sandbox.run_code(output);
stdout = sandbox.run_code(output, true);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
@@ -344,7 +333,6 @@ function parse_test(file) {
}
function make_code(ast, options) {
options.inline_script = true;
var stream = U.OutputStream(options);
ast.print(stream);
return stream.get();
@@ -379,7 +367,7 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
});
return false;
} else {
var stdout = sandbox.run_code(result.code);
var stdout = sandbox.run_code(result.code, true);
if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
stdout = expect_stdout;
}

View File

@@ -1,6 +1,27 @@
var semver = require("semver");
var vm = require("vm");
function createContext() {
var context = Object.create(null);
Object.defineProperty(context, "console", {
value: function() {
var con = Object.create(null);
Object.defineProperty(con, "log", {
value: 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);
}));
}
});
return con;
}()
});
return vm.createContext(context);
}
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
@@ -9,7 +30,8 @@ function safe_log(arg, level) {
if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString();
if (level--) for (var key in arg) {
if (!Object.getOwnPropertyDescriptor(arg, key).get) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) {
arg[key] = safe_log(arg[key], level);
}
}
@@ -21,6 +43,7 @@ function strip_func_ids(text) {
return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>");
}
var context;
var FUNC_TOSTRING = [
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
@@ -43,35 +66,30 @@ var FUNC_TOSTRING = [
" };",
"}();",
]).join("\n");
exports.run_code = function(code) {
exports.run_code = function(code, reuse) {
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
vm.runInNewContext([
if (!reuse || !context) context = createContext();
vm.runInContext([
FUNC_TOSTRING,
"!function() {",
code,
"}();",
].join("\n"), {
console: {
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);
}));
}
}
}, { timeout: 5000 });
].join("\n"), context, { timeout: 5000 });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
if (!reuse || /prototype/.test(code)) {
context = null;
} else for (var key in context) {
delete context[key];
}
}
};
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {

View File

@@ -1,40 +0,0 @@
var UglifyJS = require("..");
var ok = require("assert");
module.exports = function () {
console.log("--- Sourcemaps tests");
var basic = source_map([
'var x = 1 + 1;'
].join('\n'));
ok.equal(basic.version, 3);
ok.deepEqual(basic.names, ['x']);
var issue836 = source_map([
"({",
" get enabled() {",
" return 3;",
" },",
" set enabled(x) {",
" ;",
" }",
"});",
].join("\n"));
ok.deepEqual(issue836.names, ['enabled', 'x']);
}
function source_map(js) {
return JSON.parse(UglifyJS.minify(js, {
compress: false,
mangle: false,
sourceMap: true
}).map);
}
// Run standalone
if (module.parent === null) {
module.exports();
}

View File

@@ -975,7 +975,7 @@ function try_beautify(code, result, printfn) {
mangle: false,
output: {
beautify: true,
bracketize: true,
braces: true,
},
});
if (beautified.error) {

View File

@@ -4,7 +4,7 @@
"mangle": false,
"output": {
"beautify": true,
"bracketize": true
"braces": true
},
"rename": true
},