Compare commits

...

30 Commits

Author SHA1 Message Date
Alex Lam S.L
0f2687ecfc v3.6.7 2019-11-02 13:32:05 +08:00
Alex Lam S.L
1c0defdc03 enhance unsafe evaluate (#3564) 2019-11-02 03:34:32 +08:00
Alex Lam S.L
dcbf2236c7 more tests for #3562 (#3565) 2019-11-02 03:34:20 +08:00
Alex Lam S.L
24bb288832 fix corner case in collapse_vars (#3563)
fixes #3562
2019-11-01 22:38:19 +08:00
Alex Lam S.L
6ad8e1081f v3.6.6 2019-11-01 13:40:03 +08:00
Alex Lam S.L
815eff1f7c enhance if_return (#3560) 2019-11-01 02:08:31 +08:00
Alex Lam S.L
1e9b576ee9 fix corner case in evaluate (#3559)
fixes #3558
2019-11-01 00:01:25 +08:00
Alex Lam S.L
3797458365 enhance conditionals (#3557) 2019-10-31 09:33:46 +08:00
Alex Lam S.L
1858c2018c enhance typeofs (#3556) 2019-10-31 08:00:04 +08:00
Alex Lam S.L
ec7f071272 fix corner case in dead_code (#3553)
fixes #3552
2019-10-30 14:21:22 +08:00
Alex Lam S.L
f1eb03f2c0 enhance dead_code (#3551) 2019-10-30 06:34:54 +08:00
Alex Lam S.L
0f4cfa877a fix corner case in comments (#3550) 2019-10-30 03:49:39 +08:00
Alex Lam S.L
1d5c2becbd enhance evaluate (#3549) 2019-10-29 19:51:55 +08:00
Alex Lam S.L
22a09ea7c5 fix corner case in unsafe_math (#3548)
fixes #3547
2019-10-29 17:06:57 +08:00
Alex Lam S.L
bad664c632 compress object literals (#3546) 2019-10-29 16:53:48 +08:00
Alex Lam S.L
8a191c0a84 v3.6.5 2019-10-29 12:55:28 +08:00
Alex Lam S.L
83fb8b4ca1 fix corner case in ie8 (#3543)
fixes #3542
2019-10-28 23:54:27 +08:00
Alex Lam S.L
f38e31bd1e fix corner case in evaluate (#3540)
fixes #3539
2019-10-28 19:56:42 +08:00
Alex Lam S.L
24e8b47977 improve ufuzz resilience (#3541) 2019-10-28 18:08:51 +08:00
Alex Lam S.L
95618793a4 fix corner case in ufuzz (#3538) 2019-10-28 16:04:07 +08:00
Alex Lam S.L
2f3b460212 fix & enhance unsafe_math (#3537)
closes #3535
fixes #3536
2019-10-28 13:37:08 +08:00
Alex Lam S.L
06e135e35f migrate CI workaround (#3534) 2019-10-27 17:29:54 +08:00
Alex Lam S.L
ebbf3d4a51 improve ufuzz resilience (#3533) 2019-10-27 14:17:35 +08:00
Alex Lam S.L
a270ba6b59 fix corner cases in unsafe_math (#3532)
fixes #3531
2019-10-27 08:25:11 +08:00
Alex Lam S.L
37f35e4ac2 prevent tty truncation in test/compress (#3530) 2019-10-27 05:00:21 +08:00
Alex Lam S.L
50a578c1f6 compress arithmetic expressions further (#3529) 2019-10-27 03:07:07 +08:00
Alex Lam S.L
85237b08d4 fix corner case in collapse_vars (#3527)
fixes #3526
2019-10-26 05:41:02 +08:00
Alex Lam S.L
27b159e711 separate ufuzz job failures (#3525) 2019-10-25 02:06:29 +08:00
Alex Lam S.L
82b3eed5ef fix corner case in ie8 & mangle (#3524)
fixes #3523
2019-10-24 23:43:19 +08:00
Alex Lam S.L
0f7aa41e33 fix corner case in collapse_vars (#3521)
fixes #3520
2019-10-24 01:13:57 +08:00
23 changed files with 2619 additions and 518 deletions

View File

@@ -5,6 +5,7 @@ on:
jobs: jobs:
ufuzz: ufuzz:
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ ubuntu-latest, windows-latest ] os: [ ubuntu-latest, windows-latest ]
name: ${{ matrix.os }} name: ${{ matrix.os }}

View File

@@ -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.

View File

@@ -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];
}); });
@@ -1128,6 +1130,8 @@ merge(Compressor.prototype, {
if (!stop_if_hit && in_conditional(node, parent)) { if (!stop_if_hit && in_conditional(node, parent)) {
stop_if_hit = parent; stop_if_hit = parent;
} }
// Skip transient nodes caused by single-use variable replacement
if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
// Replace variable with assignment when found // Replace variable with assignment when found
var hit_rhs; var hit_rhs;
if (can_replace if (can_replace
@@ -1226,6 +1230,7 @@ merge(Compressor.prototype, {
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor); var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
var scan_rhs = foldable(get_rhs(candidate)); var scan_rhs = foldable(get_rhs(candidate));
if (!scan_lhs && !scan_rhs) continue; if (!scan_lhs && !scan_rhs) continue;
var modify_toplevel = false;
// Locate symbols which may execute code outside of scanning range // Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate); var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs); var lhs_local = is_lhs_local(lhs);
@@ -1578,7 +1583,16 @@ merge(Compressor.prototype, {
if (candidate instanceof AST_VarDef) { if (candidate instanceof AST_VarDef) {
lvalues[candidate.name.name] = lhs; lvalues[candidate.name.name] = lhs;
} }
var scan_iife = scope instanceof AST_Toplevel;
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
if (scan_iife && node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_PropAccess) return;
if (exp instanceof AST_Function && !exp.contains_this()) return;
modify_toplevel = true;
scan_iife = false;
return;
}
var value; var value;
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
value = node.fixed_value() || node; value = node.fixed_value() || node;
@@ -1657,7 +1671,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;
} }
@@ -1665,6 +1679,7 @@ merge(Compressor.prototype, {
var def = sym.definition(); var def = sym.definition();
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false; if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
if (def.scope !== scope) return true; if (def.scope !== scope) return true;
if (modify_toplevel && compressor.exposed(def)) return true;
return !all(def.references, function(ref) { return !all(def.references, function(ref) {
return ref.scope.resolve() === scope; return ref.scope.resolve() === scope;
}); });
@@ -1734,9 +1749,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);
@@ -1764,23 +1777,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) {
@@ -1925,9 +1949,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;
} }
@@ -2424,9 +2446,10 @@ merge(Compressor.prototype, {
|| this.operator == "=" && this.right.is_number(compressor); || this.operator == "=" && this.right.is_number(compressor);
}); });
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
return binary[this.operator] || this.operator == "+" if (binary[this.operator]) return true;
&& this.left.is_number(compressor) if (this.operator != "+") return false;
&& this.right.is_number(compressor); return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
&& (this.right.is_boolean(compressor) || this.right.is_number(compressor));
}); });
var fn = makePredicate([ var fn = makePredicate([
"charCodeAt", "charCodeAt",
@@ -2846,10 +2869,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;
}); });
@@ -2885,7 +2908,20 @@ merge(Compressor.prototype, {
case ">=" : result = left >= right; break; case ">=" : result = left >= right; break;
default : return this; default : return this;
} }
return isNaN(result) && compressor.find_parent(AST_With) ? this : result; if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
if (compressor.option("unsafe_math")
&& result
&& typeof result == "number"
&& (this.operator == "+" || this.operator == "-")) {
var digits = Math.max(0, decimals(left), decimals(right));
if (digits < 21) return +result.toFixed(digits);
}
return result;
function decimals(operand) {
var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
}
}); });
def(AST_Conditional, function(compressor, cached, depth) { def(AST_Conditional, function(compressor, cached, depth) {
var condition = this.condition._eval(compressor, cached, depth); var condition = this.condition._eval(compressor, cached, depth);
@@ -2959,6 +2995,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;
@@ -2974,9 +3011,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":
@@ -2991,7 +3031,26 @@ 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);
});
});
return stat.value ? stat.value._eval(compressor, cached, depth) : undefined;
} 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);
@@ -3005,17 +3064,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);
@@ -3029,6 +3083,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) {
@@ -3378,19 +3443,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) {
@@ -3398,8 +3464,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) {
@@ -3408,7 +3475,6 @@ merge(Compressor.prototype, {
} }
} }
result = false; result = false;
}
return true; return true;
} }
})); }));
@@ -3737,9 +3803,7 @@ merge(Compressor.prototype, {
def.value = null; def.value = null;
head.push(def); head.push(def);
} else { } else {
var value = def.value var value = def.value && def.value.drop_side_effect_free(compressor);
&& !def.value.single_use
&& def.value.drop_side_effect_free(compressor);
if (value) { if (value) {
AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value); side_effects.push(value);
@@ -4213,7 +4277,7 @@ merge(Compressor.prototype, {
def(AST_Binary, function(compressor, first_in_statement) { def(AST_Binary, function(compressor, first_in_statement) {
var right = this.right.drop_side_effect_free(compressor, first_in_statement); var right = this.right.drop_side_effect_free(compressor, first_in_statement);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement); if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
if (lazy_op[this.operator]) { if (lazy_op[this.operator] && !(right instanceof AST_Function)) {
var node = this; var node = this;
if (right !== node.right) { if (right !== node.right) {
node = this.clone(); node = this.clone();
@@ -4518,6 +4582,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;
@@ -4569,11 +4676,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, {
@@ -4607,15 +4709,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
@@ -4659,6 +4768,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;
}); });
@@ -5646,7 +5756,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;
@@ -5766,6 +5876,7 @@ merge(Compressor.prototype, {
} }
} }
if (compressor.option("evaluate")) { if (compressor.option("evaluate")) {
var associative = true;
switch (self.operator) { switch (self.operator) {
case "&&": case "&&":
var ll = fuzzy_eval(self.left); var ll = fuzzy_eval(self.left);
@@ -5834,9 +5945,6 @@ merge(Compressor.prototype, {
}).optimize(compressor); }).optimize(compressor);
} }
break; break;
}
var associative = true;
switch (self.operator) {
case "+": case "+":
// "foo" + ("bar" + x) => "foobar" + x // "foo" + ("bar" + x) => "foobar" + x
if (self.left instanceof AST_Constant if (self.left instanceof AST_Constant
@@ -5916,13 +6024,39 @@ merge(Compressor.prototype, {
}); });
break; break;
} }
case "-":
// a - -b => a + b
if (self.right instanceof AST_UnaryPrefix
&& self.right.operator == "-"
&& self.left.is_number(compressor)
&& self.right.expression.is_number(compressor)) {
self = make_node(AST_Binary, self, {
operator: "+",
left: self.left,
right: self.right.expression
});
break;
}
case "*": case "*":
case "/":
associative = compressor.option("unsafe_math"); associative = compressor.option("unsafe_math");
// +a - b => a - b
// a - +b => a - b
if (self.operator != "+") {
if (self.left instanceof AST_UnaryPrefix && self.left.operator == "+") {
self.left = self.left.expression;
}
if (self.right instanceof AST_UnaryPrefix && self.right.operator == "+") {
self.right = self.right.expression;
}
}
case "&": case "&":
case "|": case "|":
case "^": case "^":
// a + +b => +b + a // a + +b => +b + a
if (self.left.is_number(compressor) if (self.operator != "-"
&& self.operator != "/"
&& self.left.is_number(compressor)
&& self.right.is_number(compressor) && self.right.is_number(compressor)
&& reversible() && reversible()
&& !(self.left instanceof AST_Binary && !(self.left instanceof AST_Binary
@@ -5940,12 +6074,17 @@ merge(Compressor.prototype, {
self = best_of(compressor, self, reversed); self = best_of(compressor, self, reversed);
} }
} }
if (associative && self.is_number(compressor)) { if (!associative || !self.is_number(compressor)) break;
// a + (b + c) => (a + b) + c // a + (b + c) => (a + b) + c
if (self.right instanceof AST_Binary if (self.right instanceof AST_Binary
&& self.right.operator == self.operator) { && self.right.operator != "%"
&& PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
&& self.right.is_number(compressor)
&& (self.operator != "+"
|| self.right.left.is_boolean(compressor)
|| self.right.left.is_number(compressor))) {
self = make_node(AST_Binary, self, { self = make_node(AST_Binary, self, {
operator: self.operator, operator: align(self.operator, self.right.operator),
left: make_node(AST_Binary, self.left, { left: make_node(AST_Binary, self.left, {
operator: self.operator, operator: self.operator,
left: self.left, left: self.left,
@@ -5955,15 +6094,28 @@ merge(Compressor.prototype, {
}), }),
right: self.right.right right: self.right.right
}); });
if (self.operator == "+"
&& !self.right.is_boolean(compressor)
&& !self.right.is_number(compressor)) {
self.right = make_node(AST_UnaryPrefix, self.right, {
operator: "+",
expression: self.right
});
} }
// (n + 2) + 3 => 5 + n }
// (2 * n) * 3 => 6 + n // (2 * n) * 3 => 6 * n
// (n + 2) + 3 => n + 5
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.operator) { && self.left.operator != "%"
if (self.left.left instanceof AST_Constant) { && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
&& self.left.is_number(compressor)) {
if (self.left.left instanceof AST_Constant
&& (self.operator != "+"
|| self.left.left.is_boolean(compressor)
|| self.left.left.is_number(compressor))) {
self = make_node(AST_Binary, self, { self = make_node(AST_Binary, self, {
operator: self.operator, operator: self.left.operator,
left: make_node(AST_Binary, self.left, { left: make_node(AST_Binary, self.left, {
operator: self.operator, operator: self.operator,
left: self.left.left, left: self.left.left,
@@ -5974,44 +6126,34 @@ 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) {
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.operator, operator: self.left.operator,
left: make_node(AST_Binary, self.left, { left: self.left.left,
operator: self.operator, right: make_node(AST_Binary, self.left, {
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,
end: self.right.end end: self.right.end
}), })
right: self.left.left
});
}
}
// (a | 1) | (2 | d) => (3 | a) | b
if (self.left instanceof AST_Binary
&& self.left.operator == self.operator
&& self.left.right instanceof AST_Constant
&& self.right instanceof AST_Binary
&& self.right.operator == self.operator
&& self.right.left instanceof AST_Constant) {
self = make_node(AST_Binary, self, {
operator: self.operator,
left: make_node(AST_Binary, self.left, {
operator: self.operator,
left: make_node(AST_Binary, self.left.left, {
operator: self.operator,
left: self.left.right,
right: self.right.left,
start: self.left.right.start,
end: self.right.left.end
}),
right: self.left.left
}),
right: self.right.right
}); });
} }
} }
} }
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);
@@ -6075,6 +6217,17 @@ merge(Compressor.prototype, {
} }
return self; return self;
function align(ref, op) {
switch (ref) {
case "-":
return op == "+" ? "-" : "+";
case "/":
return op == "*" ? "/" : "*";
default:
return op;
}
}
function fuzzy_eval(node) { function fuzzy_eval(node) {
if (node.truthy) return true; if (node.truthy) return true;
if (node.falsy) return false; if (node.falsy) return false;
@@ -6344,10 +6497,23 @@ 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) {
var def = self.left.definition();
if (def.scope === compressor.find_parent(AST_Lambda)) {
if (self.left.is_immutable()) return strip_assignment(); if (self.left.is_immutable()) return strip_assignment();
var level = 0, node, parent = self; var level = 0, node, parent = self;
do { do {
@@ -6363,6 +6529,8 @@ 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) {
@@ -6549,8 +6717,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);
} }
@@ -6558,11 +6726,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));
} }
@@ -6570,26 +6738,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) {
@@ -6928,6 +7096,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;

View File

@@ -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);

View File

@@ -214,7 +214,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
function redefine(node, scope) { function redefine(node, scope) {
var name = node.name; var name = node.name;
var old_def = node.thedef; var old_def = node.thedef;
var new_def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node); var new_def = scope.find_variable(name);
if (new_def) {
var redef;
while (redef = new_def.redefined()) new_def = redef;
} else {
new_def = self.globals.get(name) || scope.def_variable(node);
}
old_def.orig.concat(old_def.references).forEach(function(node) { old_def.orig.concat(old_def.references).forEach(function(node) {
node.thedef = new_def; node.thedef = new_def;
node.reference(options); node.reference(options);

View File

@@ -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.4", "version": "3.6.7",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -94,6 +94,3 @@ urls.forEach(function(url) {
}); });
}); });
}); });
setInterval(function() {
process.stderr.write("\0");
}, 5 * 60 * 1000).unref();

