Compare commits

..

22 Commits

Author SHA1 Message Date
Alex Lam S.L
49ea629f3f v3.9.4 2020-05-27 07:50:18 +01:00
Alex Lam S.L
13c72a986c fix corner case in infinite recursion detection (#3926) 2020-05-27 02:03:38 +08:00
Alex Lam S.L
08af3eae44 perform ufuzz on Pull Requests (#3925) 2020-05-25 22:55:14 +08:00
Alex Lam S.L
27bdcbbd83 fix corner cases in infinite recursion detection (#3924) 2020-05-25 22:54:57 +08:00
Alex Lam S.L
2c4d7d66ef fix corner case in reduce_vars (#3923)
fixes #3922
2020-05-24 07:38:40 +08:00
Alex Lam S.L
d1cc5270a3 fix corner case in evaluate (#3921)
fixes #3920
2020-05-22 11:38:09 +08:00
Alex Lam S.L
75c5b6029b fix corner case in ie8 & reduce_vars (#3919)
fixes #3918
2020-05-22 09:56:35 +08:00
Alex Lam S.L
fa14a9cfcd fix corner case in join_vars (#3917)
fixes #3916
2020-05-22 05:26:46 +08:00
Alex Lam S.L
aeb9ea5ac2 fix corner case in inline (#3915)
fixes #3911
2020-05-21 22:05:31 +08:00
Alex Lam S.L
798841be82 improve job resilience (#3913) 2020-05-21 04:50:42 +08:00
Alex Lam S.L
cc6eb4b15f improve ufuzz (#3912)
- preserve test case if `beautify` suppresses bug
- determine suspicious options even if `minify()` fails
2020-05-21 04:00:38 +08:00
Alex Lam S.L
14eee81dc6 update header comment for --reduce-test (#3910) 2020-05-19 11:35:33 +08:00
Alex Lam S.L
55ebb27878 fix corner case in collapse_vars (#3909)
fixes #3908
2020-05-19 11:34:50 +08:00
Alex Lam S.L
87046410ef enhance dead_code (#3907) 2020-05-19 03:53:08 +08:00
Alex Lam S.L
f9b3198714 fix corner case in evaluate (#3906)
fixes #3905
2020-05-18 08:41:10 +08:00
Alex Lam S.L
48b62393a4 fix corner case in evaluate (#3904)
fixes #3903
2020-05-17 22:25:13 +08:00
Alex Lam S.L
a00f8dade7 fix suspicious toplevel detection (#3902) 2020-05-17 21:35:17 +08:00
Alex Lam S.L
9daa2fb6f5 benchmark without validation by default (#3901) 2020-05-15 23:57:50 +08:00
Alex Lam S.L
8d81d264f4 fix corner case in functions (#3900)
fixes #3899
2020-05-15 18:03:56 +08:00
Alex Lam S.L
5ef7060098 fix corner case in collapse_vars (#3898)
fixes #3897
2020-05-15 01:09:54 +08:00
Alex Lam S.L
938368ba21 enhance collapse_vars (#3896) 2020-05-14 07:52:42 +08:00
Alex Lam S.L
fe2f1965d6 fix corner case in reduce_vars (#3895)
fixes #3894
2020-05-13 23:44:54 +08:00
16 changed files with 498 additions and 72 deletions

View File

@@ -29,7 +29,7 @@ jobs:
git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
git clean -xdf
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version

View File

@@ -1,5 +1,6 @@
name: Fuzzing
on:
pull_request:
schedule:
- cron: "*/8 * * * *"
jobs:
@@ -18,7 +19,7 @@ jobs:
git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add 10 && nvs use 10'; do
cd ~/.nvs
git clean -xdf
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version

View File

@@ -738,8 +738,7 @@ merge(Compressor.prototype, {
var value = iife.args[i];
d.fixed = function() {
var j = fn.argnames.indexOf(arg);
if (j < 0) return value;
return iife.args[j] || make_node(AST_Undefined, iife);
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
};
tw.loop_ids[d.id] = tw.in_loop;
mark(tw, d, true);
@@ -1302,7 +1301,7 @@ merge(Compressor.prototype, {
value_def.replaced++;
return List.skip;
}
return get_rvalue(candidate);
return rvalue;
case 1:
if (!assign_used && node.body === candidate) {
hit = true;
@@ -1321,7 +1320,7 @@ merge(Compressor.prototype, {
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return get_rvalue(candidate).clone();
return rvalue.clone();
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
@@ -1345,13 +1344,14 @@ merge(Compressor.prototype, {
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, compressor);
var scan_rhs = foldable(get_rhs(candidate));
var scan_rhs = foldable(candidate);
if (!scan_lhs && !scan_rhs) continue;
var modify_toplevel = false;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
if (!side_effects) side_effects = value_has_side_effects(candidate);
var rvalue = get_rvalue(candidate);
if (!side_effects) side_effects = value_has_side_effects();
var replace_all = replace_all_symbols(candidate);
var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
return node.has_side_effects(compressor);
@@ -1422,6 +1422,7 @@ merge(Compressor.prototype, {
}
function should_stop(node, parent) {
if (node === rvalue) return true;
if (parent instanceof AST_For) return node !== parent.init;
if (node instanceof AST_Assign) {
return node.operator != "=" && lhs.equivalent_to(node.left);
@@ -1429,8 +1430,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Call) {
if (!(lhs instanceof AST_PropAccess)) return false;
if (!lhs.equivalent_to(node.expression)) return false;
var rhs = get_rvalue(candidate);
return !(rhs instanceof AST_Function && !rhs.contains_this());
return !(rvalue instanceof AST_Function && !rvalue.contains_this());
}
if (node instanceof AST_Debugger) return true;
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
@@ -1836,10 +1836,6 @@ merge(Compressor.prototype, {
}
}
function get_rhs(expr) {
return candidate instanceof AST_Assign && candidate.operator == "=" && candidate.right;
}
function invariant(expr) {
if (expr instanceof AST_Array) return false;
if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
@@ -1854,7 +1850,14 @@ merge(Compressor.prototype, {
}
function foldable(expr) {
if (!expr) return false;
var lhs_ids = Object.create(null);
var marker = new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
});
while (expr instanceof AST_Assign && expr.operator == "=") {
expr.left.walk(marker);
expr = expr.right;
}
if (expr instanceof AST_SymbolRef) {
var value = expr.evaluate(compressor);
if (value === expr) return rhs_exact_match;
@@ -1868,12 +1871,9 @@ merge(Compressor.prototype, {
if (!(lhs instanceof AST_SymbolRef)) return false;
if (!invariant(expr)) return false;
var circular;
var def = lhs.definition();
expr.walk(new TreeWalker(function(node) {
if (circular) return true;
if (node instanceof AST_SymbolRef && node.definition() === def) {
circular = true;
}
if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
}));
return !circular && rhs_exact_match;
@@ -1969,9 +1969,9 @@ merge(Compressor.prototype, {
|| candidate instanceof AST_Assign && candidate.operator != "="));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
function value_has_side_effects() {
if (candidate instanceof AST_Unary) return false;
return rvalue.has_side_effects(compressor);
}
function replace_all_symbols(expr) {
@@ -2507,7 +2507,7 @@ merge(Compressor.prototype, {
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
var diff = compressor.has_directive("use strict") ? function(node) {
var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
return node.key != prop && node.key.name != prop;
} : function(node) {
return node.key.name != prop;
@@ -3255,11 +3255,21 @@ merge(Compressor.prototype, {
&& unaryPrefix[this.operator];
}
});
function modified(sym) {
if (!(sym instanceof AST_SymbolRef)) return;
sym.definition().references.forEach(function(node) {
delete node._eval;
});
var scan_modified = new TreeWalker(function(node) {
if (node instanceof AST_Assign) modified(node.left);
if (node instanceof AST_Unary && unary_arithmetic[node.operator]) modified(node.expression);
});
function modified(node) {
if (node instanceof AST_Dot) {
modified(node.expression);
} else if (node instanceof AST_Sub) {
modified(node.expression);
node.property.walk(scan_modified);
} else if (node instanceof AST_SymbolRef) {
node.definition().references.forEach(function(ref) {
delete ref._eval;
});
}
}
def(AST_Statement, function() {
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
@@ -3279,12 +3289,11 @@ merge(Compressor.prototype, {
});
def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
if (!ignore_side_effects) return this;
var tail = this.tail_node();
this.walk(new TreeWalker(function(node) {
if (node === tail) return true;
if (node instanceof AST_Assign) modified(node.left);
if (node instanceof AST_Unary && unary_arithmetic[node.operator]) modified(node.expression);
}));
var exprs = this.expressions;
for (var i = 0, last = exprs.length - 1; i < last; i++) {
exprs[i].walk(scan_modified);
}
var tail = exprs[last];
var value = tail._eval(compressor, ignore_side_effects, cached, depth);
return value === tail ? this : value;
});
@@ -3565,8 +3574,10 @@ merge(Compressor.prototype, {
}
var args = eval_args(this.args);
if (!args && !ignore_side_effects) return this;
if (!stat.value) return;
if (args && !all(fn.argnames, function(sym, i) {
var val = stat.value;
if (!val) return;
var cached_args = [];
if (!args || all(fn.argnames, function(sym, i) {
var value = args[i];
var def = sym.definition();
if (def.orig[def.orig.length - 1] !== sym) return false;
@@ -3574,15 +3585,18 @@ merge(Compressor.prototype, {
node._eval = function() {
return value;
};
cached.push(node);
cached_args.push(node);
});
return true;
}) && !ignore_side_effects) return this;
fn.evaluating = true;
var val = stat.value._eval(compressor, ignore_side_effects, cached, depth);
delete fn.evaluating;
if (val === stat.value) return this;
return val;
}) || ignore_side_effects) {
fn.evaluating = true;
val = val._eval(compressor, ignore_side_effects, cached, depth);
delete fn.evaluating;
}
cached_args.forEach(function(node) {
delete node._eval;
});
return val === stat.value ? this : val;
} else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property;
if (key instanceof AST_Node) {
@@ -4358,6 +4372,7 @@ merge(Compressor.prototype, {
} else if (compressor.option("functions")
&& !compressor.option("ie8")
&& var_defs.length == 1
&& sym.assignments == 0
&& def.value === def.name.fixed_value()
&& def.value instanceof AST_Function
&& !(def.value.name && def.value.name.definition().assignments)
@@ -6227,9 +6242,9 @@ merge(Compressor.prototype, {
&& !fn.uses_arguments
&& !fn.pinned()
&& !(fn.name && fn instanceof AST_Function)
&& (value = can_flatten_body(stat))
&& (exp === fn
|| !recursive_ref(compressor, def = exp.definition()) && fn.is_constant_expression(exp.scope))
&& (value = can_flatten_body(stat))
&& !fn.contains_this()) {
if (can_substitute_directly()) {
var args = self.args.slice();
@@ -7508,6 +7523,8 @@ merge(Compressor.prototype, {
scope.inlined = true;
} while (scope = scope.parent_scope);
}
} else if (compressor.option("ie8") && fixed.name && def !== fixed.name.definition()) {
single_use = false;
}
if (single_use) fixed.parent_scope = self.scope;
} else if (!fixed || !fixed.is_constant_expression()) {
@@ -7566,6 +7583,12 @@ merge(Compressor.prototype, {
}));
} else {
value = fixed.optimize(compressor);
if (value === fixed) value = value.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_Scope) return node;
node = node.clone();
descend(node, this);
return node;
}));
}
def.replaced++;
return value;
@@ -7727,6 +7750,10 @@ merge(Compressor.prototype, {
if (compressor.option("dead_code")) {
if (self.left instanceof AST_PropAccess) {
if (self.operator == "=") {
if (self.left.equivalent_to(self.right)
&& (self.left instanceof AST_Dot || !self.left.property.has_side_effects(compressor))) {
return self.right;
}
var exp = self.left.expression;
if (exp instanceof AST_Lambda
|| !compressor.has_directive("use strict")
@@ -7739,6 +7766,7 @@ merge(Compressor.prototype, {
}
}
} else if (self.left instanceof AST_SymbolRef) {
if (self.operator == "=" && self.left.equivalent_to(self.right)) return self.right;
if (self.left.is_immutable()) return strip_assignment();
var def = self.left.definition();
var scope = def.scope.resolve();

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.9.3",
"version": "3.9.4",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -10,7 +10,7 @@ var zlib = require("zlib");
var args = process.argv.slice(2);
if (!args.length) args.push("-mc");
args.unshift("bin/uglifyjs");
args.push("--validate", "--timings");
args.push("--timings");
var urls = [
"https://code.jquery.com/jquery-3.4.1.js",
"https://code.angularjs.org/1.7.8/angular.js",

View File

@@ -8015,3 +8015,96 @@ issue_3891: {
}
expect_stdout: "function"
}
issue_3894: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a, c;
(function(b) {
a = c = 0,
log(b);
})(-0);
log(a);
log(c);
}
expect: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a, c;
void log(-(a = c = 0));
log(a);
log(c);
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
}
issue_3897: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
(function() {
function f(b) {
b = a = 1 + a;
a = 1 + a;
console.log(b);
}
f();
})();
console.log(a);
}
expect: {
var a = 0;
(function() {
function f(b) {
b = a = 1 + a;
a = 1 + a;
console.log(b);
}
f();
})();
console.log(a);
}
expect_stdout: [
"1",
"2",
]
}
issue_3908: {
options = {
collapse_vars: true,
conditionals: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
if (console) {
var o = {
p: !1
}, a = o;
}
console.log("PASS");
}
expect: {
console && 0;
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -1168,3 +1168,28 @@ redundant_assignments: {
}
expect_stdout: "PASS PASS"
}
self_assignments: {
options = {
dead_code: true,
}
input: {
var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" };
a = a;
l[0] = l[0];
l[b] = l[b];
l[b++] = l[b++];
o.p = o.p;
console.log(a, b, l[0], o.p);
}
expect: {
var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" };
a;
l[0];
l[b];
l[b++] = l[b++];
o.p;
console.log(a, b, l[0], o.p);
}
expect_stdout: "PASS 2 PASS PASS"
}

View File

@@ -2527,7 +2527,40 @@ issue_3802_2: {
}
expect: {
0;
function a() {};
var a = function() {};
console.log(typeof a);
}
expect_stdout: "function"
}
issue_3899: {
options = {
assignments: true,
evaluate: true,
functions: true,
inline: true,
join_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a = a + 1;
var a = function f(b) {
return function() {
return b;
};
}(2);
console.log(typeof a);
}
expect: {
++a;
var a = function() {
return 2;
};
console.log(typeof a);
}
expect_stdout: "function"

View File

@@ -2418,3 +2418,70 @@ issue_3887: {
}
expect_stdout: "PASS"
}
issue_3903: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f(b, c) {
return console, c;
}
var d = f(f(), a = a);
console.log(d);
}
expect: {
var a = "PASS";
function f(b, c) {
return console, c;
}
var d = f(f(), a = a);
console.log(d);
}
expect_stdout: "PASS"
}
issue_3905: {
options = {
evaluate: true,
passes: 2,
unused: true,
}
input: {
(function(a, a) {
return console.log(a = 0), a && console.log("FAIL");
})("foo", 42);
}
expect: {
(function(a, a) {
return console.log(a = 0), a && console.log("FAIL");
})("foo", 42);
}
expect_stdout: "0"
}
issue_3920: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function(b) {
return (b[b = 0] = 0) >= (b ? 0 : 1);
}("foo");
console.log(a);
}
expect: {
var a = function(b) {
return (b[b = 0] = 0) >= (b ? 0 : 1);
}("foo");
console.log(a);
}
expect_stdout: "false"
}

View File

@@ -4680,3 +4680,28 @@ issue_3852: {
}
expect_stdout: "PASS"
}
issue_3911: {
options = {
collapse_vars: true,
conditionals: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return function() {
if (a) (a++, b += a);
f();
};
}
var a = f, b;
console.log("PASS");
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -2489,3 +2489,36 @@ issue_3889: {
}
expect_stdout: "undefined"
}
issue_3918: {
options = {
conditionals: true,
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
}
input: {
if (console.log("PASS")) {
var a = function f() {
f.p;
try {
console.log("FAIL");
} catch (e) {}
}, b = a;
}
}
expect: {
var a;
console.log("PASS") && (a = function f() {
f.p;
try {
console.log("FAIL");
} catch (o) {}
}, a);
}
expect_stdout: "PASS"
}

View File

@@ -1023,3 +1023,35 @@ issue_3856: {
}
expect_stdout: "undefined"
}
issue_3916: {
options = {
join_vars: true,
}
input: {
var o = {};
o.p = "PASS";
o.__proto__ = 42;
o.q = "FAIL";
o.__proto__ = {
p: "FAIL",
q: "PASS",
};
o.__proto__ = "foo";
console.log(typeof o.__proto__, o.p, delete o.q, o.q);
}
expect: {
var o = {
p: "PASS",
__proto__: 42,
q: "FAIL",
};
o.__proto__ = {
p: "FAIL",
q: "PASS",
};
o.__proto__ = "foo";
console.log(typeof o.__proto__, o.p, delete o.q, o.q);
}
expect_stdout: "object PASS true PASS"
}

View File

@@ -7068,3 +7068,57 @@ issue_3880: {
}
expect_stdout: "PASS"
}
issue_3894: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a;
(function(b) {
a = 0,
log(b);
})(-0);
}
expect: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a;
void log(-(a = 0));
}
expect_stdout: "PASS"
}
issue_3922: {
options = {
evaluate: true,
keep_fargs: "strict",
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function(a) {
var b;
b && b[c];
a |= this;
console.log("PASS");
var c = a.undefined;
})();
}
expect: {
(function() {
0;
console.log("PASS");
})();
}
expect_stdout: "PASS"
}

View File

@@ -4,19 +4,17 @@ var List = U.List;
var os = require("os");
var sandbox = require("./sandbox");
// Reduce a ufuzz-style `console.log` based test case by iteratively replacing
// AST nodes with various permutations. Each AST_Statement in the tree is also
// speculatively dropped to determine whether it is needed. If the altered
// tree and the last known good tree produce the same non-nil error-free output
// after being run, then the permutation survives to the next generation and
// is the basis for subsequent iterations. The test case is reduced as a
// consequence of complex expressions being replaced with simpler ones.
// This function assumes that the testcase will not result in a parse or
// runtime Error. Note that a reduced test case will have different runtime
// output - it is not functionally equivalent to the original. The only criteria
// is that once the generated reduced test case is run without minification, it
// will produce different output from the code minified with `minify_options`.
// Returns a `minify` result object with an additonal boolean property `reduced`.
// Reduce a test case by iteratively replacing AST nodes with various
// permutations. Each AST_Statement in the tree is also speculatively dropped
// to determine whether it is needed. If the altered tree and the last known
// good tree produce the same output after being run, then the permutation
// survives to the next generation and is the basis for subsequent iterations.
// The test case is reduced as a consequence of complex expressions being
// replaced with simpler ones. Note that a reduced test case will have
// different runtime output - it is not functionally equivalent to the
// original. The only criteria is that once the generated reduced test case is
// run without minification, it will produce different output from the code
// minified with `minify_options`. Returns a `minify` result object.
Error.stackTraceLimit = Infinity;
module.exports = function reduce_test(testcase, minify_options, reduce_options) {

View File

@@ -9,6 +9,6 @@ require("./run")([
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
args.unshift("test/benchmark.js", "--validate");
return args;
}));

View File

@@ -1018,6 +1018,7 @@ function log_suspects(minify_options, component) {
if (!options) return;
if (typeof options != "object") options = {};
var defs = default_options[component];
var toplevel = sandbox.has_toplevel(minify_options);
var suspects = Object.keys(defs).filter(function(name) {
var flip = name == "keep_fargs";
if (flip !== (name in options ? options : defs)[name]) {
@@ -1025,12 +1026,15 @@ function log_suspects(minify_options, component) {
var o = JSON.parse(JSON.stringify(options));
o[name] = flip;
m[component] = o;
m.validate = true;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
if (typeof uglify_code != "string") {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component + "." + name);
errorln(result.error);
} else {
var r = sandbox.run_code(result.code, sandbox.has_toplevel(m));
var r = sandbox.run_code(result.code, toplevel);
return !sandbox.same_stdout(uglify_result, r);
}
}
@@ -1044,18 +1048,21 @@ function log_suspects(minify_options, component) {
}
}
function log_suspects_global(options) {
function log_suspects_global(options, toplevel) {
var suspects = Object.keys(default_options).filter(function(component) {
return typeof default_options[component] != "object";
}).filter(function(component) {
var m = JSON.parse(options);
m[component] = false;
m.validate = true;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
if (typeof uglify_code != "string") {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component);
errorln(result.error);
} else {
var r = sandbox.run_code(result.code, sandbox.has_toplevel(m));
var r = sandbox.run_code(result.code, toplevel);
return !sandbox.same_stdout(uglify_result, r);
}
});
@@ -1074,7 +1081,37 @@ function log(options) {
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
try_beautify(original_code, toplevel, original_result, errorln);
var beautified = UglifyJS.minify(original_code, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
},
});
if (beautified.error) {
errorln("// !!! beautify failed !!!");
errorln(beautified.error);
errorln("//");
errorln(original_code);
} else {
var uglified = UglifyJS.minify(beautified.code, JSON.parse(options));
var expected, actual;
if (typeof uglify_code != "string" || uglified.error) {
expected = uglify_code;
actual = uglified.error;
} else {
expected = uglify_result;
actual = sandbox.run_code(uglified.code, toplevel);
}
if (sandbox.same_stdout(expected, actual)) {
errorln("// (beautified)");
errorln(beautified.code);
} else {
errorln("//");
errorln(original_code);
}
}
errorln();
errorln();
errorln("//-------------------------------------------------------------");
@@ -1112,12 +1149,12 @@ function log(options) {
errorln("minify(options):");
errorln(JSON.stringify(JSON.parse(options), null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
if (!ok) {
Object.keys(default_options).filter(function(component) {
var defs = default_options[component];
return defs && typeof defs == "object";
}).forEach(log_suspects.bind(null, JSON.parse(options)));
log_suspects_global(options);
log_suspects_global(options, toplevel);
errorln("!!!!!! Failed... round " + round);
}
}
@@ -1140,19 +1177,19 @@ function skip_infinite_recursion(orig, toplevel) {
var code = orig;
var tries = [];
var offset = 0;
var re = /(?:(?:^|[\s{};])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g;
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g;
var match;
while (match = re.exec(code)) {
if (/}\s*finally\s*$/.test(match[0])) {
tries.shift();
continue;
}
if (tries.length && tries[0].catch) tries.shift();
var index = match.index + match[0].length + 1;
if (/(?:^|[\s{};])try\s*$/.test(match[0])) {
if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset });
continue;
}
while (tries.length && tries[0].catch) tries.shift();
tries[0].catch = index;
var insert = "throw " + match[1] + ".ufuzz_skip || (" + match[1] + ".ufuzz_skip = " + tries[0].try + "), " + match[1] + ";";
var new_code = code.slice(0, index) + insert + code.slice(index);