Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix & improve coverage of
estree
(#1935)
- fix `estree` conversion of getter/setter - fix non-directive literal in `to_mozilla_ast()` - revamp `test/mozilla-ast.js` - reuse `test/ufuzz.js` for code generation - use `acorn.parse()` for creating `estree` - extend `test/ufuzz.js` for `acorn` workaround - catch variable redefinition - non-trivial literal as directive - adjust options for tolerance Miscellaneous - optional semi-colon when parsing directives fixes #1914 closes #1915
- Loading branch information
Showing
7 changed files
with
191 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,87 @@ | ||
// Testing UglifyJS <-> SpiderMonkey AST conversion | ||
// through generative testing. | ||
|
||
var UglifyJS = require(".."), | ||
escodegen = require("escodegen"), | ||
esfuzz = require("esfuzz"), | ||
estraverse = require("estraverse"), | ||
prefix = "\r "; | ||
|
||
// Normalizes input AST for UglifyJS in order to get correct comparison. | ||
|
||
function normalizeInput(ast) { | ||
return estraverse.replace(ast, { | ||
enter: function(node, parent) { | ||
switch (node.type) { | ||
// Internally mark all the properties with semi-standard type "Property". | ||
case "ObjectExpression": | ||
node.properties.forEach(function (property) { | ||
property.type = "Property"; | ||
}); | ||
break; | ||
|
||
// Since UglifyJS doesn"t recognize different types of property keys, | ||
// decision on SpiderMonkey node type is based on check whether key | ||
// can be valid identifier or not - so we do in input AST. | ||
case "Property": | ||
var key = node.key; | ||
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) { | ||
node.key = { | ||
type: "Identifier", | ||
name: key.value | ||
}; | ||
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) { | ||
node.key = { | ||
type: "Literal", | ||
value: key.name | ||
}; | ||
} | ||
break; | ||
|
||
// UglifyJS internally flattens all the expression sequences - either | ||
// to one element (if sequence contains only one element) or flat list. | ||
case "SequenceExpression": | ||
node.expressions = node.expressions.reduce(function flatten(list, expr) { | ||
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]); | ||
}, []); | ||
if (node.expressions.length === 1) { | ||
return node.expressions[0]; | ||
} | ||
break; | ||
"use strict"; | ||
|
||
var acorn = require("acorn"); | ||
var ufuzz = require("./ufuzz"); | ||
var UglifyJS = require(".."); | ||
|
||
function try_beautify(code) { | ||
var beautified; | ||
try { | ||
beautified = UglifyJS.minify(code, { | ||
fromString: true, | ||
compress: false, | ||
mangle: false, | ||
output: { | ||
beautify: true, | ||
bracketize: true | ||
} | ||
} | ||
}); | ||
}); | ||
} catch (ex) { | ||
beautified = { error: ex }; | ||
} | ||
if (beautified.error) { | ||
console.log("// !!! beautify failed !!!"); | ||
console.log(beautified.error.stack); | ||
console.log(code); | ||
} else { | ||
console.log("// (beautified)"); | ||
console.log(beautified.code); | ||
} | ||
} | ||
|
||
module.exports = function(options) { | ||
console.log("--- UglifyJS <-> Mozilla AST conversion"); | ||
|
||
for (var counter = 0; counter < options.iterations; counter++) { | ||
process.stdout.write(prefix + counter + "/" + options.iterations); | ||
|
||
var ast1 = normalizeInput(esfuzz.generate({ | ||
maxDepth: options.maxDepth | ||
})); | ||
|
||
var ast2 = | ||
UglifyJS | ||
.AST_Node | ||
.from_mozilla_ast(ast1) | ||
.to_mozilla_ast(); | ||
|
||
var astPair = [ | ||
{name: 'expected', value: ast1}, | ||
{name: 'actual', value: ast2} | ||
]; | ||
|
||
var jsPair = astPair.map(function(item) { | ||
return { | ||
name: item.name, | ||
value: escodegen.generate(item.value) | ||
} | ||
function test(original, estree, description) { | ||
var transformed; | ||
try { | ||
transformed = UglifyJS.minify(estree, { | ||
fromString: true, | ||
compress: false, | ||
mangle: false, | ||
spidermonkey: true | ||
}); | ||
|
||
if (jsPair[0].value !== jsPair[1].value) { | ||
var fs = require("fs"); | ||
var acorn = require("acorn"); | ||
|
||
fs.existsSync("tmp") || fs.mkdirSync("tmp"); | ||
|
||
jsPair.forEach(function (item) { | ||
var fileName = "tmp/dump_" + item.name; | ||
var ast = acorn.parse(item.value); | ||
fs.writeFileSync(fileName + ".js", item.value); | ||
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2)); | ||
}); | ||
|
||
process.stdout.write("\n"); | ||
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs."); | ||
} catch (ex) { | ||
transformed = { error: ex }; | ||
} | ||
if (transformed.error || original !== transformed.code) { | ||
console.log("//============================================================="); | ||
console.log("// !!!!!! Failed... round", round); | ||
console.log("// original code"); | ||
try_beautify(original); | ||
console.log(); | ||
console.log(); | ||
console.log("//-------------------------------------------------------------"); | ||
console.log("//", description); | ||
if (transformed.error) { | ||
console.log(transformed.error.stack); | ||
} else { | ||
try_beautify(transformed.code); | ||
} | ||
console.log("!!!!!! Failed... round", round); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n"); | ||
}; | ||
var num_iterations = ufuzz.num_iterations; | ||
for (var round = 1; round <= num_iterations; round++) { | ||
process.stdout.write(round + " of " + num_iterations + "\r"); | ||
var code = ufuzz.createTopLevelCode(); | ||
var uglified = { | ||
ast: UglifyJS.parse(code), | ||
code: UglifyJS.minify(code, { | ||
fromString: true, | ||
compress: false, | ||
mangle: false | ||
}).code | ||
}; | ||
test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()"); | ||
try { | ||
test(uglified.code, acorn.parse(code), "acorn.parse()"); | ||
} catch (e) { | ||
console.log("//============================================================="); | ||
console.log("// acorn parser failed... round", round); | ||
console.log(e); | ||
console.log("// original code"); | ||
console.log(code); | ||
} | ||
} | ||
console.log(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.