View File

@@ -1,3 +1,7 @@
"use strict";
require("../tools/exit");
var assert = require("assert"); var assert = require("assert");
var child_process = require("child_process"); var child_process = require("child_process");
var fs = require("fs"); var fs = require("fs");

View File

@@ -96,7 +96,7 @@ asm_mixed: {
return +sum; return +sum;
} }
function geometricMean(start, end) { function geometricMean(start, end) {
return start |= 0, end |= 0, +exp(+logSum(start, end) / +(end - start | 0)); return start |= 0, end |= 0, +exp(logSum(start, end) / (end - start | 0));
} }
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer); var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
return { geometricMean: geometricMean }; return { geometricMean: geometricMean };

View File

@@ -6258,3 +6258,132 @@ cond_sequence_return: {
} }
expect_stdout: "2" expect_stdout: "2"
} }
issue_3520: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
var a = 0;
var b = function(c) {
for (var i = 2; --i >= 0;) {
(function f() {
c = 0;
var i = void 0;
var f = f && f[i];
})();
a += b;
c && b++;
}
}(b = 1);
console.log(a);
}
expect: {
var a = 0;
var b = function(c) {
for (var i = 2; --i >= 0;) {
(function() {
c = 0;
var f = f && f[void 0];
})();
a += b;
c && b++;
}
}(b = 1);
console.log(a);
}
expect_stdout: "2"
}
issue_3526_1: {
options = {
collapse_vars: true,
}
input: {
var b = function() {
this.a = "FAIL";
}();
var a = "PASS";
var b;
var c = b;
console.log(a);
}
expect: {
var b = function() {
this.a = "FAIL";
}();
var a = "PASS";
var b;
var c = b;
console.log(a);
}
expect_stdout: "PASS"
}
issue_3526_2: {
options = {
collapse_vars: true,
}
input: {
function f() {
this.a = "FAIL";
}
var b = f();
var a = "PASS";
var b;
var c = b;
console.log(a);
}
expect: {
function f() {
this.a = "FAIL";
}
var b = f();
var a = "PASS";
var b;
var c = b;
console.log(a);
}
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"
}

