Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d17c5b0fa | ||
|
|
5b20bad4b3 | ||
|
|
765a06340f | ||
|
|
5045e140b1 | ||
|
|
10648c9af6 | ||
|
|
87e67ec299 | ||
|
|
61a0dad9fe | ||
|
|
3e2c51a4da | ||
|
|
0e29ad5eb9 | ||
|
|
0f2687ecfc | ||
|
|
1c0defdc03 | ||
|
|
dcbf2236c7 | ||
|
|
24bb288832 | ||
|
|
6ad8e1081f | ||
|
|
815eff1f7c | ||
|
|
1e9b576ee9 | ||
|
|
3797458365 | ||
|
|
1858c2018c | ||
|
|
ec7f071272 | ||
|
|
f1eb03f2c0 | ||
|
|
0f4cfa877a | ||
|
|
1d5c2becbd | ||
|
|
22a09ea7c5 | ||
|
|
bad664c632 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -14,6 +14,10 @@ jobs:
|
|||||||
TYPE: ${{ matrix.script }}
|
TYPE: ${{ matrix.script }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: tmp
|
||||||
|
key: tmp ${{ matrix.script }}
|
||||||
- shell: bash
|
- shell: bash
|
||||||
run: |
|
run: |
|
||||||
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
|
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
|
||||||
|
|||||||
42
README.md
42
README.md
@@ -478,42 +478,42 @@ if (result.error) throw result.error;
|
|||||||
|
|
||||||
## Minify options
|
## Minify options
|
||||||
|
|
||||||
- `warnings` (default `false`) — pass `true` to return compressor warnings
|
|
||||||
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
|
|
||||||
|
|
||||||
- `parse` (default `{}`) — pass an object if you wish to specify some
|
|
||||||
additional [parse options](#parse-options).
|
|
||||||
|
|
||||||
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
||||||
Pass an object to specify custom [compress options](#compress-options).
|
Pass an object to specify custom [compress options](#compress-options).
|
||||||
|
|
||||||
|
- `ie8` (default `false`) -- set to `true` to support IE8.
|
||||||
|
|
||||||
|
- `keep_fnames` (default: `false`) -- pass `true` to prevent discarding or mangling
|
||||||
|
of function names. Useful for code relying on `Function.prototype.name`.
|
||||||
|
|
||||||
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
|
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
|
||||||
an object to specify [mangle options](#mangle-options) (see below).
|
an object to specify [mangle options](#mangle-options) (see below).
|
||||||
|
|
||||||
- `mangle.properties` (default `false`) — a subcategory of the mangle option.
|
- `mangle.properties` (default `false`) — a subcategory of the mangle option.
|
||||||
Pass an object to specify custom [mangle property options](#mangle-properties-options).
|
Pass an object to specify custom [mangle property options](#mangle-properties-options).
|
||||||
|
|
||||||
- `output` (default `null`) — pass an object if you wish to specify
|
- `nameCache` (default `null`) -- pass an empty object `{}` or a previously
|
||||||
additional [output options](#output-options). The defaults are optimized
|
|
||||||
for best compression.
|
|
||||||
|
|
||||||
- `sourceMap` (default `false`) - pass an object if you wish to specify
|
|
||||||
[source map options](#source-map-options).
|
|
||||||
|
|
||||||
- `toplevel` (default `false`) - set to `true` if you wish to enable top level
|
|
||||||
variable and function name mangling and to drop unused variables and functions.
|
|
||||||
|
|
||||||
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
|
|
||||||
used `nameCache` object if you wish to cache mangled variable and
|
used `nameCache` object if you wish to cache mangled variable and
|
||||||
property names across multiple invocations of `minify()`. Note: this is
|
property names across multiple invocations of `minify()`. Note: this is
|
||||||
a read/write property. `minify()` will read the name cache state of this
|
a read/write property. `minify()` will read the name cache state of this
|
||||||
object and update it during minification so that it may be
|
object and update it during minification so that it may be
|
||||||
reused or externally persisted by the user.
|
reused or externally persisted by the user.
|
||||||
|
|
||||||
- `ie8` (default `false`) - set to `true` to support IE8.
|
- `output` (default `null`) — pass an object if you wish to specify
|
||||||
|
additional [output options](#output-options). The defaults are optimized
|
||||||
|
for best compression.
|
||||||
|
|
||||||
- `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling
|
- `parse` (default `{}`) — pass an object if you wish to specify some
|
||||||
of function names. Useful for code relying on `Function.prototype.name`.
|
additional [parse options](#parse-options).
|
||||||
|
|
||||||
|
- `sourceMap` (default `false`) -- pass an object if you wish to specify
|
||||||
|
[source map options](#source-map-options).
|
||||||
|
|
||||||
|
- `toplevel` (default `false`) -- set to `true` if you wish to enable top level
|
||||||
|
variable and function name mangling and to drop unused variables and functions.
|
||||||
|
|
||||||
|
- `warnings` (default `false`) — pass `true` to return compressor warnings
|
||||||
|
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
|
||||||
|
|
||||||
## Minify options structure
|
## Minify options structure
|
||||||
|
|
||||||
@@ -682,6 +682,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
|||||||
where the return value is discarded, to avoid the parens that the
|
where the return value is discarded, to avoid the parens that the
|
||||||
code generator would insert.
|
code generator would insert.
|
||||||
|
|
||||||
|
- `objects` (default: `true`) -- compact duplicate keys in object literals.
|
||||||
|
|
||||||
- `passes` (default: `1`) -- The maximum number of times to run compress.
|
- `passes` (default: `1`) -- The maximum number of times to run compress.
|
||||||
In some cases more than one pass leads to further compressed code. Keep in
|
In some cases more than one pass leads to further compressed code. Keep in
|
||||||
mind more passes will take more time.
|
mind more passes will take more time.
|
||||||
|
|||||||
353
lib/compress.js
353
lib/compress.js
@@ -74,6 +74,7 @@ function Compressor(options, false_by_default) {
|
|||||||
keep_infinity : false,
|
keep_infinity : false,
|
||||||
loops : !false_by_default,
|
loops : !false_by_default,
|
||||||
negate_iife : !false_by_default,
|
negate_iife : !false_by_default,
|
||||||
|
objects : !false_by_default,
|
||||||
passes : 1,
|
passes : 1,
|
||||||
properties : !false_by_default,
|
properties : !false_by_default,
|
||||||
pure_getters : !false_by_default && "strict",
|
pure_getters : !false_by_default && "strict",
|
||||||
@@ -1041,7 +1042,8 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError");
|
var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError");
|
||||||
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
|
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
|
||||||
return !this.definition().undeclared
|
return this.defined
|
||||||
|
|| !this.definition().undeclared
|
||||||
|| compressor.option("unsafe") && global_names[this.name];
|
|| compressor.option("unsafe") && global_names[this.name];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1233,7 +1235,7 @@ merge(Compressor.prototype, {
|
|||||||
var lvalues = get_lvalues(candidate);
|
var lvalues = get_lvalues(candidate);
|
||||||
var lhs_local = is_lhs_local(lhs);
|
var lhs_local = is_lhs_local(lhs);
|
||||||
if (!side_effects) side_effects = value_has_side_effects(candidate);
|
if (!side_effects) side_effects = value_has_side_effects(candidate);
|
||||||
var replace_all = replace_all_symbols();
|
var replace_all = replace_all_symbols(candidate);
|
||||||
var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
|
var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
|
||||||
return node.has_side_effects(compressor);
|
return node.has_side_effects(compressor);
|
||||||
} : side_effects_external : return_false;
|
} : side_effects_external : return_false;
|
||||||
@@ -1281,6 +1283,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
branch.expression = branch.expression.transform(scanner);
|
branch.expression = branch.expression.transform(scanner);
|
||||||
if (!replace_all) break;
|
if (!replace_all) break;
|
||||||
|
scan_rhs = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abort = true;
|
abort = true;
|
||||||
@@ -1310,6 +1313,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function in_conditional(node, parent) {
|
function in_conditional(node, parent) {
|
||||||
if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
|
if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
|
||||||
|
if (parent instanceof AST_Case) return parent.expression !== node;
|
||||||
if (parent instanceof AST_Conditional) return parent.condition !== node;
|
if (parent instanceof AST_Conditional) return parent.condition !== node;
|
||||||
return parent instanceof AST_If && parent.condition !== node;
|
return parent instanceof AST_If && parent.condition !== node;
|
||||||
}
|
}
|
||||||
@@ -1475,19 +1479,43 @@ merge(Compressor.prototype, {
|
|||||||
if (parent instanceof AST_Call) return node;
|
if (parent instanceof AST_Call) return node;
|
||||||
if (parent instanceof AST_Case) return node;
|
if (parent instanceof AST_Case) return node;
|
||||||
if (parent instanceof AST_Conditional) return node;
|
if (parent instanceof AST_Conditional) return node;
|
||||||
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
|
if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
|
||||||
if (parent instanceof AST_Exit) return node;
|
if (parent instanceof AST_Exit) return node;
|
||||||
if (parent instanceof AST_If) return node;
|
if (parent instanceof AST_If) return node;
|
||||||
if (parent instanceof AST_IterationStatement) return node;
|
if (parent instanceof AST_IterationStatement) return node;
|
||||||
if (parent instanceof AST_PropAccess) return node;
|
if (parent instanceof AST_PropAccess) return node;
|
||||||
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
|
if (parent instanceof AST_Sequence) {
|
||||||
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
|
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
|
||||||
|
}
|
||||||
|
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
|
||||||
if (parent instanceof AST_Switch) return node;
|
if (parent instanceof AST_Switch) return node;
|
||||||
if (parent instanceof AST_Unary) return node;
|
if (parent instanceof AST_Unary) return node;
|
||||||
if (parent instanceof AST_VarDef) return node;
|
if (parent instanceof AST_VarDef) return node;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function find_stop_unused(node, level) {
|
||||||
|
var parent = scanner.parent(level);
|
||||||
|
if (is_last_node(node, parent)) return node;
|
||||||
|
if (in_conditional(node, parent)) return node;
|
||||||
|
if (parent instanceof AST_Assign) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_IterationStatement) return node;
|
||||||
|
if (parent instanceof AST_PropAccess) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
|
||||||
|
if (parent instanceof AST_VarDef) return find_stop_unused(parent, level + 1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function mangleable_var(var_def) {
|
function mangleable_var(var_def) {
|
||||||
var value = var_def.value;
|
var value = var_def.value;
|
||||||
if (!(value instanceof AST_SymbolRef)) return;
|
if (!(value instanceof AST_SymbolRef)) return;
|
||||||
@@ -1654,7 +1682,8 @@ merge(Compressor.prototype, {
|
|||||||
return get_rvalue(expr).has_side_effects(compressor);
|
return get_rvalue(expr).has_side_effects(compressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replace_all_symbols() {
|
function replace_all_symbols(expr) {
|
||||||
|
if (expr instanceof AST_Unary) return false;
|
||||||
if (side_effects) return false;
|
if (side_effects) return false;
|
||||||
if (value_def) return true;
|
if (value_def) return true;
|
||||||
if (lhs instanceof AST_SymbolRef) {
|
if (lhs instanceof AST_SymbolRef) {
|
||||||
@@ -1669,7 +1698,7 @@ merge(Compressor.prototype, {
|
|||||||
function symbol_in_lvalues(sym, parent) {
|
function symbol_in_lvalues(sym, parent) {
|
||||||
var lvalue = lvalues[sym.name];
|
var lvalue = lvalues[sym.name];
|
||||||
if (!lvalue) return;
|
if (!lvalue) return;
|
||||||
if (lvalue !== lhs) return !(parent instanceof AST_Call && parent.expression === sym);
|
if (lvalue !== lhs) return true;
|
||||||
scan_rhs = false;
|
scan_rhs = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1747,9 +1776,7 @@ merge(Compressor.prototype, {
|
|||||||
if (stat instanceof AST_If) {
|
if (stat instanceof AST_If) {
|
||||||
var ab = aborts(stat.body);
|
var ab = aborts(stat.body);
|
||||||
if (can_merge_flow(ab)) {
|
if (can_merge_flow(ab)) {
|
||||||
if (ab.label) {
|
if (ab.label) remove(ab.label.thedef.references, ab);
|
||||||
remove(ab.label.thedef.references, ab);
|
|
||||||
}
|
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
stat.condition = stat.condition.negate(compressor);
|
stat.condition = stat.condition.negate(compressor);
|
||||||
@@ -1777,23 +1804,34 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ab = aborts(stat.alternative);
|
var alt = aborts(stat.alternative);
|
||||||
if (can_merge_flow(ab)) {
|
if (can_merge_flow(alt)) {
|
||||||
if (ab.label) {
|
if (alt.label) remove(alt.label.thedef.references, alt);
|
||||||
remove(ab.label.thedef.references, ab);
|
|
||||||
}
|
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
stat.body = make_node(AST_BlockStatement, stat.body, {
|
stat.body = make_node(AST_BlockStatement, stat.body, {
|
||||||
body: as_statement_array(stat.body).concat(extract_functions())
|
body: as_statement_array(stat.body).concat(extract_functions())
|
||||||
});
|
});
|
||||||
var body = as_statement_array_with_return(stat.alternative, ab);
|
var body = as_statement_array_with_return(stat.alternative, alt);
|
||||||
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
|
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
statements[i] = stat.transform(compressor);
|
statements[i] = stat.transform(compressor);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compressor.option("typeofs")) {
|
||||||
|
if (ab && !alt) {
|
||||||
|
mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
|
||||||
|
body: statements.slice(i + 1)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (!ab && alt) {
|
||||||
|
mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
|
||||||
|
body: statements.slice(i + 1)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
|
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
|
||||||
@@ -1938,9 +1976,7 @@ merge(Compressor.prototype, {
|
|||||||
&& loop_body(lct) === self
|
&& loop_body(lct) === self
|
||||||
|| stat instanceof AST_Continue
|
|| stat instanceof AST_Continue
|
||||||
&& loop_body(lct) === self) {
|
&& loop_body(lct) === self) {
|
||||||
if (stat.label) {
|
if (stat.label) remove(stat.label.thedef.references, stat);
|
||||||
remove(stat.label.thedef.references, stat);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
statements[n++] = stat;
|
statements[n++] = stat;
|
||||||
}
|
}
|
||||||
@@ -2860,10 +2896,10 @@ merge(Compressor.prototype, {
|
|||||||
case "+": return +v;
|
case "+": return +v;
|
||||||
case "++":
|
case "++":
|
||||||
case "--":
|
case "--":
|
||||||
if (e instanceof AST_SymbolRef) {
|
if (!(e instanceof AST_SymbolRef)) return this;
|
||||||
var refs = e.definition().references;
|
var refs = e.definition().references;
|
||||||
if (refs[refs.length - 1] === e) return v;
|
if (refs[refs.length - 1] !== e) return this;
|
||||||
}
|
return HOP(e, "_eval") ? +(this.operator[0] + 1) + +v : v;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
@@ -2872,6 +2908,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!non_converting_binary[this.operator]) depth++;
|
if (!non_converting_binary[this.operator]) depth++;
|
||||||
var left = this.left._eval(compressor, cached, depth);
|
var left = this.left._eval(compressor, cached, depth);
|
||||||
if (left === this.left) return this;
|
if (left === this.left) return this;
|
||||||
|
if (this.operator == (left ? "||" : "&&")) return left;
|
||||||
var right = this.right._eval(compressor, cached, depth);
|
var right = this.right._eval(compressor, cached, depth);
|
||||||
if (right === this.right) return this;
|
if (right === this.right) return this;
|
||||||
var result;
|
var result;
|
||||||
@@ -2986,6 +3023,7 @@ merge(Compressor.prototype, {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
convert_to_predicate(static_values);
|
convert_to_predicate(static_values);
|
||||||
|
var regexp_props = makePredicate("global ignoreCase multiline source");
|
||||||
def(AST_PropAccess, function(compressor, cached, depth) {
|
def(AST_PropAccess, function(compressor, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var key = this.property;
|
var key = this.property;
|
||||||
@@ -3001,9 +3039,12 @@ merge(Compressor.prototype, {
|
|||||||
val = global_objs[exp.name];
|
val = global_objs[exp.name];
|
||||||
} else {
|
} else {
|
||||||
val = exp._eval(compressor, cached, depth + 1);
|
val = exp._eval(compressor, cached, depth + 1);
|
||||||
if (!val || val === exp) return this;
|
if (val == null || val === exp) return this;
|
||||||
if (typeof val == "object" && !HOP(val, key)) return this;
|
if (val instanceof RegExp) {
|
||||||
if (typeof val == "function") switch (key) {
|
if (!regexp_props[key]) return this;
|
||||||
|
} else if (typeof val == "object") {
|
||||||
|
if (!HOP(val, key)) return this;
|
||||||
|
} else if (typeof val == "function") switch (key) {
|
||||||
case "name":
|
case "name":
|
||||||
return val.node.name ? val.node.name.name : "";
|
return val.node.name ? val.node.name.name : "";
|
||||||
case "length":
|
case "length":
|
||||||
@@ -3018,7 +3059,29 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
def(AST_Call, function(compressor, cached, depth) {
|
def(AST_Call, function(compressor, cached, depth) {
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
if (exp instanceof AST_SymbolRef) {
|
||||||
|
var fn = exp.fixed_value();
|
||||||
|
if (!(fn instanceof AST_Lambda)) return this;
|
||||||
|
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
|
||||||
|
if (fn.body.length != 1 || !fn.is_constant_expression()) return this;
|
||||||
|
var stat = fn.body[0];
|
||||||
|
if (!(stat instanceof AST_Return)) return this;
|
||||||
|
var args = eval_args(this.args);
|
||||||
|
if (!args) return this;
|
||||||
|
fn.argnames.forEach(function(sym, i) {
|
||||||
|
var value = args[i];
|
||||||
|
sym.definition().references.forEach(function(node) {
|
||||||
|
node._eval = function() {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
cached.push(node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (!stat.value) return undefined;
|
||||||
|
var val = stat.value._eval(compressor, cached, depth);
|
||||||
|
if (val === stat.value) return this;
|
||||||
|
return val;
|
||||||
|
} else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||||
var key = exp.property;
|
var key = exp.property;
|
||||||
if (key instanceof AST_Node) {
|
if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, cached, depth);
|
key = key._eval(compressor, cached, depth);
|
||||||
@@ -3032,17 +3095,12 @@ merge(Compressor.prototype, {
|
|||||||
val = global_objs[e.name];
|
val = global_objs[e.name];
|
||||||
} else {
|
} else {
|
||||||
val = e._eval(compressor, cached, depth + 1);
|
val = e._eval(compressor, cached, depth + 1);
|
||||||
if (val === e || !val) return this;
|
if (val == null || val === e) return this;
|
||||||
var native_fn = native_fns[val.constructor.name];
|
var native_fn = native_fns[val.constructor.name];
|
||||||
if (!native_fn || !native_fn[key]) return this;
|
if (!native_fn || !native_fn[key]) return this;
|
||||||
}
|
}
|
||||||
var args = [];
|
var args = eval_args(this.args);
|
||||||
for (var i = 0; i < this.args.length; i++) {
|
if (!args) return this;
|
||||||
var arg = this.args[i];
|
|
||||||
var value = arg._eval(compressor, cached, depth);
|
|
||||||
if (arg === value) return this;
|
|
||||||
args.push(value);
|
|
||||||
}
|
|
||||||
if (key == "replace" && typeof args[1] == "function") return this;
|
if (key == "replace" && typeof args[1] == "function") return this;
|
||||||
try {
|
try {
|
||||||
return val[key].apply(val, args);
|
return val[key].apply(val, args);
|
||||||
@@ -3056,6 +3114,17 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
function eval_args(args) {
|
||||||
|
var values = [];
|
||||||
|
for (var i = 0; i < args.length; i++) {
|
||||||
|
var arg = args[i];
|
||||||
|
var value = arg._eval(compressor, cached, depth);
|
||||||
|
if (arg === value) return;
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
def(AST_New, return_this);
|
def(AST_New, return_this);
|
||||||
})(function(node, func) {
|
})(function(node, func) {
|
||||||
@@ -3405,19 +3474,20 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Lambda, function(scope) {
|
def(AST_Lambda, function(scope) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var result = true;
|
var result = true;
|
||||||
var inner_scopes = [];
|
var scopes = [];
|
||||||
self.walk(new TreeWalker(function(node, descend) {
|
self.walk(new TreeWalker(function(node, descend) {
|
||||||
if (!result) return true;
|
if (!result) return true;
|
||||||
if (node instanceof AST_Catch) {
|
if (node instanceof AST_Catch) {
|
||||||
inner_scopes.push(node.argname.scope);
|
scopes.push(node.argname.scope);
|
||||||
descend();
|
descend();
|
||||||
inner_scopes.pop();
|
scopes.pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== self) {
|
if (node instanceof AST_Scope) {
|
||||||
inner_scopes.push(node);
|
if (node === self) return;
|
||||||
|
scopes.push(node);
|
||||||
descend();
|
descend();
|
||||||
inner_scopes.pop();
|
scopes.pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
@@ -3425,8 +3495,9 @@ merge(Compressor.prototype, {
|
|||||||
result = false;
|
result = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (self.variables.has(node.name)) return true;
|
||||||
var def = node.definition();
|
var def = node.definition();
|
||||||
if (!self.variables.has(def.name) && !member(def.scope, inner_scopes)) {
|
if (member(def.scope, scopes)) return true;
|
||||||
if (scope) {
|
if (scope) {
|
||||||
var scope_def = scope.find_variable(node);
|
var scope_def = scope.find_variable(node);
|
||||||
if (def.undeclared ? !scope_def : scope_def === def) {
|
if (def.undeclared ? !scope_def : scope_def === def) {
|
||||||
@@ -3435,7 +3506,6 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = false;
|
result = false;
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -4543,6 +4613,49 @@ merge(Compressor.prototype, {
|
|||||||
return if_break_in_loop(self, compressor);
|
return if_break_in_loop(self, compressor);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function mark_locally_defined(condition, consequent, alternative, operator) {
|
||||||
|
if (!(condition instanceof AST_Binary)) return;
|
||||||
|
if (!(condition.left instanceof AST_String)) {
|
||||||
|
if (!operator) operator = condition.operator;
|
||||||
|
if (condition.operator != operator) return;
|
||||||
|
switch (operator) {
|
||||||
|
case "&&":
|
||||||
|
case "||":
|
||||||
|
mark_locally_defined(condition.left, consequent, alternative, operator);
|
||||||
|
mark_locally_defined(condition.right, consequent, alternative, operator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(condition.right instanceof AST_UnaryPrefix)) return;
|
||||||
|
if (condition.right.operator != "typeof") return;
|
||||||
|
var sym = condition.right.expression;
|
||||||
|
if (!is_undeclared_ref(sym)) return;
|
||||||
|
var body;
|
||||||
|
var undef = condition.left.getValue() == "undefined";
|
||||||
|
switch (condition.operator) {
|
||||||
|
case "==":
|
||||||
|
body = undef ? alternative : consequent;
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
body = undef ? consequent : alternative;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!body) return;
|
||||||
|
var def = sym.definition();
|
||||||
|
var tw = new TreeWalker(function(node) {
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
var parent = tw.parent();
|
||||||
|
if (parent instanceof AST_Call && parent.expression === node) return;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
|
||||||
|
});
|
||||||
|
body.walk(tw);
|
||||||
|
}
|
||||||
|
|
||||||
OPT(AST_If, function(self, compressor) {
|
OPT(AST_If, function(self, compressor) {
|
||||||
if (is_empty(self.alternative)) self.alternative = null;
|
if (is_empty(self.alternative)) self.alternative = null;
|
||||||
|
|
||||||
@@ -4594,11 +4707,6 @@ merge(Compressor.prototype, {
|
|||||||
self.body = self.alternative || make_node(AST_EmptyStatement, self);
|
self.body = self.alternative || make_node(AST_EmptyStatement, self);
|
||||||
self.alternative = tmp;
|
self.alternative = tmp;
|
||||||
}
|
}
|
||||||
if (is_empty(self.body) && is_empty(self.alternative)) {
|
|
||||||
return make_node(AST_SimpleStatement, self.condition, {
|
|
||||||
body: self.condition.clone()
|
|
||||||
}).optimize(compressor);
|
|
||||||
}
|
|
||||||
if (self.body instanceof AST_SimpleStatement
|
if (self.body instanceof AST_SimpleStatement
|
||||||
&& self.alternative instanceof AST_SimpleStatement) {
|
&& self.alternative instanceof AST_SimpleStatement) {
|
||||||
return make_node(AST_SimpleStatement, self, {
|
return make_node(AST_SimpleStatement, self, {
|
||||||
@@ -4632,15 +4740,22 @@ merge(Compressor.prototype, {
|
|||||||
}).transform(compressor)
|
}).transform(compressor)
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
if (self.body instanceof AST_EmptyStatement
|
if (is_empty(self.body)) {
|
||||||
&& self.alternative instanceof AST_SimpleStatement) {
|
if (is_empty(self.alternative)) return make_node(AST_SimpleStatement, self.condition, {
|
||||||
return make_node(AST_SimpleStatement, self, {
|
body: self.condition.clone()
|
||||||
|
}).optimize(compressor);
|
||||||
|
if (self.alternative instanceof AST_SimpleStatement) return make_node(AST_SimpleStatement, self, {
|
||||||
body: make_node(AST_Binary, self, {
|
body: make_node(AST_Binary, self, {
|
||||||
operator : "||",
|
operator : "||",
|
||||||
left : self.condition,
|
left : self.condition,
|
||||||
right : self.alternative.body
|
right : self.alternative.body
|
||||||
}).transform(compressor)
|
}).transform(compressor)
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
|
self = make_node(AST_If, self, {
|
||||||
|
condition: negated,
|
||||||
|
body: self.alternative,
|
||||||
|
alternative: null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (self.body instanceof AST_Exit
|
if (self.body instanceof AST_Exit
|
||||||
&& self.alternative instanceof AST_Exit
|
&& self.alternative instanceof AST_Exit
|
||||||
@@ -4684,6 +4799,7 @@ merge(Compressor.prototype, {
|
|||||||
body: [ self, body ]
|
body: [ self, body ]
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
|
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -5671,7 +5787,7 @@ merge(Compressor.prototype, {
|
|||||||
// "undefined" == typeof x => undefined === x
|
// "undefined" == typeof x => undefined === x
|
||||||
else if (compressor.option("typeofs")
|
else if (compressor.option("typeofs")
|
||||||
&& self.left instanceof AST_String
|
&& self.left instanceof AST_String
|
||||||
&& self.left.value == "undefined"
|
&& self.left.getValue() == "undefined"
|
||||||
&& self.right instanceof AST_UnaryPrefix
|
&& self.right instanceof AST_UnaryPrefix
|
||||||
&& self.right.operator == "typeof") {
|
&& self.right.operator == "typeof") {
|
||||||
var expr = self.right.expression;
|
var expr = self.right.expression;
|
||||||
@@ -6023,9 +6139,12 @@ merge(Compressor.prototype, {
|
|||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
&& self.left instanceof AST_Binary
|
&& self.left instanceof AST_Binary
|
||||||
&& self.left.operator != "%"
|
&& self.left.operator != "%"
|
||||||
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]) {
|
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
|
||||||
|
&& self.left.is_number(compressor)) {
|
||||||
if (self.left.left instanceof AST_Constant
|
if (self.left.left instanceof AST_Constant
|
||||||
&& (self.left.operator != "+" || self.left.right.is_number(compressor))) {
|
&& (self.operator != "+"
|
||||||
|
|| self.left.left.is_boolean(compressor)
|
||||||
|
|| self.left.left.is_number(compressor))) {
|
||||||
self = make_node(AST_Binary, self, {
|
self = make_node(AST_Binary, self, {
|
||||||
operator: self.left.operator,
|
operator: self.left.operator,
|
||||||
left: make_node(AST_Binary, self.left, {
|
left: make_node(AST_Binary, self.left, {
|
||||||
@@ -6037,13 +6156,16 @@ merge(Compressor.prototype, {
|
|||||||
}),
|
}),
|
||||||
right: self.left.right
|
right: self.left.right
|
||||||
});
|
});
|
||||||
} else if (self.left.right instanceof AST_Constant
|
} else if (self.left.right instanceof AST_Constant) {
|
||||||
&& (self.left.operator != "+" || self.left.left.is_number(compressor))) {
|
var op = align(self.left.operator, self.operator);
|
||||||
|
if (op != "+"
|
||||||
|
|| self.left.right.is_boolean(compressor)
|
||||||
|
|| self.left.right.is_number(compressor)) {
|
||||||
self = make_node(AST_Binary, self, {
|
self = make_node(AST_Binary, self, {
|
||||||
operator: self.left.operator,
|
operator: self.left.operator,
|
||||||
left: self.left.left,
|
left: self.left.left,
|
||||||
right: make_node(AST_Binary, self.left, {
|
right: make_node(AST_Binary, self.left, {
|
||||||
operator: align(self.left.operator, self.operator),
|
operator: op,
|
||||||
left: self.left.right,
|
left: self.left.right,
|
||||||
right: self.right,
|
right: self.right,
|
||||||
start: self.left.right.start,
|
start: self.left.right.start,
|
||||||
@@ -6052,9 +6174,18 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (compressor.option("typeofs")) switch (self.operator) {
|
||||||
|
case "&&":
|
||||||
|
mark_locally_defined(self.left, self.right, null, "&&");
|
||||||
|
break;
|
||||||
|
case "||":
|
||||||
|
mark_locally_defined(self.left, null, self.right, "||");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var indexRight = is_indexFn(self.right);
|
var indexRight = is_indexFn(self.right);
|
||||||
if (in_bool
|
if (in_bool
|
||||||
@@ -6397,18 +6528,39 @@ merge(Compressor.prototype, {
|
|||||||
var ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &");
|
var ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &");
|
||||||
var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
|
var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
|
||||||
OPT(AST_Assign, function(self, compressor) {
|
OPT(AST_Assign, function(self, compressor) {
|
||||||
var def;
|
if (compressor.option("dead_code")) {
|
||||||
if (compressor.option("dead_code")
|
if (self.left instanceof AST_PropAccess) {
|
||||||
&& self.left instanceof AST_SymbolRef
|
if (self.operator == "=") {
|
||||||
&& (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) {
|
var exp = self.left.expression;
|
||||||
|
if (exp instanceof AST_Lambda
|
||||||
|
|| !compressor.has_directive("use strict")
|
||||||
|
&& exp instanceof AST_Constant
|
||||||
|
&& !exp.may_throw_on_access(compressor)) {
|
||||||
|
return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
|
||||||
|
self.left.property,
|
||||||
|
self.right
|
||||||
|
]).optimize(compressor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (self.left instanceof AST_SymbolRef) {
|
||||||
if (self.left.is_immutable()) return strip_assignment();
|
if (self.left.is_immutable()) return strip_assignment();
|
||||||
|
var def = self.left.definition();
|
||||||
|
var scope = def.scope.resolve();
|
||||||
|
var local = scope === compressor.find_parent(AST_Lambda);
|
||||||
var level = 0, node, parent = self;
|
var level = 0, node, parent = self;
|
||||||
do {
|
do {
|
||||||
node = parent;
|
node = parent;
|
||||||
parent = compressor.parent(level++);
|
parent = compressor.parent(level++);
|
||||||
if (parent instanceof AST_Exit) {
|
if (parent instanceof AST_Assign) {
|
||||||
|
if (!(parent.left instanceof AST_SymbolRef)) continue;
|
||||||
|
if (parent.left.definition() !== def) continue;
|
||||||
if (in_try(level, parent)) break;
|
if (in_try(level, parent)) break;
|
||||||
if (is_reachable(def.scope, [ def ])) break;
|
def.fixed = false;
|
||||||
|
return strip_assignment();
|
||||||
|
} else if (parent instanceof AST_Exit) {
|
||||||
|
if (!local) break;
|
||||||
|
if (in_try(level, parent)) break;
|
||||||
|
if (is_reachable(scope, [ def ])) break;
|
||||||
def.fixed = false;
|
def.fixed = false;
|
||||||
return strip_assignment();
|
return strip_assignment();
|
||||||
}
|
}
|
||||||
@@ -6416,6 +6568,7 @@ merge(Compressor.prototype, {
|
|||||||
|| parent instanceof AST_Sequence && parent.tail_node() === node
|
|| parent instanceof AST_Sequence && parent.tail_node() === node
|
||||||
|| parent instanceof AST_UnaryPrefix);
|
|| parent instanceof AST_UnaryPrefix);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self = self.lift_sequences(compressor);
|
self = self.lift_sequences(compressor);
|
||||||
if (!compressor.option("assignments")) return self;
|
if (!compressor.option("assignments")) return self;
|
||||||
if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
|
if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
|
||||||
@@ -6453,9 +6606,9 @@ merge(Compressor.prototype, {
|
|||||||
self.right = make_node(AST_Null, right);
|
self.right = make_node(AST_Null, right);
|
||||||
var may_throw = node.may_throw(compressor);
|
var may_throw = node.may_throw(compressor);
|
||||||
self.right = right;
|
self.right = right;
|
||||||
var scope = self.left.definition().scope;
|
|
||||||
var parent;
|
var parent;
|
||||||
while ((parent = compressor.parent(level++)) !== scope) {
|
while (parent = compressor.parent(level++)) {
|
||||||
|
if (parent === scope) return false;
|
||||||
if (parent instanceof AST_Try) {
|
if (parent instanceof AST_Try) {
|
||||||
if (parent.bfinally) return true;
|
if (parent.bfinally) return true;
|
||||||
if (may_throw && parent.bcatch) return true;
|
if (may_throw && parent.bcatch) return true;
|
||||||
@@ -6522,7 +6675,7 @@ merge(Compressor.prototype, {
|
|||||||
&& alt_tail instanceof AST_Assign
|
&& alt_tail instanceof AST_Assign
|
||||||
&& seq_tail.operator == alt_tail.operator
|
&& seq_tail.operator == alt_tail.operator
|
||||||
&& seq_tail.left.equivalent_to(alt_tail.left)
|
&& seq_tail.left.equivalent_to(alt_tail.left)
|
||||||
&& (is_eq && !seq_tail.left.has_side_effects(compressor)
|
&& (is_eq && seq_tail.left instanceof AST_SymbolRef
|
||||||
|| !condition.has_side_effects(compressor)
|
|| !condition.has_side_effects(compressor)
|
||||||
&& can_shift_lhs_of_tail(consequent)
|
&& can_shift_lhs_of_tail(consequent)
|
||||||
&& can_shift_lhs_of_tail(alternative))) {
|
&& can_shift_lhs_of_tail(alternative))) {
|
||||||
@@ -6602,8 +6755,8 @@ merge(Compressor.prototype, {
|
|||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
||||||
if (is_true(self.consequent)) {
|
if (is_true(consequent)) {
|
||||||
if (is_false(self.alternative)) {
|
if (is_false(alternative)) {
|
||||||
// c ? true : false ---> !!c
|
// c ? true : false ---> !!c
|
||||||
return booleanize(condition);
|
return booleanize(condition);
|
||||||
}
|
}
|
||||||
@@ -6611,11 +6764,11 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: booleanize(condition),
|
left: booleanize(condition),
|
||||||
right: self.alternative
|
right: alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_false(self.consequent)) {
|
if (is_false(consequent)) {
|
||||||
if (is_true(self.alternative)) {
|
if (is_true(alternative)) {
|
||||||
// c ? false : true ---> !c
|
// c ? false : true ---> !c
|
||||||
return booleanize(condition.negate(compressor));
|
return booleanize(condition.negate(compressor));
|
||||||
}
|
}
|
||||||
@@ -6623,26 +6776,26 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: booleanize(condition.negate(compressor)),
|
left: booleanize(condition.negate(compressor)),
|
||||||
right: self.alternative
|
right: alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_true(self.alternative)) {
|
if (is_true(alternative)) {
|
||||||
// c ? x : true ---> !c || x
|
// c ? x : true ---> !c || x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: booleanize(condition.negate(compressor)),
|
left: booleanize(condition.negate(compressor)),
|
||||||
right: self.consequent
|
right: consequent
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_false(self.alternative)) {
|
if (is_false(alternative)) {
|
||||||
// c ? x : false ---> !!c && x
|
// c ? x : false ---> !!c && x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: booleanize(condition),
|
left: booleanize(condition),
|
||||||
right: self.consequent
|
right: consequent
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
function booleanize(node) {
|
function booleanize(node) {
|
||||||
@@ -6691,16 +6844,9 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function can_shift_lhs_of_tail(node) {
|
function can_shift_lhs_of_tail(node) {
|
||||||
if (node === node.tail_node()) return true;
|
return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
|
||||||
var exprs = node.expressions;
|
return !expr.has_side_effects(compressor);
|
||||||
for (var i = exprs.length - 1; --i >= 0;) {
|
});
|
||||||
var expr = exprs[i];
|
|
||||||
if (!(expr instanceof AST_Assign) && expr.has_side_effects(compressor)
|
|
||||||
|| expr.operator != "="
|
|
||||||
|| expr.left.has_side_effects(compressor)
|
|
||||||
|| expr.right.has_side_effects(compressor)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pop_lhs(node) {
|
function pop_lhs(node) {
|
||||||
@@ -6981,6 +7127,41 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OPT(AST_Object, function(self, compressor) {
|
||||||
|
if (!compressor.option("objects") || compressor.has_directive("use strict")) return self;
|
||||||
|
var props = self.properties;
|
||||||
|
var keys = new Dictionary();
|
||||||
|
var values = [];
|
||||||
|
self.properties.forEach(function(prop) {
|
||||||
|
if (typeof prop.key != "string") {
|
||||||
|
flush();
|
||||||
|
values.push(prop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (prop.value.has_side_effects(compressor)) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
keys.add(prop.key, prop.value);
|
||||||
|
});
|
||||||
|
flush();
|
||||||
|
if (self.properties.length != values.length) {
|
||||||
|
return make_node(AST_Object, self, {
|
||||||
|
properties: values
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
|
||||||
|
function flush() {
|
||||||
|
keys.each(function(expressions, key) {
|
||||||
|
values.push(make_node(AST_ObjectKeyVal, self, {
|
||||||
|
key: key,
|
||||||
|
value: make_sequence(self, expressions)
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
keys = new Dictionary();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_Return, function(self, compressor) {
|
OPT(AST_Return, function(self, compressor) {
|
||||||
if (self.value && is_undefined(self.value, compressor)) {
|
if (self.value && is_undefined(self.value, compressor)) {
|
||||||
self.value = null;
|
self.value = null;
|
||||||
|
|||||||
@@ -1253,6 +1253,7 @@ function parse($TEXT, options) {
|
|||||||
var ex = expression(true);
|
var ex = expression(true);
|
||||||
var len = start.comments_before.length;
|
var len = start.comments_before.length;
|
||||||
[].unshift.apply(ex.start.comments_before, start.comments_before);
|
[].unshift.apply(ex.start.comments_before, start.comments_before);
|
||||||
|
start.comments_before.length = 0;
|
||||||
start.comments_before = ex.start.comments_before;
|
start.comments_before = ex.start.comments_before;
|
||||||
start.comments_before_length = len;
|
start.comments_before_length = len;
|
||||||
if (len == 0 && start.comments_before.length > 0) {
|
if (len == 0 && start.comments_before.length > 0) {
|
||||||
@@ -1268,6 +1269,7 @@ function parse($TEXT, options) {
|
|||||||
var end = prev();
|
var end = prev();
|
||||||
end.comments_before = ex.end.comments_before;
|
end.comments_before = ex.end.comments_before;
|
||||||
[].push.apply(ex.end.comments_after, end.comments_after);
|
[].push.apply(ex.end.comments_after, end.comments_after);
|
||||||
|
end.comments_after.length = 0;
|
||||||
end.comments_after = ex.end.comments_after;
|
end.comments_after = ex.end.comments_after;
|
||||||
ex.end = end;
|
ex.end = end;
|
||||||
if (ex instanceof AST_Call) mark_pure(ex);
|
if (ex instanceof AST_Call) mark_pure(ex);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "3.6.5",
|
"version": "3.6.9",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6348,3 +6348,123 @@ issue_3526_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_3562: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
conditionals: true,
|
||||||
|
sequences: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
console.log("PASS", a);
|
||||||
|
}
|
||||||
|
function g(b) {
|
||||||
|
console.log("FAIL", b);
|
||||||
|
}
|
||||||
|
var h;
|
||||||
|
var c;
|
||||||
|
if (console) {
|
||||||
|
h = f;
|
||||||
|
c = "PASS";
|
||||||
|
} else {
|
||||||
|
h = g;
|
||||||
|
c = "FAIL";
|
||||||
|
}
|
||||||
|
h(c);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
console.log("PASS", a);
|
||||||
|
}
|
||||||
|
function g(b) {
|
||||||
|
console.log("FAIL", b);
|
||||||
|
}
|
||||||
|
var h;
|
||||||
|
var c;
|
||||||
|
c = console ? (h = f, "PASS") : (h = g, "FAIL"),
|
||||||
|
h(c);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
dot_throw_assign_sequence: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "FAIL";
|
||||||
|
try {
|
||||||
|
var b;
|
||||||
|
b[0] = (a = "PASS", 0);
|
||||||
|
a = 1 + a;
|
||||||
|
} catch (c) {
|
||||||
|
}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "FAIL";
|
||||||
|
try {
|
||||||
|
var b;
|
||||||
|
b[0] = (a = "PASS", 0);
|
||||||
|
a = 1 + a;
|
||||||
|
} catch (c) {
|
||||||
|
}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
call_assign_order: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a, b = 1, c = 0, log = console.log;
|
||||||
|
(function() {
|
||||||
|
a = b = "PASS";
|
||||||
|
})((b = "FAIL", c++));
|
||||||
|
log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a, b = 1, c = 0, log = console.log;
|
||||||
|
(function() {
|
||||||
|
a = b = "PASS";
|
||||||
|
})((b = "FAIL", c++));
|
||||||
|
log(a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3573: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var c = 0;
|
||||||
|
(function(b) {
|
||||||
|
while (--b) {
|
||||||
|
b = NaN;
|
||||||
|
switch (0 / this < 0) {
|
||||||
|
case c++, false:
|
||||||
|
case c++, NaN:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(3);
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var c = 0;
|
||||||
|
(function(b) {
|
||||||
|
while (--b) {
|
||||||
|
b = NaN;
|
||||||
|
switch (0 / this < 0) {
|
||||||
|
case c++, false:
|
||||||
|
case c++, NaN:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(3);
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|||||||
@@ -161,6 +161,24 @@ ifs_6: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ifs_7: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
if (A); else;
|
||||||
|
if (A) while (B); else;
|
||||||
|
if (A); else while (C);
|
||||||
|
if (A) while (B); else while (C);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
A;
|
||||||
|
if (A) while (B);
|
||||||
|
if (!A) while (C);
|
||||||
|
if (A) while (B); else while (C);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cond_1: {
|
cond_1: {
|
||||||
options = {
|
options = {
|
||||||
conditionals: true,
|
conditionals: true,
|
||||||
@@ -1471,3 +1489,29 @@ angularjs_chain: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_3576: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var c = "FAIL";
|
||||||
|
(function(a) {
|
||||||
|
(a = -1) ? (a && (a.a = 0)) : (a && (a.a = 0));
|
||||||
|
a && a[c = "PASS"]++;
|
||||||
|
})();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var c = "FAIL";
|
||||||
|
(function(a) {
|
||||||
|
a = -1, a, a.a = 0;
|
||||||
|
a, a[c = "PASS"]++;
|
||||||
|
})();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1013,3 +1013,119 @@ issue_3406: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "true"
|
expect_stdout: "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function_assign: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = "PASS";
|
||||||
|
function h(c) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
h.p = function(b) {
|
||||||
|
return b;
|
||||||
|
}.p = a;
|
||||||
|
return h;
|
||||||
|
}().p);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = "PASS";
|
||||||
|
function h(c) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
h.p = a;
|
||||||
|
return h;
|
||||||
|
}().p);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3552: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "PASS";
|
||||||
|
(function() {
|
||||||
|
(1..p += 42) && (a = "FAIL");
|
||||||
|
})();
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "PASS";
|
||||||
|
(function() {
|
||||||
|
(1..p += 42) && (a = "FAIL");
|
||||||
|
})();
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable_assign: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(A = "P" + (A = "A" + (B = "S" + (A = B = "S"))), A, B);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(A = "P" + "A" + (B = "S" + "S"), A, B);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS PASS SS"
|
||||||
|
}
|
||||||
|
|
||||||
|
catch_return_assign: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
return e = "PASS";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3578: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "FAIL", b, c;
|
||||||
|
try {
|
||||||
|
b = c.p = b = 0;
|
||||||
|
} catch (e) {
|
||||||
|
b += 42;
|
||||||
|
b && (a = "PASS");
|
||||||
|
}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "FAIL", b, c;
|
||||||
|
try {
|
||||||
|
b = c.p = b = 0;
|
||||||
|
} catch (e) {
|
||||||
|
b += 42;
|
||||||
|
b && (a = "PASS");
|
||||||
|
}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -2187,3 +2187,37 @@ issue_3515_3: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function_assign: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = "PASS";
|
||||||
|
function g(b) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
g.p = a;
|
||||||
|
function h(c) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
h.p = a;
|
||||||
|
return h;
|
||||||
|
}().p);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = "PASS";
|
||||||
|
function h(c) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
h.p = a;
|
||||||
|
return h;
|
||||||
|
}().p);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -237,22 +237,39 @@ unsafe_constant: {
|
|||||||
unsafe: true,
|
unsafe: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log(
|
console.log(true.a, false.a);
|
||||||
true.a,
|
console.log(true.valueOf(), false.valueOf());
|
||||||
false.a,
|
try {
|
||||||
null.a,
|
console.log(null.a);
|
||||||
undefined.a
|
} catch (e) {
|
||||||
);
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
console.log(undefined.a);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log(
|
console.log(void 0, void 0);
|
||||||
void 0,
|
console.log(true, false);
|
||||||
false.a,
|
try {
|
||||||
null.a,
|
console.log(null.a);
|
||||||
(void 0).a
|
} catch (e) {
|
||||||
);
|
console.log("PASS");
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
try {
|
||||||
|
console.log((void 0).a);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"undefined undefined",
|
||||||
|
"true false",
|
||||||
|
"PASS",
|
||||||
|
"PASS",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe_object: {
|
unsafe_object: {
|
||||||
@@ -1757,3 +1774,129 @@ issue_3387_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "NaN"
|
expect_stdout: "NaN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simple_function_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function sum(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
console.log(sum(1, 2) * sum(3, 4));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(21);
|
||||||
|
}
|
||||||
|
expect_stdout: "21"
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_function_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var sum = function(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
console.log(sum(1, 2) * sum(3, 4));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(21);
|
||||||
|
}
|
||||||
|
expect_stdout: "21"
|
||||||
|
}
|
||||||
|
|
||||||
|
recursive_function_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function factorial(a) {
|
||||||
|
return a > 0 ? a * factorial(a - 1) : 1;
|
||||||
|
}
|
||||||
|
console.log(factorial(5));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function factorial(a) {
|
||||||
|
return a > 0 ? a * factorial(a - 1) : 1;
|
||||||
|
}(5));
|
||||||
|
}
|
||||||
|
expect_stdout: "120"
|
||||||
|
}
|
||||||
|
|
||||||
|
recursive_function_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var factorial = function(a) {
|
||||||
|
return a > 0 ? a * factorial(a - 1) : 1;
|
||||||
|
}
|
||||||
|
console.log(factorial(5));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var factorial = function(a) {
|
||||||
|
return a > 0 ? a * factorial(a - 1) : 1;
|
||||||
|
}
|
||||||
|
console.log(factorial(5));
|
||||||
|
}
|
||||||
|
expect_stdout: "120"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3558: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
return 1 + --a;
|
||||||
|
}
|
||||||
|
console.log(f(true), f(false));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
return 1 + --a;
|
||||||
|
}
|
||||||
|
console.log(1, 0);
|
||||||
|
}
|
||||||
|
expect_stdout: "1 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3568: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 0;
|
||||||
|
function f(b) {
|
||||||
|
return b && b.p;
|
||||||
|
}
|
||||||
|
console.log(f(++a + f()));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 0;
|
||||||
|
function f(b) {
|
||||||
|
return b && b.p;
|
||||||
|
}
|
||||||
|
console.log(NaN);
|
||||||
|
}
|
||||||
|
expect_stdout: "NaN"
|
||||||
|
}
|
||||||
|
|||||||
@@ -3370,3 +3370,33 @@ issue_3512: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_3562: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "PASS";
|
||||||
|
function f(b) {
|
||||||
|
f = function() {
|
||||||
|
console.log(b);
|
||||||
|
};
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
a = f(a);
|
||||||
|
f(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "PASS";
|
||||||
|
function f(b) {
|
||||||
|
f = function() {
|
||||||
|
console.log(b);
|
||||||
|
};
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
a = f(a);
|
||||||
|
f(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -781,3 +781,139 @@ issue_3539: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "NaN -Infinity Infinity"
|
expect_stdout: "NaN -Infinity Infinity"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_3547_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe_math: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
[
|
||||||
|
1/0 + "1" + 0,
|
||||||
|
1/0 + "1" - 0,
|
||||||
|
1/0 - "1" + 0,
|
||||||
|
1/0 - "1" - 0,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
[
|
||||||
|
1/0 + "10",
|
||||||
|
NaN,
|
||||||
|
1/0,
|
||||||
|
1/0,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"string Infinity10",
|
||||||
|
"number NaN",
|
||||||
|
"number Infinity",
|
||||||
|
"number Infinity",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3547_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe_math: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
[
|
||||||
|
"1" + 1/0 + 0,
|
||||||
|
"1" + 1/0 - 0,
|
||||||
|
"1" - 1/0 + 0,
|
||||||
|
"1" - 1/0 - 0,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
[
|
||||||
|
"1" + 1/0 + 0,
|
||||||
|
NaN,
|
||||||
|
-1/0,
|
||||||
|
-1/0,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"string 1Infinity0",
|
||||||
|
"number NaN",
|
||||||
|
"number -Infinity",
|
||||||
|
"number -Infinity",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3547_3: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe_math: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "3";
|
||||||
|
[
|
||||||
|
a + "2" + 1,
|
||||||
|
a + "2" - 1,
|
||||||
|
a - "2" + 1,
|
||||||
|
a - "2" - 1,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "3";
|
||||||
|
[
|
||||||
|
a + "21",
|
||||||
|
a + "2" - 1,
|
||||||
|
a - 1,
|
||||||
|
a - "2" - 1,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"string 321",
|
||||||
|
"number 31",
|
||||||
|
"number 2",
|
||||||
|
"number 0",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_3547_4: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe_math: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "2";
|
||||||
|
[
|
||||||
|
"3" + a + 1,
|
||||||
|
"3" + a - 1,
|
||||||
|
"3" - a + 1,
|
||||||
|
"3" - a - 1,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "2";
|
||||||
|
[
|
||||||
|
"3" + a + 1,
|
||||||
|
"3" + a - 1,
|
||||||
|
"3" - a + 1,
|
||||||
|
2 - a,
|
||||||
|
].forEach(function(n) {
|
||||||
|
console.log(typeof n, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"string 321",
|
||||||
|
"number 31",
|
||||||
|
"number 2",
|
||||||
|
"number 0",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
223
test/compress/objects.js
Normal file
223
test/compress/objects.js
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
duplicate_key: {
|
||||||
|
options = {
|
||||||
|
objects: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = {
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
a: 3,
|
||||||
|
};
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = {
|
||||||
|
a: 3,
|
||||||
|
b: 2,
|
||||||
|
};
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"a 3",
|
||||||
|
"b 2",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicate_key_strict: {
|
||||||
|
options = {
|
||||||
|
objects: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
var o = {
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
a: 3,
|
||||||
|
};
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
var o = {
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
a: 3,
|
||||||
|
};
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicate_key_side_effect: {
|
||||||
|
options = {
|
||||||
|
objects: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = {
|
||||||
|
a: 1,
|
||||||
|
b: o = 2,
|
||||||
|
a: 3,
|
||||||
|
};
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = {
|
||||||
|
a: 1,
|
||||||
|
b: o = 2,
|
||||||
|
a: 3,
|
||||||
|
};
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"a 3",
|
||||||
|
"b 2",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicate_key_with_accessor: {
|
||||||
|
options = {
|
||||||
|
objects: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
a: 0,
|
||||||
|
b: 1,
|
||||||
|
a: 2,
|
||||||
|
set b(v) {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: 3,
|
||||||
|
b: 4,
|
||||||
|
get a() {
|
||||||
|
return 5;
|
||||||
|
},
|
||||||
|
a: 6,
|
||||||
|
b: 7,
|
||||||
|
a: 8,
|
||||||
|
b: 9,
|
||||||
|
},
|
||||||
|
].forEach(function(o) {
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
a: 2,
|
||||||
|
b: 1,
|
||||||
|
set b(v) {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: 3,
|
||||||
|
b: 4,
|
||||||
|
get a() {
|
||||||
|
return 5;
|
||||||
|
},
|
||||||
|
a: 8,
|
||||||
|
b: 9,
|
||||||
|
},
|
||||||
|
].forEach(function(o) {
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe_object_repeated: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
objects: true,
|
||||||
|
reduce_funcs: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = { a: { b: 1 }, a: 1 };
|
||||||
|
console.log(
|
||||||
|
o + 1,
|
||||||
|
o.a + 1,
|
||||||
|
o.b + 1,
|
||||||
|
o.a.b + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = { a: 1 };
|
||||||
|
console.log(
|
||||||
|
o + 1,
|
||||||
|
2,
|
||||||
|
o.b + 1,
|
||||||
|
NaN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
numeric_literal: {
|
||||||
|
options = {
|
||||||
|
objects: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
mangle = {
|
||||||
|
properties: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var obj = {
|
||||||
|
0: 0,
|
||||||
|
"-0": 1,
|
||||||
|
42: 2,
|
||||||
|
"42": 3,
|
||||||
|
0x25: 4,
|
||||||
|
"0x25": 5,
|
||||||
|
1E42: 6,
|
||||||
|
"1E42": 7,
|
||||||
|
"1e+42": 8,
|
||||||
|
};
|
||||||
|
console.log(obj[-0], obj[-""], obj["-0"]);
|
||||||
|
console.log(obj[42], obj["42"]);
|
||||||
|
console.log(obj[0x25], obj["0x25"], obj[37], obj["37"]);
|
||||||
|
console.log(obj[1E42], obj["1E42"], obj["1e+42"]);
|
||||||
|
}
|
||||||
|
expect_exact: [
|
||||||
|
'var obj = {',
|
||||||
|
' 0: 0,',
|
||||||
|
' "-0": 1,',
|
||||||
|
' 42: 3,',
|
||||||
|
' 37: 4,',
|
||||||
|
' o: 5,',
|
||||||
|
' 1e42: 8,',
|
||||||
|
' b: 7',
|
||||||
|
'};',
|
||||||
|
'',
|
||||||
|
'console.log(obj[-0], obj[-""], obj["-0"]);',
|
||||||
|
'',
|
||||||
|
'console.log(obj[42], obj["42"]);',
|
||||||
|
'',
|
||||||
|
'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
|
||||||
|
'',
|
||||||
|
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
|
||||||
|
]
|
||||||
|
expect_stdout: [
|
||||||
|
"0 0 1",
|
||||||
|
"3 3",
|
||||||
|
"4 5 4 4",
|
||||||
|
"8 7 8",
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -36,6 +36,20 @@ regexp_2: {
|
|||||||
expect_stdout: '["PASS","pass"]'
|
expect_stdout: '["PASS","pass"]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regexp_properties: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(/abc/g.source, /abc/g.global, /abc/g.ignoreCase, /abc/g.lastIndex, /abc/g.multiline);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("abc", true, false, /abc/g.lastIndex, false);
|
||||||
|
}
|
||||||
|
expect_stdout: "abc true false 0 false"
|
||||||
|
}
|
||||||
|
|
||||||
issue_3434_1: {
|
issue_3434_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
|||||||
@@ -295,3 +295,145 @@ issue_2728_6: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "function undefined"
|
expect_stdout: "function undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typeof_defined_1: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
typeofs: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"undefined" == typeof A && A;
|
||||||
|
"undefined" != typeof A && A;
|
||||||
|
"undefined" == typeof A || A;
|
||||||
|
"undefined" != typeof A || A;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"undefined" == typeof A && A;
|
||||||
|
"undefined" != typeof A || A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_defined_2: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
typeofs: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"function" == typeof A && A;
|
||||||
|
"function" != typeof A && A;
|
||||||
|
"function" == typeof A || A;
|
||||||
|
"function" != typeof A || A;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"function" != typeof A && A;
|
||||||
|
"function" == typeof A || A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_defined_3: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
typeofs: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"undefined" == typeof A && "undefined" == typeof B && (A, B);
|
||||||
|
"undefined" == typeof A && "undefined" != typeof B && (A, B);
|
||||||
|
"undefined" != typeof A && "undefined" == typeof B && (A, B);
|
||||||
|
"undefined" != typeof A && "undefined" != typeof B && (A, B);
|
||||||
|
"undefined" == typeof A && "undefined" == typeof B || (A, B);
|
||||||
|
"undefined" == typeof A && "undefined" != typeof B || (A, B);
|
||||||
|
"undefined" != typeof A && "undefined" == typeof B || (A, B);
|
||||||
|
"undefined" != typeof A && "undefined" != typeof B || (A, B);
|
||||||
|
"undefined" == typeof A || "undefined" == typeof B && (A, B);
|
||||||
|
"undefined" == typeof A || "undefined" != typeof B && (A, B);
|
||||||
|
"undefined" != typeof A || "undefined" == typeof B && (A, B);
|
||||||
|
"undefined" != typeof A || "undefined" != typeof B && (A, B);
|
||||||
|
"undefined" == typeof A || "undefined" == typeof B || (A, B);
|
||||||
|
"undefined" == typeof A || "undefined" != typeof B || (A, B);
|
||||||
|
"undefined" != typeof A || "undefined" == typeof B || (A, B);
|
||||||
|
"undefined" != typeof A || "undefined" != typeof B || (A, B);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"undefined" == typeof A && "undefined" == typeof B && (A, B);
|
||||||
|
"undefined" == typeof A && "undefined" != typeof B && A;
|
||||||
|
"undefined" != typeof A && "undefined" == typeof B && B;
|
||||||
|
"undefined" == typeof A && "undefined" == typeof B || (A, B);
|
||||||
|
"undefined" == typeof A && "undefined" != typeof B || (A, B);
|
||||||
|
"undefined" != typeof A && "undefined" == typeof B || (A, B);
|
||||||
|
"undefined" != typeof A && "undefined" != typeof B || (A, B);
|
||||||
|
"undefined" == typeof A || "undefined" == typeof B && B;
|
||||||
|
"undefined" != typeof A || "undefined" == typeof B && (A, B);
|
||||||
|
"undefined" != typeof A || "undefined" != typeof B && A;
|
||||||
|
"undefined" == typeof A || "undefined" != typeof B || B;
|
||||||
|
"undefined" != typeof A || "undefined" == typeof B || A;
|
||||||
|
"undefined" != typeof A || "undefined" != typeof B || (A, B);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_defined_4: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
typeofs: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"object" == typeof A && "object" == typeof B && (A, B);
|
||||||
|
"object" == typeof A && "object" != typeof B && (A, B);
|
||||||
|
"object" != typeof A && "object" == typeof B && (A, B);
|
||||||
|
"object" != typeof A && "object" != typeof B && (A, B);
|
||||||
|
"object" == typeof A && "object" == typeof B || (A, B);
|
||||||
|
"object" == typeof A && "object" != typeof B || (A, B);
|
||||||
|
"object" != typeof A && "object" == typeof B || (A, B);
|
||||||
|
"object" != typeof A && "object" != typeof B || (A, B);
|
||||||
|
"object" == typeof A || "object" == typeof B && (A, B);
|
||||||
|
"object" == typeof A || "object" != typeof B && (A, B);
|
||||||
|
"object" != typeof A || "object" == typeof B && (A, B);
|
||||||
|
"object" != typeof A || "object" != typeof B && (A, B);
|
||||||
|
"object" == typeof A || "object" == typeof B || (A, B);
|
||||||
|
"object" == typeof A || "object" != typeof B || (A, B);
|
||||||
|
"object" != typeof A || "object" == typeof B || (A, B);
|
||||||
|
"object" != typeof A || "object" != typeof B || (A, B);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"object" == typeof A && "object" != typeof B && B;
|
||||||
|
"object" != typeof A && "object" == typeof B && A;
|
||||||
|
"object" != typeof A && "object" != typeof B && (A, B);
|
||||||
|
"object" == typeof A && "object" == typeof B || (A, B);
|
||||||
|
"object" == typeof A && "object" != typeof B || (A, B);
|
||||||
|
"object" != typeof A && "object" == typeof B || (A, B);
|
||||||
|
"object" != typeof A && "object" != typeof B || (A, B);
|
||||||
|
"object" == typeof A || "object" == typeof B && A;
|
||||||
|
"object" == typeof A || "object" != typeof B && (A, B);
|
||||||
|
"object" != typeof A || "object" != typeof B && B;
|
||||||
|
"object" == typeof A || "object" == typeof B || (A, B);
|
||||||
|
"object" == typeof A || "object" != typeof B || A;
|
||||||
|
"object" != typeof A || "object" == typeof B || B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emberjs_global: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
conditionals: true,
|
||||||
|
if_return: true,
|
||||||
|
passes: 2,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
typeofs: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
if (typeof A === "object") {
|
||||||
|
a = A;
|
||||||
|
} else if (typeof B === "object") {
|
||||||
|
a = B;
|
||||||
|
} else {
|
||||||
|
throw new Error("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
if ("object" != typeof A && "object" != typeof B)
|
||||||
|
throw new Error("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: Error("PASS")
|
||||||
|
}
|
||||||
|
|||||||
@@ -259,6 +259,30 @@ describe("comments", function() {
|
|||||||
assert.strictEqual(result.code, code);
|
assert.strictEqual(result.code, code);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should handle comments around parenthesis correctly", function() {
|
||||||
|
var code = [
|
||||||
|
"a();",
|
||||||
|
"/* foo */",
|
||||||
|
"(b())",
|
||||||
|
"/* bar */",
|
||||||
|
"c();",
|
||||||
|
].join("\n");
|
||||||
|
var result = UglifyJS.minify(code, {
|
||||||
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
|
output: {
|
||||||
|
comments: "all",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code, [
|
||||||
|
"a();",
|
||||||
|
"/* foo */",
|
||||||
|
"b()",
|
||||||
|
"/* bar */;c();",
|
||||||
|
].join("\n"));
|
||||||
|
});
|
||||||
|
|
||||||
it("Should preserve comments around IIFE", function() {
|
it("Should preserve comments around IIFE", function() {
|
||||||
var result = UglifyJS.minify("/*a*/(/*b*/function(){/*c*/}/*d*/)/*e*/();", {
|
var result = UglifyJS.minify("/*a*/(/*b*/function(){/*c*/}/*d*/)/*e*/();", {
|
||||||
compress: false,
|
compress: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user