Compare commits

...

34 Commits

Author SHA1 Message Date
Alex Lam S.L
c3ca293e6b v3.6.2 2019-10-12 20:19:05 +08:00
Alex Lam S.L
516b67a43b minor tweaks to CI test scripts (#3467) 2019-10-12 05:36:38 +08:00
Alex Lam S.L
eba3a37bb5 fix boolean context detection (#3466)
fixes #3465
2019-10-12 03:42:57 +08:00
Alex Lam S.L
6d57ca1a59 improve source map handling (#3464)
fixes #2947
fixes #3277
fixes #3411
2019-10-11 03:52:33 +08:00
Alex Lam S.L
3320251b4b update benchmark URLs (#3462) 2019-10-11 01:00:09 +08:00
Alex Lam S.L
33c94d3bd9 detect boolean context across IIFEs (#3461) 2019-10-10 09:37:02 +08:00
Alex Lam S.L
b18f717b46 improve readability of --help ast (#3460) 2019-10-10 04:32:32 +08:00
Alex Lam S.L
a0d4b648bb remove extraneous property (#3459)
fixes #3455
2019-10-10 01:36:58 +08:00
Alex Lam S.L
6db880e16d clean up AST_Binary optimisation logic (#3458) 2019-10-09 23:45:41 +08:00
Alex Lam S.L
a82003d6ac v3.6.1 2019-10-07 14:36:46 +08:00
Alex Lam S.L
da9f1622fc report errors correctly in ufuzz (#3456) 2019-10-07 14:36:00 +08:00
Alex Lam S.L
8a4c7077bb account for catch in constant lambda expressions (#3454) 2019-10-06 16:51:37 +08:00
Alex Lam S.L
0a63f2f2b0 workaround V8 RegExp bug (#3453)
fixes #3434
2019-10-06 11:49:39 +08:00
Alex Lam S.L
931ac66638 fix corner case in hoist_props (#3452)
fixes #3440
2019-10-06 10:29:13 +08:00
Alex Lam S.L
35338a100f handle function/variable name collisions correctly (#3451)
fixes #3439
2019-10-06 08:51:38 +08:00
David xu
d57b606e73 exclude mangling of addEventListener parameters (#3445) 2019-10-06 05:29:08 +08:00
Sampson Crowley
00ada04111 facilitate Webpack compatibility (#3435)
Verbose application of `require.resolve` instead of `[].map`
2019-10-06 05:20:47 +08:00
Alex Lam S.L
a31c477fea fix variable scope determination (#3449)
fixes #3444
2019-10-06 05:13:44 +08:00
Alex Lam S.L
bde7418ce1 update & fix dependencies (#3450) 2019-10-06 03:10:12 +08:00
Alex Lam S.L
70bb304a0a v3.6.0 2019-05-30 15:30:00 +08:00
Alex Lam S.L
9d3b1efd86 fix corner case in assignments (#3430)
fixes #3429
2019-05-30 05:01:53 +08:00
Alex Lam S.L
482e1baea3 enhance assignments & unused (#3428)
closes #3427
2019-05-29 01:21:08 +08:00
Alex Lam S.L
e4f5ba1d29 v3.5.15 2019-05-21 14:26:58 +08:00
Alex Lam S.L
b9053c7a25 fix corner case in keep_fargs (#3424)
fixes #3423
2019-05-21 12:55:34 +08:00
Alex Lam S.L
d357a7aabc v3.5.14 2019-05-20 00:13:06 +08:00
Alex Lam S.L
ae77ebe5a5 fix corner case in arguments (#3421)
fixes #3420
2019-05-19 12:59:40 +08:00
Alex Lam S.L
04439edcec v3.5.13 2019-05-17 14:10:33 +08:00
Alex Lam S.L
a246195412 enhance unsafe comparisons (#3419) 2019-05-17 01:28:18 +08:00
Alex Lam S.L
8939a36bc7 reduce false positives from fuzzing (#3417) 2019-05-16 16:15:03 +08:00
Alex Lam S.L
a21c348d93 improve sandbox fidelity (#3415) 2019-05-15 23:26:57 +08:00
Alex Lam S.L
1f0def10eb fix corner case in comparisons (#3414)
fixes #3413
2019-05-15 01:01:18 +08:00
Alex Lam S.L
f87caac9d8 fix corner case in hoist_props (#3412)
fixes #3411
2019-05-14 19:12:00 +08:00
Alex Lam S.L
d538a73250 enhance side_effects (#3410) 2019-05-14 05:26:40 +08:00
Alex Lam S.L
2e4fbdeb08 enhance keep_fargs (#3409) 2019-05-13 21:58:04 +08:00
30 changed files with 2536 additions and 313 deletions

View File

@@ -1,6 +1,6 @@
UglifyJS is released under the BSD license:
Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com>
Copyright 2012-2019 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions

View File

@@ -664,8 +664,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused
function arguments. You need this for code which relies on `Function.length`.
- `keep_fargs` (default: `strict`) -- Discard unused function arguments. Code
which relies on `Function.length` will break if this is done indiscriminately,
i.e. when passing `true`. Pass `false` to always retain function arguments.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on

View File

@@ -376,7 +376,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
}
}, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
$documentation: "Base class for functions",
$propdoc: {
name: "[AST_SymbolDeclaration?] the name of this function",
@@ -614,6 +614,18 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
$propdoc: {
expression: "[AST_Node] the “container” expression",
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
},
getProperty: function() {
var p = this.property;
if (p instanceof AST_Constant) {
return p.getValue();
}
if (p instanceof AST_UnaryPrefix
&& p.operator == "void"
&& p.expression instanceof AST_Constant) {
return;
}
return p;
}
});
@@ -825,11 +837,10 @@ var AST_String = DEFNODE("String", "value quote", {
}
}, AST_Constant);
var AST_Number = DEFNODE("Number", "value literal", {
var AST_Number = DEFNODE("Number", "value", {
$documentation: "A number literal",
$propdoc: {
value: "[number] the numeric value",
literal: "[string] numeric value as string (optional)"
}
}, AST_Constant);
@@ -968,6 +979,15 @@ TreeWalker.prototype = {
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else if (p instanceof AST_Return) {
var fn;
do {
fn = this.parent(++i);
if (!fn) return false;
} while (!(fn instanceof AST_Lambda));
if (fn.name) return false;
self = this.parent(++i);
if (!self || self.TYPE != "Call" || self.expression !== fn) return false;
} else {
return false;
}

View File

@@ -69,7 +69,7 @@ function Compressor(options, false_by_default) {
if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default,
keep_fargs : true,
keep_fargs : false_by_default || "strict",
keep_fnames : false,
keep_infinity : false,
loops : !false_by_default,
@@ -104,6 +104,17 @@ function Compressor(options, false_by_default) {
}
}
if (this.options["inline"] === true) this.options["inline"] = 3;
var keep_fargs = this.options["keep_fargs"];
this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) {
if (lambda.length_read) return false;
var name = lambda.name;
if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
if (name.fixed_value() !== lambda) return false;
var def = name.definition();
if (def.direct_access) return false;
var escaped = def.escaped;
return escaped && escaped.depth != 1;
} : keep_fargs ? return_false : return_true;
var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs;
@@ -118,6 +129,8 @@ function Compressor(options, false_by_default) {
} else {
this.pure_funcs = return_true;
}
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
var top_retain = this.options["top_retain"];
if (top_retain instanceof RegExp) {
this.top_retain = function(def) {
@@ -141,8 +154,6 @@ function Compressor(options, false_by_default) {
funcs: toplevel,
vars: toplevel
};
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
}
Compressor.prototype = new TreeTransformer;
@@ -272,14 +283,19 @@ merge(Compressor.prototype, {
self.transform(tt);
});
function read_property(obj, key) {
key = get_value(key);
function read_property(obj, node) {
var key = node.getProperty();
if (key instanceof AST_Node) return;
var value;
if (obj instanceof AST_Array) {
var elements = obj.elements;
if (key == "length") return make_node_from_constant(elements.length, obj);
if (typeof key == "number" && key in elements) value = elements[key];
} else if (obj instanceof AST_Lambda) {
if (key == "length") {
obj.length_read = true;
return make_node_from_constant(obj.argnames.length, obj);
}
} else if (obj instanceof AST_Object) {
key = "" + key;
var props = obj.properties;
@@ -326,11 +342,17 @@ merge(Compressor.prototype, {
return is_modified(compressor, tw, obj, obj, level + 2);
}
if (parent instanceof AST_PropAccess && parent.expression === node) {
var prop = read_property(value, parent.property);
var prop = read_property(value, parent);
return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
}
}
function is_arguments(def) {
if (def.name != "arguments") return false;
var orig = def.orig;
return orig.length == 1 && orig[0] instanceof AST_SymbolFunarg;
}
(function(def) {
def(AST_Node, noop);
@@ -428,9 +450,9 @@ merge(Compressor.prototype, {
if (def.single_use == "m") return false;
if (tw.safe_ids[def.id]) {
if (def.fixed == null) {
var orig = def.orig[0];
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
def.fixed = make_node(AST_Undefined, orig);
if (is_arguments(def)) return false;
if (def.global && def.name == "arguments") return false;
def.fixed = make_node(AST_Undefined, def.orig);
}
return true;
}
@@ -490,23 +512,24 @@ merge(Compressor.prototype, {
var obj = tw.parent(level + 1);
mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property);
value = read_property(value, parent);
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
if (value) return;
}
if (level > 0) return;
if (parent instanceof AST_Call && node === parent.expression) return;
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
if (parent instanceof AST_SimpleStatement) return;
if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
d.direct_access = true;
}
function mark_assignment_to_arguments(node) {
if (!(node instanceof AST_Sub)) return;
var expr = node.expression;
var prop = node.property;
if (expr instanceof AST_SymbolRef && expr.name == "arguments" && prop instanceof AST_Number) {
expr.definition().reassigned = true;
}
if (!(expr instanceof AST_SymbolRef)) return;
var def = expr.definition();
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
}
var suppressor = new TreeWalker(function(node) {
@@ -773,7 +796,7 @@ merge(Compressor.prototype, {
});
def(AST_Unary, function(tw, descend) {
var node = this;
if (node.operator != "++" && node.operator != "--") return;
if (!unary_arithmetic[node.operator]) return;
var exp = node.expression;
if (!(exp instanceof AST_SymbolRef)) {
mark_assignment_to_arguments(exp);
@@ -869,9 +892,13 @@ merge(Compressor.prototype, {
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda;
});
function is_lhs_read_only(lhs) {
function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_This) return true;
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
if (lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
return def.orig[0] instanceof AST_SymbolLambda
|| compressor.exposed(def) && identifier_atom[def.name];
}
if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression;
if (lhs instanceof AST_SymbolRef) {
@@ -880,7 +907,7 @@ merge(Compressor.prototype, {
}
if (!lhs) return true;
if (lhs.is_constant()) return true;
return is_lhs_read_only(lhs);
return is_lhs_read_only(lhs, compressor);
}
return false;
}
@@ -1195,7 +1222,7 @@ merge(Compressor.prototype, {
var stop_if_hit = null;
var lhs = get_lhs(candidate);
var side_effects = lhs && lhs.has_side_effects(compressor);
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
var scan_rhs = foldable(get_rhs(candidate));
if (!scan_lhs && !scan_rhs) continue;
// Locate symbols which may execute code outside of scanning range
@@ -1266,6 +1293,7 @@ merge(Compressor.prototype, {
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
}
if (node instanceof AST_Debugger) return true;
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
if (node instanceof AST_IterationStatement) return !(node instanceof AST_For);
if (node instanceof AST_LoopControl) return true;
if (node instanceof AST_Try) return true;
@@ -1419,7 +1447,7 @@ merge(Compressor.prototype, {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
if (unary_arithmetic[expr.operator]) {
candidates.push(hit_stack.slice());
} else {
extract_candidates(expr.expression);
@@ -1489,9 +1517,9 @@ merge(Compressor.prototype, {
function mangleable_var(var_def) {
var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return;
var def = value.definition();
if (def.undeclared) return;
if (is_arguments(def)) return;
return value_def = def;
}
@@ -2217,18 +2245,6 @@ merge(Compressor.prototype, {
}));
}
function get_value(key) {
if (key instanceof AST_Constant) {
return key.getValue();
}
if (key instanceof AST_UnaryPrefix
&& key.operator == "void"
&& key.expression instanceof AST_Constant) {
return;
}
return key;
}
function is_undefined(node, compressor) {
return node.is_undefined
|| node instanceof AST_Undefined
@@ -2267,8 +2283,7 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
return !compressor.option("pure_getters")
|| this._dot_throw(compressor);
return !compressor.option("pure_getters") || this._dot_throw(compressor);
});
function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters"));
@@ -2276,7 +2291,15 @@ merge(Compressor.prototype, {
def(AST_Node, is_strict);
def(AST_Array, return_false);
def(AST_Assign, function(compressor) {
return this.operator == "=" && this.right._dot_throw(compressor);
if (this.operator != "=") return false;
var rhs = this.right;
if (!rhs._dot_throw(compressor)) return false;
var sym = this.left;
if (!(sym instanceof AST_SymbolRef)) return true;
if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
return rhs.right._dot_throw(compressor);
}
return true;
});
def(AST_Binary, function(compressor) {
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
@@ -2307,6 +2330,7 @@ merge(Compressor.prototype, {
if (!is_strict(compressor)) return false;
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
if (this.is_immutable()) return false;
if (is_arguments(this.definition())) return false;
var fixed = this.fixed_value();
if (!fixed) return true;
this._dot_throw = return_true;
@@ -2334,7 +2358,7 @@ merge(Compressor.prototype, {
case "&&":
return this.left.is_defined(compressor) && this.right.is_defined(compressor);
case "||":
return this.left.is_defined(compressor) || this.right.is_defined(compressor);
return this.left.is_truthy() || this.right.is_defined(compressor);
default:
return true;
}
@@ -2354,7 +2378,7 @@ merge(Compressor.prototype, {
if (this.is_immutable()) return true;
var fixed = this.fixed_value();
if (!fixed) return false;
this.is_defined = return_true;
this.is_defined = return_false;
var result = fixed.is_defined(compressor);
delete this.is_defined;
return result;
@@ -2549,6 +2573,7 @@ merge(Compressor.prototype, {
});
var lazy_op = makePredicate("&& ||");
var unary_arithmetic = makePredicate("++ --");
var unary_side_effects = makePredicate("delete ++ --");
function is_lhs(node, parent) {
@@ -3375,16 +3400,28 @@ merge(Compressor.prototype, {
def(AST_Lambda, function(scope) {
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
var inner_scopes = [];
self.walk(new TreeWalker(function(node, descend) {
if (!result) return true;
if (node instanceof AST_Catch) {
inner_scopes.push(node.argname.scope);
descend();
inner_scopes.pop();
return true;
}
if (node instanceof AST_Scope && node !== self) {
inner_scopes.push(node);
descend();
inner_scopes.pop();
return true;
}
if (node instanceof AST_SymbolRef) {
if (self.inlined) {
result = false;
return true;
}
var def = node.definition();
if (member(def, self.enclosed)
&& !self.variables.has(def.name)) {
if (!self.variables.has(def.name) && !member(def.scope, inner_scopes)) {
if (scope) {
var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) {
@@ -3601,7 +3638,7 @@ merge(Compressor.prototype, {
var value = null;
if (node instanceof AST_Assign) {
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
value = node.right;
value = get_rhs(node);
}
} else if (!in_use) {
value = make_node(AST_Number, node, {
@@ -3621,7 +3658,7 @@ merge(Compressor.prototype, {
node.name = null;
}
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = !compressor.option("keep_fargs");
var trim = compressor.drop_fargs(node, parent);
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (!(sym.definition().id in in_use_ids)) {
@@ -3812,6 +3849,7 @@ merge(Compressor.prototype, {
};
}
});
tt.push(compressor.parent());
self.transform(tt);
function verify_safe_usage(def, read, modified) {
@@ -3825,6 +3863,15 @@ merge(Compressor.prototype, {
}
}
function get_rhs(assign) {
var rhs = assign.right;
if (!assign.write_only) return rhs;
if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
var sym = assign.left;
if (!(sym instanceof AST_SymbolRef) || sym.name != rhs.left.name) return rhs;
return rhs.right.has_side_effects(compressor) ? rhs : rhs.right;
}
function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
@@ -3832,9 +3879,10 @@ merge(Compressor.prototype, {
prop.walk(tw);
});
if (node instanceof AST_Assign) {
node.right.walk(tw);
var right = get_rhs(node);
right.walk(tw);
if (node.left === sym) {
if (!node_def.chained && sym.fixed_value(true) === node.right) {
if (!node_def.chained && sym.fixed_value(true) === right) {
fixed_ids[node_def.id] = node;
}
if (!node.write_only) {
@@ -4020,10 +4068,11 @@ merge(Compressor.prototype, {
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null);
self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_Assign
&& node.operator == "="
&& node.write_only
&& can_hoist(node.left, node.right, 1)) {
if (node instanceof AST_Assign) {
if (node.operator != "=") return;
if (!node.write_only) return;
if (node.left.scope !== self) return;
if (!can_hoist(node.left, node.right, 1)) return;
descend(node, this);
var defs = new Dictionary();
var assignments = [];
@@ -4052,7 +4101,9 @@ merge(Compressor.prototype, {
}));
return make_sequence(node, assignments);
}
if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) {
if (node instanceof AST_Scope) return node === self ? undefined : node;
if (node instanceof AST_VarDef) {
if (!can_hoist(node.name, node.value, 0)) return;
descend(node, this);
var defs = new Dictionary();
var var_defs = [];
@@ -4065,32 +4116,6 @@ merge(Compressor.prototype, {
defs_by_id[node.name.definition().id] = defs;
return MAP.splice(var_defs);
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function can_hoist(sym, right, count) {
if (sym.scope !== self) return;
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped.depth == 1) return;
if (def.references.length == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
return right instanceof AST_Object;
}
function make_sym(sym, key) {
var new_var = make_node(AST_SymbolVar, sym, {
@@ -4103,6 +4128,43 @@ merge(Compressor.prototype, {
return new_var;
}
}));
self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_PropAccess) {
if (!(node.expression instanceof AST_SymbolRef)) return;
var defs = defs_by_id[node.expression.definition().id];
if (!defs) return;
var def = defs.get(node.getProperty());
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
if (node instanceof AST_Unary) {
if (unary_side_effects[node.operator]) return;
if (!(node.expression instanceof AST_SymbolRef)) return;
if (!(node.expression.definition().id in defs_by_id)) return;
var opt = node.clone();
opt.expression = make_node(AST_Object, node, {
properties: []
});
return opt;
}
}));
function can_hoist(sym, right, count) {
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped.depth == 1) return;
if (def.references.length == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
return right instanceof AST_Object;
}
});
// drop_side_effect_free()
@@ -4135,12 +4197,14 @@ merge(Compressor.prototype, {
});
def(AST_Assign, function(compressor) {
var left = this.left;
if (left.has_side_effects(compressor)
|| compressor.has_directive("use strict")
&& left instanceof AST_PropAccess
&& left.expression.is_constant()) {
return this;
if (left instanceof AST_PropAccess) {
var expr = left.expression;
if (expr instanceof AST_Assign && !expr.may_throw_on_access(compressor)) {
expr.write_only = true;
}
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
@@ -4215,8 +4279,9 @@ merge(Compressor.prototype, {
});
def(AST_Constant, return_null);
def(AST_Dot, function(compressor, first_in_statement) {
if (this.expression.may_throw_on_access(compressor)) return this;
return this.expression.drop_side_effect_free(compressor, first_in_statement);
var expr = this.expression;
if (expr.may_throw_on_access(compressor)) return this;
return expr.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Function, function(compressor) {
return this.name && compressor.option("ie8") ? this : null;
@@ -4406,8 +4471,9 @@ merge(Compressor.prototype, {
OPT(AST_For, function(self, compressor) {
if (!compressor.option("loops")) return self;
if (compressor.option("side_effects") && self.init) {
self.init = self.init.drop_side_effect_free(compressor);
if (compressor.option("side_effects")) {
if (self.init) self.init = self.init.drop_side_effect_free(compressor);
if (self.step) self.step = self.step.drop_side_effect_free(compressor);
}
if (self.condition) {
var cond = self.condition.evaluate(compressor);
@@ -5171,25 +5237,26 @@ merge(Compressor.prototype, {
return return_value(stat);
}
function var_exists(catches, name) {
return catches[name] || identifier_atom[name] || scope.var_names()[name];
function var_exists(defined, name) {
return defined[name] || identifier_atom[name] || scope.var_names()[name];
}
function can_inject_args(catches, safe_to_inject) {
function can_inject_args(catches, used, safe_to_inject) {
for (var i = 0; i < fn.argnames.length; i++) {
var arg = fn.argnames[i];
if (arg.__unused) continue;
if (!safe_to_inject || var_exists(catches, arg.name)) return false;
used[arg.name] = true;
if (in_loop) in_loop.push(arg.definition());
}
return true;
}
function can_inject_vars(catches, safe_to_inject) {
function can_inject_vars(catches, used, safe_to_inject) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (stat instanceof AST_Defun) {
if (!safe_to_inject || var_exists(catches, stat.name.name)) return false;
if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
continue;
}
if (!(stat instanceof AST_Var)) continue;
@@ -5218,8 +5285,9 @@ merge(Compressor.prototype, {
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
&& (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope));
var inline = compressor.option("inline");
if (!can_inject_vars(catches, inline >= 3 && safe_to_inject)) return false;
if (!can_inject_args(catches, inline >= 2 && safe_to_inject)) return false;
var used = Object.create(catches);
if (!can_inject_args(catches, used, inline >= 2 && safe_to_inject)) return false;
if (!can_inject_vars(catches, used, inline >= 3 && safe_to_inject)) return false;
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
}
@@ -5527,20 +5595,29 @@ merge(Compressor.prototype, {
self.right = tmp;
}
}
if (commutativeOperators[self.operator]) {
if (self.right.is_constant()
&& !self.left.is_constant()) {
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse();
}
if (commutativeOperators[self.operator] && self.right.is_constant() && !self.left.is_constant()) {
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse();
}
}
self = self.lift_sequences(compressor);
if (compressor.option("assignments") && lazy_op[self.operator]) {
var assign = self.right;
// a || (a = x) => a = a || x
// a && (a = x) => a = a && x
if (self.left instanceof AST_SymbolRef
&& assign instanceof AST_Assign
&& assign.operator == "="
&& self.left.equivalent_to(assign.left)) {
self.right = assign.right;
assign.right = self;
return assign;
}
}
if (compressor.option("comparisons")) switch (self.operator) {
case "===":
case "!==":
@@ -5619,7 +5696,8 @@ merge(Compressor.prototype, {
}
break;
}
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (in_bool) switch (self.operator) {
case "+":
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
@@ -5652,9 +5730,9 @@ merge(Compressor.prototype, {
}
break;
}
var parent = compressor.parent();
if (compressor.option("comparisons") && self.is_boolean(compressor)) {
if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) {
if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: self.negate(compressor, first_in_statement(compressor))
@@ -5692,14 +5770,14 @@ merge(Compressor.prototype, {
var ll = fuzzy_eval(self.left);
if (!ll) {
AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
} else if (!(ll instanceof AST_Node)) {
AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
}
var rr = self.right.evaluate(compressor);
if (!rr) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
if (in_bool) {
AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
@@ -5707,9 +5785,7 @@ merge(Compressor.prototype, {
]).optimize(compressor);
} else self.falsy = true;
} else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
@@ -5731,18 +5807,16 @@ merge(Compressor.prototype, {
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} else if (!(ll instanceof AST_Node)) {
AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
}
var rr = self.right.evaluate(compressor);
if (!rr) {
var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
} else if (!(rr instanceof AST_Node)) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
if (in_bool) {
AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
@@ -5938,32 +6012,38 @@ merge(Compressor.prototype, {
}
}
}
if (compressor.option("unsafe")
&& self.right instanceof AST_Call
&& self.right.expression instanceof AST_Dot
&& indexFns[self.right.expression.property]) {
if (compressor.option("booleans")
if (compressor.option("unsafe")) {
var indexRight = is_indexFn(self.right);
if (in_bool
&& indexRight
&& (self.operator == "==" || self.operator == "!=")
&& self.left instanceof AST_Number
&& self.left.getValue() == 0
&& compressor.in_boolean_context()) {
&& self.left.getValue() == 0) {
return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: self.right
}) : self.right).optimize(compressor);
}
var indexLeft = is_indexFn(self.left);
if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
var node = make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_UnaryPrefix, self, {
operator: "~",
expression: self.right
expression: indexLeft ? self.left : self.right
})
});
if (self.operator == "!=" || self.operator == "<=") node = make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: node
});
switch (self.operator) {
case "<":
if (indexLeft) break;
case "<=":
case "!=":
node = make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: node
});
break;
}
return node.optimize(compressor);
}
}
@@ -6001,17 +6081,26 @@ merge(Compressor.prototype, {
return node.evaluate(compressor);
}
function is_indexFn(node) {
return node instanceof AST_Call
&& node.expression instanceof AST_Dot
&& indexFns[node.expression.property];
}
function is_indexOf_match_pattern() {
switch (self.operator) {
case ">":
case "<=":
// 0 > array.indexOf(string) => !~array.indexOf(string)
// 0 <= array.indexOf(string) => !!~array.indexOf(string)
return self.left instanceof AST_Number && self.left.getValue() == 0;
return indexRight && self.left instanceof AST_Number && self.left.getValue() == 0;
case "<":
// array.indexOf(string) < 0 => !~array.indexOf(string)
if (indexLeft && self.right instanceof AST_Number && self.right.getValue() == 0) return true;
// -1 < array.indexOf(string) => !!~array.indexOf(string)
case "==":
case "!=":
// -1 == array.indexOf(string) => !~array.indexOf(string)
// -1 != array.indexOf(string) => !!~array.indexOf(string)
if (!indexRight) return false;
return self.left instanceof AST_Number && self.left.getValue() == -1
|| self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
&& self.left.expression instanceof AST_Number && self.left.expression.getValue() == 1;
@@ -6635,24 +6724,30 @@ merge(Compressor.prototype, {
}
}
}
var fn;
var parent = compressor.parent();
var def, fn, fn_parent;
if (compressor.option("arguments")
&& expr instanceof AST_SymbolRef
&& expr.name == "arguments"
&& expr.definition().orig.length == 1
&& is_arguments(def = expr.definition())
&& prop instanceof AST_Number
&& (fn = expr.scope) === compressor.find_parent(AST_Lambda)) {
&& (fn = expr.scope) === find_lambda()) {
var index = prop.getValue();
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
if (!def.deleted) def.deleted = [];
def.deleted[index] = true;
}
var argname = fn.argnames[index];
if (argname && compressor.has_directive("use strict")) {
var def = argname.definition();
if (def.deleted && def.deleted[index]) {
argname = null;
} else if (argname && compressor.has_directive("use strict")) {
var arg_def = argname.definition();
if (!compressor.option("reduce_vars")
|| expr.definition().reassigned
|| def.assignments
|| def.orig.length > 1) {
|| def.reassigned
|| arg_def.assignments
|| arg_def.orig.length > 1) {
argname = null;
}
} else if (!argname && !compressor.option("keep_fargs") && index < fn.argnames.length + 5) {
} else if (!argname && index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent)) {
while (index >= fn.argnames.length) {
argname = make_node(AST_SymbolFunarg, fn, {
name: fn.make_var_name("argument_" + fn.argnames.length),
@@ -6665,13 +6760,14 @@ merge(Compressor.prototype, {
if (argname && find_if(function(node) {
return node.name === argname.name;
}, fn.argnames) === argname) {
def.reassigned = false;
var sym = make_node(AST_SymbolRef, self, argname);
sym.reference({});
delete argname.__unused;
return sym;
}
}
if (is_lhs(compressor.self(), compressor.parent())) return self;
if (is_lhs(compressor.self(), parent)) return self;
if (key !== prop) {
var sub = self.flatten_object(property, compressor);
if (sub) {
@@ -6720,6 +6816,16 @@ merge(Compressor.prototype, {
return best_of(compressor, ev, self);
}
return self;
function find_lambda() {
var i = 0, p;
while (p = compressor.parent(i++)) {
if (p instanceof AST_Lambda) {
fn_parent = compressor.parent(i);
return p;
}
}
}
});
AST_Scope.DEFMETHOD("contains_this", function() {

View File

@@ -1,19 +1,39 @@
"use strict";
var to_ascii = typeof atob == "undefined" ? function(b64) {
return new Buffer(b64, "base64").toString();
} : atob;
var to_base64 = typeof btoa == "undefined" ? function(str) {
return new Buffer(str).toString("base64");
} : btoa;
var to_ascii, to_base64;
if (typeof Buffer == "undefined") {
to_ascii = atob;
to_base64 = btoa;
} else if (typeof Buffer.alloc == "undefined") {
to_ascii = function(b64) {
return new Buffer(b64, "base64").toString();
};
to_base64 = function(str) {
return new Buffer(str).toString("base64");
};
} else {
to_ascii = function(b64) {
return Buffer.from(b64, "base64").toString();
};
to_base64 = function(str) {
return Buffer.from(str).toString("base64");
};
}
function read_source_map(name, code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(\S+)\s*$/.exec(code);
if (!match) {
AST_Node.warn("inline source map not found: " + name);
return null;
function read_source_map(name, toplevel) {
var comments = toplevel.end.comments_after;
for (var i = comments.length; --i >= 0;) {
var comment = comments[i];
if (comment.type != "comment1") break;
var match = /^# ([^\s=]+)=(\S+)\s*$/.exec(comment.value);
if (!match) break;
if (match[1] == "sourceMappingURL") {
match = /^data:application\/json(;.*?)?;base64,(\S+)$/.exec(match[2]);
if (!match) break;
return to_ascii(match[2]);
}
}
return to_ascii(match[2]);
AST_Node.warn("inline source map not found: " + name);
}
function parse_source_map(content) {
@@ -134,10 +154,10 @@ function minify(files, options) {
source_maps = source_map_content && Object.create(null);
for (var name in files) if (HOP(files, name)) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
options.parse.toplevel = toplevel = parse(files[name], options.parse);
if (source_maps) {
if (source_map_content == "inline") {
var inlined_content = read_source_map(name, files[name]);
var inlined_content = read_source_map(name, toplevel);
if (inlined_content) {
source_maps[name] = parse_source_map(inlined_content);
}
@@ -146,7 +166,6 @@ function minify(files, options) {
}
}
}
toplevel = options.parse.toplevel;
}
if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props);

View File

@@ -136,8 +136,7 @@ function OutputStream(options) {
function make_string(str, quote) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g,
function(s, i) {
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
switch (s) {
case '"': ++dq; return '"';
case "'": ++sq; return "'";
@@ -599,7 +598,6 @@ function OutputStream(options) {
}
print(encoded);
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
@@ -1383,8 +1381,27 @@ function OutputStream(options) {
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
str = output.to_utf8(str);
output.print(str);
output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(seq) {
switch (seq[1]) {
case "\n": return "\\n";
case "\r": return "\\r";
case "\t": return "\t";
case "\b": return "\b";
case "\f": return "\f";
case "\0": return "\0";
case "\x0B": return "\v";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
default: return seq;
}
}).replace(/[\n\r\u2028\u2029]/g, function(c) {
switch (c) {
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
}
}));
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
output.print(" ");

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.5.12",
"version": "3.6.2",
"engines": {
"node": ">=0.8.0"
},
@@ -23,12 +23,12 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.20.0",
"commander": "2.20.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~6.1.1",
"semver": "~6.0.0"
"acorn": "~7.1.0",
"semver": "~6.3.0"
},
"scripts": {
"test": "node test/compress.js && node test/mocha.js"

View File

@@ -13,15 +13,15 @@ if (!args.length) {
}
args.push("--timings");
var urls = [
"https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js",
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://code.jquery.com/jquery-3.4.1.js",
"https://code.angularjs.org/1.7.8/angular.js",
"https://unpkg.com/mathjs@6.2.3/dist/math.js",
"https://unpkg.com/react@15.3.2/dist/react.js",
"http://builds.emberjs.com/tags/v2.11.0/ember.prod.js",
"https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js",
"https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js",
"https://raw.githubusercontent.com/kangax/html-minifier/v3.5.7/dist/htmlminifier.js",
"https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.js",
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js",
"https://cdnjs.cloudflare.com/ajax/libs/ember.js/2.12.2/ember.prod.js",
"https://raw.githubusercontent.com/kangax/html-minifier/v4.0.0/dist/htmlminifier.js",
];
var results = {};
var remaining = 2 * urls.length;
@@ -94,3 +94,6 @@ urls.forEach(function(url) {
});
});
});
setInterval(function() {
process.stderr.write("\0");
}, 5 * 60 * 1000).unref();

View File

@@ -1,35 +1,47 @@
#! /usr/bin/env node
var assert = require("assert");
var child_process = require("child_process");
var fs = require("fs");
var path = require("path");
var sandbox = require("./sandbox");
var semver = require("semver");
var U = require("./node");
var failures = 0;
var failed_files = Object.create(null);
var minify_options = require("./ufuzz.json").map(JSON.stringify);
var file = process.argv[2];
var dir = path.resolve(path.dirname(module.filename), "compress");
fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
}).forEach(function(file) {
if (file) {
var minify_options = require("./ufuzz.json").map(JSON.stringify);
log("--- {file}", { file: file });
var tests = parse_test(path.resolve(dir, file));
for (var i in tests) if (!test_case(tests[i])) {
failures++;
failed_files[file] = 1;
}
});
if (failures) {
console.error();
console.error("!!! Failed " + failures + " test case(s).");
console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1);
process.exit(Object.keys(tests).filter(function(name) {
return !test_case(tests[name]);
}).length);
} else {
var files = fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
});
var failures = 0;
var failed_files = Object.create(null);
(function next() {
var file = files.shift();
if (file) {
child_process.spawn(process.argv[0], [ process.argv[1], file ], {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) {
failures += code;
failed_files[file] = code;
}
next();
});
} else if (failures) {
console.error();
console.error("!!! Failed " + failures + " test case(s).");
console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1);
}
})();
}
/* -----[ utils ]----- */
function evaluate(code) {
if (code instanceof U.AST_Node) code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")();
@@ -160,7 +172,7 @@ function parse_test(file) {
// Try to reminify original input with standard options
// to see if it matches expect_stdout.
function reminify(orig_options, input_code, input_formatted, expect_stdout) {
function reminify(orig_options, input_code, input_formatted, stdout) {
for (var i = 0; i < minify_options.length; i++) {
var options = JSON.parse(minify_options[i]);
if (options.compress) [
@@ -191,11 +203,12 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
});
return false;
} else {
var stdout = run_code(result.code);
if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
stdout = expect_stdout;
var expected = stdout[options.toplevel ? 1 : 0];
var actual = run_code(result.code, options.toplevel);
if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
actual = expected;
}
if (!sandbox.same_stdout(expect_stdout, stdout)) {
if (!sandbox.same_stdout(expected, actual)) {
log([
"!!! failed running reminified input",
"---INPUT---",
@@ -214,10 +227,10 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
input: input_formatted,
options: options_formatted,
output: result.code,
expected_type: typeof expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
expected_type: typeof expected == "string" ? "STDOUT" : "ERROR",
expected: expected,
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
actual: actual,
});
return false;
}
@@ -226,8 +239,8 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
return true;
}
function run_code(code) {
var result = sandbox.run_code(code, true);
function run_code(code, toplevel) {
var result = sandbox.run_code(code, toplevel);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}
@@ -359,13 +372,14 @@ function test_case(test) {
return false;
}
}
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = run_code(input_code);
if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = [ run_code(input_code), run_code(input_code, true) ];
var toplevel = test.options.toplevel;
var actual = stdout[toplevel ? 1 : 0];
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
test.expect_stdout = actual;
}
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! Invalid input or expected stdout",
"---INPUT---",
@@ -380,13 +394,13 @@ function test_case(test) {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
actual: actual,
});
return false;
}
stdout = run_code(output);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
actual = run_code(output, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! failed",
"---INPUT---",
@@ -401,12 +415,12 @@ function test_case(test) {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
actual: actual,
});
return false;
}
if (!reminify(test.options, input_code, input_formatted, test.expect_stdout)) {
if (!reminify(test.options, input_code, input_formatted, stdout)) {
return false;
}
}

View File

@@ -405,6 +405,52 @@ issue_3273_global_strict_reduce_vars: {
]
}
issue_3273_keep_fargs_false: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
(function() {
"use strict";
arguments[0]++;
console.log(arguments[0]);
})(0);
}
expect: {
(function(argument_0) {
"use strict";
argument_0++;
console.log(argument_0);
})(0);
}
expect_stdout: "1"
}
issue_3273_keep_fargs_strict: {
options = {
arguments: true,
keep_fargs: "strict",
reduce_vars: true,
}
input: {
(function() {
"use strict";
arguments[0]++;
console.log(arguments[0]);
})(0);
}
expect: {
(function(argument_0) {
"use strict";
argument_0++;
console.log(argument_0);
})(0);
}
expect_stdout: "1"
}
issue_3282_1: {
options = {
arguments: true,
@@ -576,3 +622,157 @@ issue_3282_2_passes: {
}
expect_stdout: true
}
issue_3420_1: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
return function() {
return arguments[0];
};
}().length);
}
expect: {
console.log(function() {
return function() {
return arguments[0];
};
}().length);
}
expect_stdout: "0"
}
issue_3420_2: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
var foo = function() {
delete arguments[0];
};
foo();
}
expect: {
var foo = function() {
delete arguments[0];
};
foo();
}
expect_stdout: true
}
issue_3420_3: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
var foo = function() {
delete arguments[0];
};
foo();
}
expect: {
"use strict";
var foo = function() {
delete arguments[0];
};
foo();
}
expect_stdout: true
}
issue_3420_4: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
!function() {
console.log(arguments[0]);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect: {
!function(argument_0) {
console.log(argument_0);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect_stdout: [
"42",
"undefined",
]
}
issue_3420_5: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
!function() {
console.log(arguments[0]);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect: {
"use strict";
!function(argument_0) {
console.log(argument_0);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect_stdout: [
"42",
"undefined",
]
}
issue_3420_6: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
return delete arguments[0];
}());
}
expect: {
console.log(function() {
return delete arguments[0];
}());
}
expect_stdout: "true"
}
issue_3420_7: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
console.log(function() {
return delete arguments[0];
}());
}
expect: {
"use strict";
console.log(function() {
return delete arguments[0];
}());
}
expect_stdout: "true"
}

View File

@@ -311,3 +311,65 @@ issue_3375: {
}
expect_stdout: "string"
}
issue_3427: {
options = {
assignments: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a || (a = {});
})();
}
expect: {}
}
issue_3429_1: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a = "PASS";
(function(b) {
b && (b = a = "FAIL");
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(b) {
b = b && (a = "FAIL");
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_3429_2: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a;
(function(b) {
b || (b = a = "FAIL");
})(42);
console.log(a);
}
expect: {
var a;
(function(b) {
b = b || (a = "FAIL");
})(42);
console.log(a);
}
expect_stdout: "undefined"
}

88
test/compress/booleans.js Normal file
View File

@@ -0,0 +1,88 @@
iife_boolean_context: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(function() {
return Object(1) || false;
}() ? "PASS" : "FAIL");
console.log(function() {
return [].length || true;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function() {
return Object(1);
}() ? "PASS" : "FAIL");
console.log(function() {
return [].length, 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: [
"PASS",
"PASS",
]
expect_warnings: [
"WARN: Dropping side-effect-free || [test/compress/booleans.js:2,19]",
"WARN: Boolean || always true [test/compress/booleans.js:5,19]",
]
}
issue_3465_1: {
options = {
booleans: true,
}
input: {
console.log(function(a) {
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function(a) {
return 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_3465_2: {
options = {
booleans: true,
}
input: {
console.log(function f(a) {
if (!a) console.log(f(42));
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function f(a) {
if (!a) console.log(f(42));
return typeof a;
}() ? "PASS" : "FAIL");
}
expect_stdout: [
"number",
"PASS",
]
}
issue_3465_3: {
options = {
booleans: true,
passes: 2,
unused: true,
}
input: {
console.log(function f(a) {
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function(a) {
return 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}

View File

@@ -6178,3 +6178,62 @@ assign_undeclared: {
"object",
]
}
Infinity_assignment: {
options = {
collapse_vars: true,
pure_getters: "strict",
unsafe: true,
}
input: {
var Infinity;
Infinity = 42;
console.log(Infinity);
}
expect: {
var Infinity;
Infinity = 42;
console.log(Infinity);
}
expect_stdout: true
}
issue_3439_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect_stdout: "function"
}
issue_3439_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
var a = 42;
function a() {}
return a;
}());
}
expect: {
console.log(typeof function() {
return 42;
}());
}
expect_stdout: "number"
}

View File

@@ -373,10 +373,70 @@ unsafe_indexOf: {
unsafe: true,
}
input: {
if (Object.keys({ foo: 42 }).indexOf("foo") >= 0) console.log("PASS");
var a = Object.keys({ foo: 42 });
if (a.indexOf("bar") < 0) console.log("PASS");
if (0 > a.indexOf("bar")) console.log("PASS");
if (a.indexOf("foo") >= 0) console.log("PASS");
if (0 <= a.indexOf("foo")) console.log("PASS");
if (a.indexOf("foo") > -1) console.log("PASS");
if (-1 < a.indexOf("foo")) console.log("PASS");
if (a.indexOf("bar") == -1) console.log("PASS");
if (-1 == a.indexOf("bar")) console.log("PASS");
if (a.indexOf("bar") === -1) console.log("PASS");
if (-1 === a.indexOf("bar")) console.log("PASS");
if (a.indexOf("foo") != -1) console.log("PASS");
if (-1 != a.indexOf("foo")) console.log("PASS");
if (a.indexOf("foo") !== -1) console.log("PASS");
if (-1 !== a.indexOf("foo")) console.log("PASS");
}
expect: {
if (~Object.keys({ foo: 42 }).indexOf("foo")) console.log("PASS");
var a = Object.keys({ foo: 42 });
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
]
}
issue_3413: {
options = {
comparisons: true,
evaluate: true,
side_effects: true,
}
input: {
var b;
void 0 !== ("" < b || void 0) || console.log("PASS");
}
expect: {
var b;
void 0 !== ("" < b || void 0) || console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -2028,3 +2028,37 @@ issue_3375: {
}
expect_stdout: "0 0"
}
issue_3427_1: {
options = {
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a = a || {};
})();
}
expect: {}
}
issue_3427_2: {
options = {
unused: true,
}
input: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect_stdout: "PASS"
}

View File

@@ -3148,3 +3148,51 @@ issue_3402: {
"function",
]
}
issue_3439: {
options = {
inline: true,
}
input: {
console.log(typeof function() {
return function(a) {
function a() {}
return a;
}(42);
}());
}
expect: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect_stdout: "function"
}
issue_3444: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(h) {
return f;
function f() {
g();
}
function g() {
h("PASS");
}
})(console.log)();
}
expect: {
(function(h) {
return function() {
void h("PASS");
};
})(console.log)();
}
expect_stdout: "PASS"
}

View File

@@ -862,3 +862,55 @@ issue_3071_3: {
}
expect_stdout: "2"
}
issue_3411: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
var c = 1;
!function f() {
var o = {
p: --c && f()
};
+o || console.log("PASS");
}();
}
expect: {
var c = 1;
!function f() {
var o_p = --c && f();
+{} || console.log("PASS");
}();
}
expect_stdout: "PASS"
}
issue_3440: {
options = {
hoist_props: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
function f() {
console.log(o.p);
}
var o = {
p: "PASS",
};
return f;
})()();
}
expect: {
(function() {
var o_p = "PASS";
return function() {
console.log(o_p);
};
})()();
}
expect_stdout: "PASS"
}

View File

@@ -366,7 +366,7 @@ mangle_catch_redef_3: {
console.log(o);
}
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
expect_stdout: "PASS"
expect_stdout: true
}
mangle_catch_redef_3_toplevel: {
@@ -389,10 +389,10 @@ mangle_catch_redef_3_toplevel: {
console.log(o);
}
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
expect_stdout: "PASS"
expect_stdout: true
}
mangle_catch_redef_ie8_3: {
mangle_catch_redef_3_ie8: {
mangle = {
ie8: true,
toplevel: false,
@@ -412,7 +412,7 @@ mangle_catch_redef_ie8_3: {
console.log(o);
}
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
expect_stdout: "PASS"
expect_stdout: true
}
mangle_catch_redef_3_ie8_toplevel: {
@@ -435,5 +435,5 @@ mangle_catch_redef_3_ie8_toplevel: {
console.log(o);
}
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
expect_stdout: "PASS"
expect_stdout: true
}

1157
test/compress/keep_fargs.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -673,3 +673,19 @@ issue_3371: {
}
expect_stdout: "PASS"
}
step: {
options = {
loops: true,
side_effects: true,
}
input: {
for (var i = 0; i < 42; "foo", i++, "bar");
console.log(i);
}
expect: {
for (var i = 0; i < 42; i++);
console.log(i);
}
expect_stdout: "42"
}

View File

@@ -1161,3 +1161,49 @@ collapse_rhs_lhs: {
}
expect_stdout: "1 3"
}
drop_arguments: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
(function() {
arguments.slice = function() {
console.log("PASS");
};
arguments[42];
arguments.length;
arguments.slice();
})();
}
expect: {
(function() {
arguments.slice = function() {
console.log("PASS");
};
arguments.slice();
})();
}
expect_stdout: "PASS"
}
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
(function(b) {
b.p = 42;
})(a || (a = {}));
}
expect: {}
}

View File

@@ -35,3 +35,140 @@ regexp_2: {
}
expect_stdout: '["PASS","pass"]'
}
issue_3434_1: {
options = {
evaluate: true,
unsafe: true,
}
beautify = {
beautify: true,
}
input: {
var o = {
"\n": RegExp("\n"),
"\r": RegExp("\r"),
"\t": RegExp("\t"),
"\b": RegExp("\b"),
"\f": RegExp("\f"),
"\0": RegExp("\0"),
"\x0B": RegExp("\x0B"),
"\u2028": RegExp("\u2028"),
"\u2029": RegExp("\u2029"),
};
for (var c in o)
console.log(o[c].test("\\"), o[c].test(c));
}
expect_exact: [
"var o = {",
' "\\n": /\\n/,',
' "\\r": /\\r/,',
' "\\t": /\t/,',
' "\\b": /\b/,',
' "\\f": /\f/,',
' "\\0": /\0/,',
' "\\v": /\v/,',
' "\\u2028": /\\u2028/,',
' "\\u2029": /\\u2029/',
"};",
"",
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
]
expect_stdout: [
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
]
}
issue_3434_2: {
options = {
evaluate: true,
unsafe: true,
}
beautify = {
beautify: true,
}
input: {
var o = {
"\n": RegExp("\\\n"),
"\r": RegExp("\\\r"),
"\t": RegExp("\\\t"),
"\b": RegExp("\\\b"),
"\f": RegExp("\\\f"),
"\0": RegExp("\\\0"),
"\x0B": RegExp("\\\x0B"),
"\u2028": RegExp("\\\u2028"),
"\u2029": RegExp("\\\u2029"),
};
for (var c in o)
console.log(o[c].test("\\"), o[c].test(c));
}
expect_exact: [
"var o = {",
' "\\n": /\\n/,',
' "\\r": /\\r/,',
' "\\t": /\t/,',
' "\\b": /\b/,',
' "\\f": /\f/,',
' "\\0": /\0/,',
' "\\v": /\v/,',
' "\\u2028": /\\u2028/,',
' "\\u2029": /\\u2029/',
"};",
"",
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
]
expect_stdout: [
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
]
}
issue_3434_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
RegExp("\n");
RegExp("\r");
RegExp("\\n");
RegExp("\\\n");
RegExp("\\\\n");
RegExp("\\\\\n");
RegExp("\\\\\\n");
RegExp("\\\\\\\n");
RegExp("\u2028");
RegExp("\u2029");
RegExp("\n\r\u2028\u2029");
RegExp("\\\nfo\n[\n]o\\bbb");
}
expect: {
/\n/;
/\r/;
/\n/;
/\n/;
/\\n/;
/\\\n/;
/\\\n/;
/\\\n/;
/\u2028/;
/\u2029/;
/\n\r\u2028\u2029/;
/\nfo\n[\n]o\bbb/;
}
}

View File

@@ -12,3 +12,71 @@ console_log: {
"% %s",
]
}
typeof_arguments: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var arguments;
console.log((typeof arguments).length);
}
expect: {
var arguments;
console.log((typeof arguments).length);
}
expect_stdout: "6"
}
typeof_arguments_assigned: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var arguments = void 0;
console.log((typeof arguments).length);
}
expect: {
console.log("undefined".length);
}
expect_stdout: "9"
}
toplevel_Infinity_NaN_undefined: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var Infinity = "foo";
var NaN = 42;
var undefined = null;
console.log(Infinity, NaN, undefined);
}
expect: {
console.log("foo", 42, null);
}
expect_stdout: "foo 42 null"
}
log_global: {
input: {
console.log(function() {
return this;
}());
}
expect: {
console.log(function() {
return this;
}());
}
expect_stdout: "[object global]"
}

View File

@@ -0,0 +1,8 @@
// Generated by CoffeeScript 2.4.1
(function() {
console.log('hello');
}).call(this);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4uY29mZmVlIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTtFQUFBLE9BQU8sQ0FBQyxHQUFSLENBQVksT0FBWjtBQUFBIiwic291cmNlc0NvbnRlbnQiOlsiY29uc29sZS5sb2cgJ2hlbGxvJ1xuIl19
//# sourceURL=/Users/mohamed/Downloads/main.coffee

View File

@@ -181,6 +181,18 @@ describe("sourcemaps", function() {
if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8"));
});
it("Should work in presence of unrecognised annotations", function() {
var result = UglifyJS.minify(read("./test/input/issue-3441/input.js"), {
compress: false,
mangle: false,
sourceMap: {
content: "inline",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, '(function(){console.log("hello")}).call(this);');
assert.strictEqual(result.map, '{"version":3,"sources":["main.coffee"],"names":["console","log"],"mappings":"CAAA,WAAAA,QAAQC,IAAI"}');
});
});
describe("sourceMapInline", function() {

View File

@@ -1,29 +1,7 @@
var semver = require("semver");
var vm = require("vm");
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
}
}
return arg;
}
function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
var func_toString = new vm.Script([
var setupContext = new vm.Script([
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
"});",
@@ -44,40 +22,51 @@ var func_toString = new vm.Script([
' return "function(){}";',
" };",
"}();",
"this;",
]).join("\n"));
function createContext() {
var ctx = vm.createContext(Object.defineProperty({}, "console", { value: { log: log } }));
func_toString.runInContext(ctx);
var global = setupContext.runInContext(ctx);
return ctx;
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
}
}
return arg;
}
function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
}
var context;
exports.run_code = function(code, reuse) {
exports.run_code = function(code, toplevel) {
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
if (!reuse || !context) context = createContext();
vm.runInContext([
"!function() {",
code,
"}();",
].join("\n"), context, { timeout: 5000 });
vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: 5000 });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
if (!reuse || code.indexOf(".prototype") >= 0) {
context = null;
} else {
vm.runInContext(Object.keys(context).map(function(name) {
return "delete " + name;
}).join("\n"), context);
}
}
};

View File

@@ -15,7 +15,7 @@ if (process.argv[2] == "run") {
var branch = process.argv[3] || "v" + require("../package.json").version;
var repository = encodeURIComponent(process.argv[4] || "mishoo/UglifyJS2");
var concurrency = process.argv[5] || 1;
var platform = process.argv[6] || "node/latest";
var platform = process.argv[6] || "latest";
(function request() {
setTimeout(request, (period + wait) / concurrency);
var options = url.parse("https://api.travis-ci.org/repo/" + repository + "/requests");
@@ -37,7 +37,7 @@ if (process.argv[2] == "run") {
branch: branch,
config: {
cache: false,
env: "NODEJS_VER=" + platform,
env: "NODE=" + platform,
script: "node test/travis-ufuzz run"
}
}

View File

@@ -960,16 +960,16 @@ if (require.main !== module) {
}
function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg);
if (typeof msg != "undefined") process.stdout.write(typeof msg == "string" ? msg : msg.stack);
process.stdout.write("\n");
}
function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg);
if (typeof msg != "undefined") process.stderr.write(typeof msg == "string" ? msg : msg.stack);
process.stderr.write("\n");
}
function try_beautify(code, result, printfn) {
function try_beautify(code, toplevel, result, printfn) {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
@@ -980,8 +980,8 @@ function try_beautify(code, result, printfn) {
});
if (beautified.error) {
printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
printfn(beautified.error);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code, toplevel), result)) {
printfn("// (beautified)");
printfn(beautified.code);
return;
@@ -1007,9 +1007,9 @@ function log_suspects(minify_options, component) {
var result = UglifyJS.minify(original_code, m);
if (result.error) {
errorln("Error testing options." + component + "." + name);
errorln(result.error.stack);
errorln(result.error);
} else {
var r = sandbox.run_code(result.code);
var r = sandbox.run_code(result.code, m.toplevel);
return sandbox.same_stdout(original_result, r);
}
}
@@ -1029,9 +1029,9 @@ function log_rename(options) {
var result = UglifyJS.minify(original_code, m);
if (result.error) {
errorln("Error testing options.rename");
errorln(result.error.stack);
errorln(result.error);
} else {
var r = sandbox.run_code(result.code);
var r = sandbox.run_code(result.code, m.toplevel);
if (sandbox.same_stdout(original_result, r)) {
errorln("Suspicious options:");
errorln(" rename");
@@ -1045,31 +1045,31 @@ function log(options) {
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
try_beautify(original_code, original_result, errorln);
try_beautify(original_code, false, original_result, errorln);
errorln();
errorln();
errorln("//-------------------------------------------------------------");
options = JSON.parse(options);
if (typeof uglify_code == "string") {
errorln("// uglified code");
try_beautify(uglify_code, uglify_result, errorln);
try_beautify(uglify_code, options.toplevel, uglify_result, errorln);
errorln();
errorln();
errorln("original result:");
errorln(typeof original_result == "string" ? original_result : original_result.stack);
errorln(original_result);
errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
errorln(uglify_result);
} else {
errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack);
if (typeof original_result != "string") {
errorln(uglify_code);
if (errored) {
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result.stack);
errorln(original_result);
}
}
errorln("minify(options):");
options = JSON.parse(options);
errorln(JSON.stringify(options, null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
@@ -1084,34 +1084,38 @@ var fallback_options = [ JSON.stringify({
mangle: false
}) ];
var minify_options = require("./ufuzz.json").map(JSON.stringify);
var original_code, original_result;
var original_code, original_result, errored;
var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
original_code = createTopLevelCode();
original_result = sandbox.run_code(original_code);
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
uglify_code = UglifyJS.minify(original_code, JSON.parse(options));
var orig_result = [ sandbox.run_code(original_code) ];
errored = typeof orig_result[0] != "string";
if (!errored) orig_result.push(sandbox.run_code(original_code, true));
(errored ? fallback_options : minify_options).forEach(function(options) {
var o = JSON.parse(options);
uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[o.toplevel ? 1 : 0];
if (!uglify_code.error) {
uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code);
uglify_result = sandbox.run_code(uglify_code, o.toplevel);
ok = sandbox.same_stdout(original_result, uglify_result);
} else {
uglify_code = uglify_code.error;
if (typeof original_result != "string") {
if (errored) {
ok = uglify_code.name == original_result.name;
}
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") {
else if (errored) {
println("//=============================================================");
println("// original code");
try_beautify(original_code, original_result, println);
try_beautify(original_code, o.toplevel, original_result, println);
println();
println();
println("original result:");
println(original_result.stack);
println(original_result);
println();
}
if (!ok && isFinite(num_iterations)) {

View File

@@ -2993,6 +2993,7 @@
"caption",
"caption-side",
"captionSide",
"capture",
"captureEvents",
"captureStackTrace",
"captureStream",
@@ -4957,6 +4958,7 @@
"oncandidatewindowupdate",
"oncanplay",
"oncanplaythrough",
"once",
"oncellchange",
"onchange",
"onchargingchange",
@@ -5343,6 +5345,7 @@
"parseInt",
"part",
"participants",
"passive",
"password",
"pasteHTML",
"path",

View File

@@ -1,21 +1,19 @@
var fs = require("fs");
exports.FILES = [
"../lib/utils.js",
"../lib/ast.js",
"../lib/parse.js",
"../lib/transform.js",
"../lib/scope.js",
"../lib/output.js",
"../lib/compress.js",
"../lib/sourcemap.js",
"../lib/mozilla-ast.js",
"../lib/propmangle.js",
"../lib/minify.js",
"./exports.js",
].map(function(file) {
return require.resolve(file);
});
require.resolve("../lib/utils.js"),
require.resolve("../lib/ast.js"),
require.resolve("../lib/parse.js"),
require.resolve("../lib/transform.js"),
require.resolve("../lib/scope.js"),
require.resolve("../lib/output.js"),
require.resolve("../lib/compress.js"),
require.resolve("../lib/sourcemap.js"),
require.resolve("../lib/mozilla-ast.js"),
require.resolve("../lib/propmangle.js"),
require.resolve("../lib/minify.js"),
require.resolve("./exports.js"),
];
new Function("MOZ_SourceMap", "exports", function() {
var code = exports.FILES.map(function(file) {
@@ -48,7 +46,9 @@ function describe_ast() {
if (ctor.SUBCLASSES.length > 0) {
out.space();
out.with_block(function() {
ctor.SUBCLASSES.forEach(function(ctor, i) {
ctor.SUBCLASSES.sort(function(a, b) {
return a.TYPE < b.TYPE ? -1 : 1;
}).forEach(function(ctor, i) {
out.indent();
doitem(ctor);
out.newline();