View File

@@ -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,

View File

@@ -1013,3 +1013,54 @@ 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"
}

View File

@@ -2121,7 +2121,8 @@ issue_3515_1: {
expect: { expect: {
var c = 0; var c = 0;
(function() { (function() {
for (var key20 in !(this[c++] = 0)); this[c++] = 0;
for (var key20 in !0);
})(); })();
console.log(c); console.log(c);
} }
@@ -2186,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"
}

View File

@@ -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,105 @@ 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"
}

View File

@@ -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"
}

View File

@@ -1959,3 +1959,405 @@ issue_3493_ie8: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3523: {
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var f, g, h, i, j, k, l, m, n, o, p, q, r, s;
})();
try {
throw 0;
} catch (t) {
(function() {
(function t() {
c = "PASS";
})();
})();
(function e() {
try {} catch (t) {}
})();
}
console.log(c);
}
expect: {
var a = 0, b, c = "FAIL";
(function() {
var c, n, t, o, a, r, f, i, u, h, l, v, y, A;
})();
try {
throw 0;
} catch (n) {
(function() {
(function n() {
c = "PASS";
})();
})();
(function c() {
try {} catch (c) {}
})();
}
console.log(c);
}
expect_stdout: "PASS"
}
issue_3523_ie8: {
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var f, g, h, i, j, k, l, m, n, o, p, q, r, s;
})();
try {
throw 0;
} catch (t) {
(function() {
(function t() {
c = "PASS";
})();
})();
(function e() {
try {} catch (t) {}
})();
}
console.log(c);
}
expect: {
var a = 0, b, c = "FAIL";
(function() {
var c, t, n, o, a, r, f, i, u, h, e, l, v, y;
})();
try {
throw 0;
} catch (t) {
(function() {
(function t() {
c = "PASS";
})();
})();
(function e() {
try {} catch (t) {}
})();
}
console.log(c);
}
expect_stdout: "PASS"
}
issue_3523_toplevel: {
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var f, g, h, i, j, k, l, m, n, o, p, q, r, s;
})();
try {
throw 0;
} catch (t) {
(function() {
(function t() {
c = "PASS";
})();
})();
(function e() {
try {} catch (t) {}
})();
}
console.log(c);
}
expect: {
var c = 0, n, t = "FAIL";
(function() {
var c, n, t, o, r, a, f, i, u, h, l, v, y, A;
})();
try {
throw 0;
} catch (c) {
(function() {
(function c() {
t = "PASS";
})();
})();
(function c() {
try {} catch (c) {}
})();
}
console.log(t);
}
expect_stdout: "PASS"
}
issue_3523_ie8_toplevel: {
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var f, g, h, i, j, k, l, m, n, o, p, q, r, s;
})();
try {
throw 0;
} catch (t) {
(function() {
(function t() {
c = "PASS";
})();
})();
(function e() {
try {} catch (t) {}
})();
}
console.log(c);
}
expect: {
var c = 0, n, t = "FAIL";
(function() {
var c, n, t, o, r, a, f, i, u, h, l, v, y, A;
})();
try {
throw 0;
} catch (o) {
(function() {
(function o() {
t = "PASS";
})();
})();
(function r() {
try {} catch (o) {}
})();
}
console.log(t);
}
expect_stdout: "PASS"
}
issue_3523_rename: {
rename = true
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var d, e, f, g, h, i, j, k, l, m, o, p, q, r;
})();
try {
throw 0;
} catch (e) {
(function() {
(function e() {
c = "PASS";
})();
})();
(function d() {
try {
} catch (e) {
}
})();
}
console.log(c);
}
expect: {
var a = 0, b, c = "FAIL";
(function() {
var c, n, t, o, a, r, f, i, u, h, l, v, y, A;
})();
try {
throw 0;
} catch (n) {
(function() {
(function n() {
c = "PASS";
})();
})();
(function c() {
try {} catch (c) {}
})();
}
console.log(c);
}
expect_stdout: "PASS"
}
issue_3523_rename_ie8: {
rename = true
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var d, e, f, g, h, i, j, k, l, m, o, p, q, r;
})();
try {
throw 0;
} catch (e) {
(function() {
(function e() {
c = "PASS";
})();
})();
(function d() {
try {
} catch (e) {
}
})();
}
console.log(c);
}
expect: {
var a = 0, b, c = "FAIL";
(function() {
var c, n, t, o, a, r, f, i, u, e, h, l, v, y;
})();
try {
throw 0;
} catch (e) {
(function() {
(function n() {
c = "PASS";
})();
})();
(function d() {
try {} catch (e) {}
})();
}
console.log(c);
}
expect_stdout: "PASS"
}
issue_3523_rename_toplevel: {
rename = true
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var d, e, f, g, h, i, j, k, l, m, o, p, q, r;
})();
try {
throw 0;
} catch (e) {
(function() {
(function e() {
c = "PASS";
})();
})();
(function d() {
try {
} catch (e) {
}
})();
}
console.log(c);
}
expect: {
var c = 0, n, t = "FAIL";
(function() {
var c, n, t, o, r, a, f, i, u, h, l, v, y, A;
})();
try {
throw 0;
} catch (c) {
(function() {
(function c() {
t = "PASS";
})();
})();
(function c() {
try {} catch (c) {}
})();
}
console.log(t);
}
expect_stdout: "PASS"
}
issue_3523_rename_ie8_toplevel: {
rename = true
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = 0, b, c = "FAIL";
(function() {
var d, e, f, g, h, i, j, k, l, m, o, p, q, r;
})();
try {
throw 0;
} catch (e) {
(function() {
(function e() {
c = "PASS";
})();
})();
(function d() {
try {
} catch (e) {
}
})();
}
console.log(c);
}
expect: {
var c = 0, n, t = "FAIL";
(function() {
var c, n, t, o, r, a, f, i, u, h, l, v, y, A;
})();
try {
throw 0;
} catch (o) {
(function() {
(function o() {
t = "PASS";
})();
})();
(function r() {
try {} catch (o) {}
})();
}
console.log(t);
}
expect_stdout: "PASS"
}
issue_3542: {
options = {
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
var b = a++;
var c = b && function a() {} || b;
console.log(a);
}
expect: {
var a = 0;
a++;
(function a() {});
console.log(a);
}
expect_stdout: "1"
}

