@@ -246,4 +246,52 @@ describe("test/reduce.js", function() {
|
|||||||
"// }",
|
"// }",
|
||||||
].join("\n"));
|
].join("\n"));
|
||||||
});
|
});
|
||||||
|
it("Should reduce test case which differs only in Error.message", function() {
|
||||||
|
var code = [
|
||||||
|
"var a=0;",
|
||||||
|
"try{",
|
||||||
|
"null[function(){}]",
|
||||||
|
"}catch(e){",
|
||||||
|
"for(var i in e.toString())a++",
|
||||||
|
"}",
|
||||||
|
"console.log(a);",
|
||||||
|
].join("");
|
||||||
|
var result = reduce_test(code, {
|
||||||
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
|
output: {
|
||||||
|
beautify: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code.replace(/function \(/g, "function("), (semver.satisfies(process.version, "<=0.10") ? [
|
||||||
|
"// Can't reproduce test failure",
|
||||||
|
"// minify options: {",
|
||||||
|
'// "compress": false,',
|
||||||
|
'// "mangle": false,',
|
||||||
|
'// "output": {',
|
||||||
|
'// "beautify": true',
|
||||||
|
"// }",
|
||||||
|
"// }",
|
||||||
|
] : [
|
||||||
|
[
|
||||||
|
"try{",
|
||||||
|
"null[function(){}]",
|
||||||
|
"}catch(e){",
|
||||||
|
"console.log(e)",
|
||||||
|
"}",
|
||||||
|
].join(""),
|
||||||
|
"// output: TypeError: Cannot read property 'function(){}' of null",
|
||||||
|
"// ",
|
||||||
|
"// minify: TypeError: Cannot read property 'function() {}' of null",
|
||||||
|
"// ",
|
||||||
|
"// options: {",
|
||||||
|
'// "compress": false,',
|
||||||
|
'// "mangle": false,',
|
||||||
|
'// "output": {',
|
||||||
|
'// "beautify": true',
|
||||||
|
"// }",
|
||||||
|
"// }",
|
||||||
|
]).join("\n"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
114
test/reduce.js
114
test/reduce.js
@@ -125,40 +125,13 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node instanceof U.AST_Binary) {
|
else if (node instanceof U.AST_Binary) {
|
||||||
CHANGED = true;
|
|
||||||
var permute = ((node.start._permute += step) * steps | 0) % 4;
|
var permute = ((node.start._permute += step) * steps | 0) % 4;
|
||||||
var expr = [
|
var expr = [
|
||||||
node.left,
|
node.left,
|
||||||
node.right,
|
node.right,
|
||||||
][ permute & 1 ];
|
][ permute & 1 ];
|
||||||
if (permute < 2) return expr;
|
|
||||||
// wrap with console.log()
|
|
||||||
return new U.AST_Call({
|
|
||||||
expression: new U.AST_Dot({
|
|
||||||
expression: new U.AST_SymbolRef({
|
|
||||||
name: "console",
|
|
||||||
start: {},
|
|
||||||
}),
|
|
||||||
property: "log",
|
|
||||||
start: {},
|
|
||||||
}),
|
|
||||||
args: [ expr ],
|
|
||||||
start: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (node instanceof U.AST_Catch || node instanceof U.AST_Finally) {
|
|
||||||
// drop catch or finally block
|
|
||||||
node.start._permute++;
|
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return null;
|
return permute < 2 ? expr : wrap_with_console_log(expr);
|
||||||
}
|
|
||||||
else if (node instanceof U.AST_Conditional) {
|
|
||||||
CHANGED = true;
|
|
||||||
return [
|
|
||||||
node.condition,
|
|
||||||
node.consequent,
|
|
||||||
node.alternative,
|
|
||||||
][ ((node.start._permute += step) * steps | 0) % 3 ];
|
|
||||||
}
|
}
|
||||||
else if (node instanceof U.AST_BlockStatement) {
|
else if (node instanceof U.AST_BlockStatement) {
|
||||||
if (in_list) {
|
if (in_list) {
|
||||||
@@ -193,12 +166,26 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
return to_sequence(seq);
|
return to_sequence(seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (node instanceof U.AST_Catch) {
|
||||||
|
// drop catch block
|
||||||
|
node.start._permute++;
|
||||||
|
CHANGED = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if (node instanceof U.AST_Conditional) {
|
||||||
|
CHANGED = true;
|
||||||
|
return [
|
||||||
|
node.condition,
|
||||||
|
node.consequent,
|
||||||
|
node.alternative,
|
||||||
|
][ ((node.start._permute += step) * steps | 0) % 3 ];
|
||||||
|
}
|
||||||
else if (node instanceof U.AST_Defun) {
|
else if (node instanceof U.AST_Defun) {
|
||||||
switch (((node.start._permute += step) * steps | 0) % 2) {
|
switch (((node.start._permute += step) * steps | 0) % 2) {
|
||||||
case 0:
|
case 0:
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return List.skip;
|
return List.skip;
|
||||||
case 1:
|
default:
|
||||||
if (!has_exit(node)) {
|
if (!has_exit(node)) {
|
||||||
// hoist function declaration body
|
// hoist function declaration body
|
||||||
var body = node.body;
|
var body = node.body;
|
||||||
@@ -230,15 +217,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
return to_statement(expr);
|
return to_statement(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node instanceof U.AST_PropAccess) {
|
else if (node instanceof U.AST_Finally) {
|
||||||
var expr = [
|
// drop finally block
|
||||||
node.expression,
|
node.start._permute++;
|
||||||
node.property instanceof U.AST_Node && node.property,
|
CHANGED = true;
|
||||||
][ node.start._permute++ % 2 ];
|
return null;
|
||||||
if (expr) {
|
|
||||||
CHANGED = true;
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (node instanceof U.AST_For) {
|
else if (node instanceof U.AST_For) {
|
||||||
var expr = [
|
var expr = [
|
||||||
@@ -287,6 +270,16 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (node instanceof U.AST_PropAccess) {
|
||||||
|
var expr = [
|
||||||
|
node.expression,
|
||||||
|
node.property instanceof U.AST_Node && node.property,
|
||||||
|
][ node.start._permute++ % 2 ];
|
||||||
|
if (expr) {
|
||||||
|
CHANGED = true;
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (node instanceof U.AST_SimpleStatement) {
|
else if (node instanceof U.AST_SimpleStatement) {
|
||||||
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
|
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
|
||||||
// hoist simple statement IIFE function expression body
|
// hoist simple statement IIFE function expression body
|
||||||
@@ -414,8 +407,36 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var diff_error_message;
|
||||||
for (var pass = 1; pass <= 3; ++pass) {
|
for (var pass = 1; pass <= 3; ++pass) {
|
||||||
var testcase_ast = U.parse(testcase);
|
var testcase_ast = U.parse(testcase);
|
||||||
|
if (diff_error_message === testcase) {
|
||||||
|
// only difference detected is in error message, so expose that and try again
|
||||||
|
testcase_ast.transform(new U.TreeTransformer(function(node, descend) {
|
||||||
|
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
|
||||||
|
return new U.AST_Sequence({
|
||||||
|
expressions: node.args,
|
||||||
|
start: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (node instanceof U.AST_Catch) {
|
||||||
|
descend(node, this);
|
||||||
|
node.body.unshift(new U.AST_SimpleStatement({
|
||||||
|
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
|
||||||
|
start: {},
|
||||||
|
}));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
var code = testcase_ast.print_to_string();
|
||||||
|
if (diff = producesDifferentResultWhenMinified(result_cache, code, minify_options, max_timeout)) {
|
||||||
|
testcase = code;
|
||||||
|
differs = diff;
|
||||||
|
} else {
|
||||||
|
testcase_ast = U.parse(testcase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff_error_message = null;
|
||||||
testcase_ast.walk(new U.TreeWalker(function(node) {
|
testcase_ast.walk(new U.TreeWalker(function(node) {
|
||||||
// unshare start props to retain visit data between iterations
|
// unshare start props to retain visit data between iterations
|
||||||
node.start = JSON.parse(JSON.stringify(node.start));
|
node.start = JSON.parse(JSON.stringify(node.start));
|
||||||
@@ -457,6 +478,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
&& is_error(diff.minified_result)
|
&& is_error(diff.minified_result)
|
||||||
&& diff.unminified_result.name == diff.minified_result.name) {
|
&& diff.unminified_result.name == diff.minified_result.name) {
|
||||||
// ignore difference in error messages caused by minification
|
// ignore difference in error messages caused by minification
|
||||||
|
diff_error_message = testcase;
|
||||||
} else {
|
} else {
|
||||||
// latest permutation is valid, so use it as the basis of new changes
|
// latest permutation is valid, so use it as the basis of new changes
|
||||||
testcase_ast = code_ast;
|
testcase_ast = code_ast;
|
||||||
@@ -592,6 +614,22 @@ function to_statement(node) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrap_with_console_log(node) {
|
||||||
|
// wrap with console.log()
|
||||||
|
return new U.AST_Call({
|
||||||
|
expression: new U.AST_Dot({
|
||||||
|
expression: new U.AST_SymbolRef({
|
||||||
|
name: "console",
|
||||||
|
start: {},
|
||||||
|
}),
|
||||||
|
property: "log",
|
||||||
|
start: {},
|
||||||
|
}),
|
||||||
|
args: [ node ],
|
||||||
|
start: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function run_code(result_cache, code, toplevel, timeout) {
|
function run_code(result_cache, code, toplevel, timeout) {
|
||||||
var key = crypto.createHash("sha1").update(code).digest("base64");
|
var key = crypto.createHash("sha1").update(code).digest("base64");
|
||||||
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout));
|
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout));
|
||||||
|
|||||||
Reference in New Issue
Block a user