View File

@@ -51,17 +51,20 @@ comparisons: {
comparisons: true, comparisons: true,
} }
input: { input: {
var x = "42", y = "0x30";
console.log( console.log(
~x === 42, ~x === 42,
x % n === 42 x % y === 42
); );
} }
expect: { expect: {
var x = "42", y = "0x30";
console.log( console.log(
42 == ~x, 42 == ~x,
x % n == 42 x % y == 42
); );
} }
expect_stdout: "false true"
} }
evaluate_1: { evaluate_1: {
@@ -103,7 +106,7 @@ evaluate_1: {
} }
} }
evaluate_2: { evaluate_1_unsafe_math: {
options = { options = {
evaluate: true, evaluate: true,
unsafe_math: true, unsafe_math: true,
@@ -118,6 +121,8 @@ evaluate_2: {
1 + x-- + 2 + 3, 1 + x-- + 2 + 3,
1 + (x*y + 2) + 3, 1 + (x*y + 2) + 3,
1 + (2 + x + 3), 1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3), 1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3) 1 + (2 + (x |= 0) + 3)
); );
@@ -126,18 +131,138 @@ evaluate_2: {
console.log( console.log(
x + 1 + 2, x + 1 + 2,
2 * x, 2 * x,
3 + +x, +x + 3,
1 + x + 2 + 3, 1 + x + 2 + 3,
3 | x, 3 | x,
6 + x--, 6 + x--,
6 + x*y, x*y + 6,
1 + (2 + x + 3), 1 + (2 + x + 3),
6 + ~x,
5 - y + ~x,
0 & x, 0 & x,
6 + (x |= 0) 6 + (x |= 0)
); );
} }
} }
evaluate_2: {
options = {
evaluate: true,
unsafe_math: false,
}
input: {
var x = "42", y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var x = "42", y = null;
[
x + 1 + 2,
1 * x * 2,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
1 + (2 + x + 3),
2 + ~x + 3 + 1,
2 + ~x + 3 - y,
0 & x,
2 + (x |= 0) + 3 + 1,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"string 4212",
"number 84",
"number 45",
"string 14223",
"number 43",
"number 48",
"number 6",
"number 47",
"number -36",
"number -37",
"number 0",
"number 47",
]
}
evaluate_2_unsafe_math: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var x = "42", y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var x = "42", y = null;
[
x + 1 + 2,
2 * x,
+x + 3,
1 + x + 2 + 3,
3 | x,
6 + x--,
x*y + 6,
1 + (2 + x + 3),
6 + ~x,
5 + ~x - y,
0 & x,
6 + (x |= 0),
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"string 4212",
"number 84",
"number 45",
"string 14223",
"number 43",
"number 48",
"number 6",
"number 47",
"number -36",
"number -37",
"number 0",
"number 47",
]
}
evaluate_3: { evaluate_3: {
options = { options = {
evaluate: true, evaluate: true,
@@ -148,7 +273,7 @@ evaluate_3: {
console.log(1 + Number(x) + 2); console.log(1 + Number(x) + 2);
} }
expect: { expect: {
console.log(3 + +x); console.log(+x + 3);
} }
} }
@@ -182,6 +307,352 @@ evaluate_4: {
} }
} }
evaluate_5: {
options = {
evaluate: true,
unsafe_math: false,
}
input: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
a - 2 + 3,
a - 2 - 3,
+a + 2 + 3,
+a + 2 - 3,
2 - a + 3,
2 - a - 3,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"number 6",
"number 0",
"number 2",
"number -4",
"number 6",
"number 0",
"number 4",
"number -2",
"number 6",
"number 4",
"number 0",
"number -2",
]
}
evaluate_5_unsafe_math: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var a = "1";
[
+a + 5,
+a + -1,
a - -1,
a - 5,
+a + 5,
+a + -1,
5 - a,
-1 - a,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"number 6",
"number 0",
"number 2",
"number -4",
"number 6",
"number 0",
"number 4",
"number -2",
"number 6",
"number 4",
"number 0",
"number -2",
]
}
evaluate_6: {
options = {
evaluate: true,
unsafe_math: false,
}
input: {
var a = "1";
[
-a + 2 + 3,
-a + 2 - 3,
-a - 2 + 3,
-a - 2 - 3,
2 + -a + 3,
2 + -a - 3,
2 - -a + 3,
2 - -a - 3,
2 + 3 + -a,
2 + 3 - -a,
2 - 3 + -a,
2 - 3 - -a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var a = "1";
[
2 - a + 3,
2 - a - 3,
-a - 2 + 3,
-a - 2 - 3,
2 - a + 3,
2 - a - 3,
2 - -a + 3,
2 - -a - 3,
5 - a,
5 - -a,
-1 - a,
-1 - -a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"number 4",
"number -2",
"number 0",
"number -6",
"number 4",
"number -2",
"number 6",
"number 0",
"number 4",
"number 6",
"number -2",
"number 0",
]
}
evaluate_6_unsafe_math: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = "1";
[
-a + 2 + 3,
-a + 2 - 3,
-a - 2 + 3,
-a - 2 - 3,
2 + -a + 3,
2 + -a - 3,
2 - -a + 3,
2 - -a - 3,
2 + 3 + -a,
2 + 3 - -a,
2 - 3 + -a,
2 - 3 - -a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var a = "1";
[
5 - a,
-1 - a,
-a - -1,
-a - 5,
5 - a,
-1 - a,
5 - -a,
-1 - -a,
5 - a,
5 - -a,
-1 - a,
-1 - -a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"number 4",
"number -2",
"number 0",
"number -6",
"number 4",
"number -2",
"number 6",
"number 0",
"number 4",
"number 6",
"number -2",
"number 0",
]
}
evaluate_7: {
options = {
evaluate: true,
unsafe_math: false,
}
input: {
var x = "42", y;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
+x - 2 + (3 + !y),
+x - 2 + (3 - !y),
+x - 2 - (3 + !y),
+x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var x = "42", y;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
x - 2 + (3 + !y),
x - 2 + (3 - !y),
x - 2 - (3 + !y),
x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"number 48",
"number 46",
"number 40",
"number 42",
"number 44",
"number 42",
"number 36",
"number 38",
]
}
evaluate_7_unsafe_math: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var x = "42", y;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
+x - 2 + (3 + !y),
+x - 2 + (3 - !y),
+x - 2 - (3 + !y),
+x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect: {
var x = "42", y;
[
+x + 5 + !y,
+x + 5 - !y,
+x + -1 - !y,
+x + -1 + !y,
x - -1 + !y,
x - -1 - !y,
x - 5 - !y,
x - 5 + !y,
].forEach(function(n) {
console.log(typeof n, n);
});
}
expect_stdout: [
"number 48",
"number 46",
"number 40",
"number 42",
"number 44",
"number 42",
"number 36",
"number 38",
]
}
NaN_redefined: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var NaN;
console.log(1 / (0 / 0));
}
expect: {
var NaN;
console.log(0 / 0);
}
expect_stdout: "NaN"
}
issue_1710: { issue_1710: {
options = { options = {
evaluate: true, evaluate: true,
@@ -230,3 +701,219 @@ unary_binary_parenthesis: {
} }
expect_stdout: true expect_stdout: true
} }
issue_3531_1: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = "1";
console.log(typeof (a + 1 - .1 - .1 - .1));
}
expect: {
var a = "1";
console.log(typeof (a + 1 - .3));
}
expect_stdout: "number"
}
issue_3531_2: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
console.log(1 - (2 - {}));
}
expect: {
console.log(-1 + +{});
}
expect_stdout: "NaN"
}
issue_3531_3: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = "3";
console.log(1 - (2 + a));
}
expect: {
var a = "3";
console.log(1 - (2 + a));
}
expect_stdout: "-22"
}
issue_3536: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = 100, b = 10;
var c = --a + ("23" - (b++, 1));
console.log(typeof c, a, b, c);
}
expect: {
var a = 100, b = 10;
var c = --a + ("23" - (b++, 1));
console.log(typeof c, a, b, c);
}
expect_stdout: "number 99 11 121"
}
issue_3539: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = -0 + -"";
console.log(0/a, 1/a, -1/a);
}
expect: {
var a = -0;
console.log(0/a, 1/a, -1/a);
}
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
View 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",
]
}

View File

@@ -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,

View File

@@ -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")
}

View File

@@ -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,

View File

@@ -1,3 +1,6 @@
setInterval(function() {
process.stderr.write("\0");
}, 8 * 60 * 1000).unref();
require("./run")([ require("./run")([
"-b", "-b",
"-b braces", "-b braces",

File diff suppressed because it is too large Load Diff