Adds esprima-based preprocessor.
This commit is contained in:
parent
bc3bcebde2
commit
bf52ff156d
@ -9,6 +9,7 @@ external/jpgjs/
|
|||||||
external/jasmine/
|
external/jasmine/
|
||||||
external/cmapscompress/
|
external/cmapscompress/
|
||||||
external/importL10n/
|
external/importL10n/
|
||||||
|
external/builder/fixtures_esprima/
|
||||||
shared/
|
shared/
|
||||||
test/tmp/
|
test/tmp/
|
||||||
test/features/
|
test/features/
|
||||||
|
10
external/builder/fixtures_esprima/blocks-expected.js
vendored
Normal file
10
external/builder/fixtures_esprima/blocks-expected.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
function test() {
|
||||||
|
"test";
|
||||||
|
"1";
|
||||||
|
"2";
|
||||||
|
"3";
|
||||||
|
if ("test") {
|
||||||
|
"5";
|
||||||
|
}
|
||||||
|
"4";
|
||||||
|
}
|
19
external/builder/fixtures_esprima/blocks.js
vendored
Normal file
19
external/builder/fixtures_esprima/blocks.js
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
function test() {
|
||||||
|
{;}
|
||||||
|
;
|
||||||
|
"test";
|
||||||
|
{
|
||||||
|
"1";
|
||||||
|
if (true) {
|
||||||
|
"2";
|
||||||
|
}
|
||||||
|
;
|
||||||
|
{
|
||||||
|
"3";
|
||||||
|
if ("test") {
|
||||||
|
"5";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"4";
|
||||||
|
}
|
||||||
|
}
|
28
external/builder/fixtures_esprima/comments-expected.js
vendored
Normal file
28
external/builder/fixtures_esprima/comments-expected.js
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
function f1() {
|
||||||
|
/* head */
|
||||||
|
"1";
|
||||||
|
/* mid */
|
||||||
|
"2";
|
||||||
|
}
|
||||||
|
/* tail */
|
||||||
|
function f2() {
|
||||||
|
// head
|
||||||
|
"1";
|
||||||
|
// mid
|
||||||
|
"2";
|
||||||
|
}
|
||||||
|
// tail
|
||||||
|
function f2() {
|
||||||
|
if ("1") {
|
||||||
|
// begin block
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
"2";
|
||||||
|
// trailing
|
||||||
|
if (/* s */
|
||||||
|
"3")
|
||||||
|
/*e*/
|
||||||
|
{
|
||||||
|
"4";
|
||||||
|
}
|
||||||
|
}
|
26
external/builder/fixtures_esprima/comments.js
vendored
Normal file
26
external/builder/fixtures_esprima/comments.js
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/* globals f0 */
|
||||||
|
function f1() {
|
||||||
|
/* head */
|
||||||
|
"1";
|
||||||
|
/* mid */
|
||||||
|
"2";
|
||||||
|
/* tail */
|
||||||
|
}
|
||||||
|
|
||||||
|
function f2() {
|
||||||
|
// head
|
||||||
|
"1";
|
||||||
|
// mid
|
||||||
|
"2";
|
||||||
|
// tail
|
||||||
|
}
|
||||||
|
|
||||||
|
function f2() {
|
||||||
|
if ("1") { // begin block
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
"2"; // trailing
|
||||||
|
if (/* s */"3"/*e*/) {
|
||||||
|
"4";
|
||||||
|
}
|
||||||
|
}
|
15
external/builder/fixtures_esprima/constants-expected.js
vendored
Normal file
15
external/builder/fixtures_esprima/constants-expected.js
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
var a = true;
|
||||||
|
var b = false;
|
||||||
|
var c = '1';
|
||||||
|
var d = false;
|
||||||
|
var e = true;
|
||||||
|
var f = '0';
|
||||||
|
var g = '1';
|
||||||
|
var h = '0';
|
||||||
|
var i = true;
|
||||||
|
var j = false;
|
||||||
|
var k = false;
|
||||||
|
var l = true;
|
||||||
|
var m = '1' === true;
|
||||||
|
var n = false;
|
||||||
|
var o = true;
|
15
external/builder/fixtures_esprima/constants.js
vendored
Normal file
15
external/builder/fixtures_esprima/constants.js
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
var a = true;
|
||||||
|
var b = false;
|
||||||
|
var c = true && '1';
|
||||||
|
var d = false && '0';
|
||||||
|
var e = true || '1';
|
||||||
|
var f = false || '0';
|
||||||
|
var g = true ? '1' : '0';
|
||||||
|
var h = false ? '1' : '0';
|
||||||
|
var i = 'test' === 'test';
|
||||||
|
var j = 'test' !== 'test';
|
||||||
|
var k = 'test' === 'test2';
|
||||||
|
var l = 'test' !== 'test2';
|
||||||
|
var m = '1' === true;
|
||||||
|
var n = !true;
|
||||||
|
var o = !false;
|
13
external/builder/fixtures_esprima/evals-expected.js
vendored
Normal file
13
external/builder/fixtures_esprima/evals-expected.js
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
var a = false;
|
||||||
|
var b = true;
|
||||||
|
var c = true;
|
||||||
|
var d = false;
|
||||||
|
var e = true;
|
||||||
|
var f = 'text';
|
||||||
|
var g = {
|
||||||
|
"obj": { "i": 1 },
|
||||||
|
"j": 2
|
||||||
|
};
|
||||||
|
var h = { 'test': 'test' };
|
||||||
|
var i = '0';
|
||||||
|
var j = { "i": 1 };
|
10
external/builder/fixtures_esprima/evals.js
vendored
Normal file
10
external/builder/fixtures_esprima/evals.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
var a = typeof PDFJSDev === 'undefined';
|
||||||
|
var b = typeof PDFJSDev !== 'undefined';
|
||||||
|
var c = PDFJSDev.test('TRUE');
|
||||||
|
var d = PDFJSDev.test('FALSE');
|
||||||
|
var e = PDFJSDev.eval('TRUE');
|
||||||
|
var f = PDFJSDev.eval('TEXT');
|
||||||
|
var g = PDFJSDev.eval('OBJ');
|
||||||
|
var h = PDFJSDev.json('$ROOT/external/builder/fixtures_esprima/evals.json');
|
||||||
|
var i = typeof PDFJSDev === 'undefined' ? PDFJSDev.eval('FALSE') : '0';
|
||||||
|
var j = typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('OBJ.obj') : '0';
|
1
external/builder/fixtures_esprima/evals.json
vendored
Normal file
1
external/builder/fixtures_esprima/evals.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ 'test': 'test' }
|
17
external/builder/fixtures_esprima/ifs-expected.js
vendored
Normal file
17
external/builder/fixtures_esprima/ifs-expected.js
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
if ('test') {
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
;
|
||||||
|
{
|
||||||
|
"2";
|
||||||
|
}
|
||||||
|
;
|
||||||
|
if ('1') {
|
||||||
|
"1";
|
||||||
|
}
|
25
external/builder/fixtures_esprima/ifs.js
vendored
Normal file
25
external/builder/fixtures_esprima/ifs.js
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
if ('test') {
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
"1";
|
||||||
|
} else {
|
||||||
|
"2";
|
||||||
|
}
|
||||||
|
if (false) {
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
if (false) {
|
||||||
|
"1";
|
||||||
|
} else {
|
||||||
|
"2";
|
||||||
|
}
|
||||||
|
if (true && false) {
|
||||||
|
"1";
|
||||||
|
}
|
||||||
|
if (true && false || '1') {
|
||||||
|
"1";
|
||||||
|
}
|
266
external/builder/preprocessor2.js
vendored
Normal file
266
external/builder/preprocessor2.js
vendored
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
/* jshint node:true */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var esprima = require('esprima');
|
||||||
|
var escodegen = require('escodegen');
|
||||||
|
var vm = require('vm');
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var PDFJS_PREPROCESSOR_NAME = 'PDFJSDev';
|
||||||
|
var ROOT_PREFIX = '$ROOT/';
|
||||||
|
|
||||||
|
function isLiteral(obj, value) {
|
||||||
|
return obj.type === 'Literal' && obj.value === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPDFJSPreprocessor(obj) {
|
||||||
|
return obj.type === 'Identifier' &&
|
||||||
|
obj.name === PDFJS_PREPROCESSOR_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
function evalWithDefines(code, defines, loc) {
|
||||||
|
if (!code || !code.trim()) {
|
||||||
|
throw new Error('No JavaScript expression given');
|
||||||
|
}
|
||||||
|
return vm.runInNewContext(code, defines, {displayErrors: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePreprocessorAction(ctx, actionName, args, loc) {
|
||||||
|
try {
|
||||||
|
var arg;
|
||||||
|
switch (actionName) {
|
||||||
|
case 'test':
|
||||||
|
arg = args[0];
|
||||||
|
if (!arg || arg.type !== 'Literal' ||
|
||||||
|
typeof arg.value !== 'string') {
|
||||||
|
throw new Error('No code for testing is given');
|
||||||
|
}
|
||||||
|
var isTrue = !!evalWithDefines(arg.value, ctx.defines);
|
||||||
|
return {type: 'Literal', value: isTrue, loc: loc};
|
||||||
|
case 'eval':
|
||||||
|
arg = args[0];
|
||||||
|
if (!arg || arg.type !== 'Literal' ||
|
||||||
|
typeof arg.value !== 'string') {
|
||||||
|
throw new Error('No code for eval is given');
|
||||||
|
}
|
||||||
|
var result = evalWithDefines(arg.value, ctx.defines);
|
||||||
|
if (typeof result === 'boolean' || typeof result === 'string' ||
|
||||||
|
typeof result === 'number') {
|
||||||
|
return {type: 'Literal', value: result, loc: loc};
|
||||||
|
}
|
||||||
|
if (typeof result === 'object') {
|
||||||
|
var parsedObj = esprima.parse('(' + JSON.stringify(result) + ')');
|
||||||
|
parsedObj.body[0].expression.loc = loc;
|
||||||
|
return parsedObj.body[0].expression;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
arg = args[0];
|
||||||
|
if (!arg || arg.type !== 'Literal' ||
|
||||||
|
typeof arg.value !== 'string') {
|
||||||
|
throw new Error('Path to JSON is not provided');
|
||||||
|
}
|
||||||
|
var jsonPath = arg.value;
|
||||||
|
if (jsonPath.indexOf(ROOT_PREFIX) === 0) {
|
||||||
|
jsonPath = path.join(ctx.rootPath,
|
||||||
|
jsonPath.substring(ROOT_PREFIX.length));
|
||||||
|
}
|
||||||
|
var jsonContent = fs.readFileSync(jsonPath).toString();
|
||||||
|
var parsedJSON = esprima.parse('(' +jsonContent + ')');
|
||||||
|
parsedJSON.body[0].expression.loc = loc;
|
||||||
|
return parsedJSON.body[0].expression;
|
||||||
|
}
|
||||||
|
throw new Error('Unsupported action');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Could not process ' + PDFJS_PREPROCESSOR_NAME + '.' +
|
||||||
|
actionName + ' at ' + JSON.stringify(loc) + '\n' +
|
||||||
|
e.name + ': ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function postprocessNode(ctx, node) {
|
||||||
|
switch (node.type) {
|
||||||
|
case 'IfStatement':
|
||||||
|
if (isLiteral(node.test, true)) {
|
||||||
|
// if (true) stmt1; => stmt1
|
||||||
|
return node.consequent;
|
||||||
|
} else if (isLiteral(node.test, false)) {
|
||||||
|
// if (false) stmt1; else stmt2; => stmt2
|
||||||
|
return node.alternate || {type: 'EmptyStatement', loc: node.loc};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ConditionalExpression':
|
||||||
|
if (isLiteral(node.test, true)) {
|
||||||
|
// true ? stmt1 : stmt2 => stmt1
|
||||||
|
return node.consequent;
|
||||||
|
} else if (isLiteral(node.test, false)) {
|
||||||
|
// false ? stmt1 : stmt2 => stmt2
|
||||||
|
return node.alternate;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'UnaryExpression':
|
||||||
|
if (node.operator === 'typeof' &&
|
||||||
|
isPDFJSPreprocessor(node.argument)) {
|
||||||
|
// typeof PDFJSDev => 'object'
|
||||||
|
return {type: 'Literal', value: 'object', loc: node.loc};
|
||||||
|
}
|
||||||
|
if (node.operator === '!' &&
|
||||||
|
node.argument.type === 'Literal' &&
|
||||||
|
typeof node.argument.value === 'boolean') {
|
||||||
|
// !true => false, !false => true
|
||||||
|
return {type: 'Literal', value: !node.argument.value, loc: node.loc};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'LogicalExpression':
|
||||||
|
switch (node.operator) {
|
||||||
|
case '&&':
|
||||||
|
if (isLiteral(node.left, true)) {
|
||||||
|
return node.right;
|
||||||
|
}
|
||||||
|
if (isLiteral(node.left, false)) {
|
||||||
|
return node.left;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '||':
|
||||||
|
if (isLiteral(node.left, true)) {
|
||||||
|
return node.left;
|
||||||
|
}
|
||||||
|
if (isLiteral(node.left, false)) {
|
||||||
|
return node.right;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'BinaryExpression':
|
||||||
|
switch (node.operator) {
|
||||||
|
case '==':
|
||||||
|
case '===':
|
||||||
|
case '!=':
|
||||||
|
case '!==':
|
||||||
|
if (node.left.type === 'Literal' &&
|
||||||
|
node.right.type === 'Literal' &&
|
||||||
|
typeof node.left.value === typeof node.right.value) {
|
||||||
|
// folding two literals == and != check
|
||||||
|
switch (typeof node.left.value) {
|
||||||
|
case 'string':
|
||||||
|
case 'boolean':
|
||||||
|
case 'number':
|
||||||
|
var equal = node.left.value === node.right.value;
|
||||||
|
return {
|
||||||
|
type: 'Literal',
|
||||||
|
value: (node.operator[0] === '=') === equal,
|
||||||
|
loc: node.loc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'CallExpression':
|
||||||
|
if (node.callee.type === 'MemberExpression' &&
|
||||||
|
isPDFJSPreprocessor(node.callee.object) &&
|
||||||
|
node.callee.property.type === 'Identifier') {
|
||||||
|
// PDFJSDev.xxxx(arg1, arg2, ...) => tranform
|
||||||
|
var action = node.callee.property.name;
|
||||||
|
return handlePreprocessorAction(ctx, action,
|
||||||
|
node.arguments, node.loc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'BlockStatement':
|
||||||
|
var subExpressionIndex = 0;
|
||||||
|
while (subExpressionIndex < node.body.length) {
|
||||||
|
if (node.body[subExpressionIndex].type === 'EmptyStatement') {
|
||||||
|
// Removing empty statements from the blocks.
|
||||||
|
node.body.splice(subExpressionIndex, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (node.body[subExpressionIndex].type === 'BlockStatement') {
|
||||||
|
// Block statements inside a block are moved to the parent one.
|
||||||
|
var subChildren = node.body[subExpressionIndex].body;
|
||||||
|
Array.prototype.splice.apply(node.body,
|
||||||
|
[subExpressionIndex, 1].concat(subChildren));
|
||||||
|
subExpressionIndex += subChildren.length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
subExpressionIndex++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixComments(ctx, node) {
|
||||||
|
if (!ctx.saveComments) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Fixes double comments in the escodegen output.
|
||||||
|
delete node.trailingComments;
|
||||||
|
// Removes jshint and other service comments.
|
||||||
|
if (node.leadingComments) {
|
||||||
|
var i = 0;
|
||||||
|
while (i < node.leadingComments.length) {
|
||||||
|
var type = node.leadingComments[i].type;
|
||||||
|
var value = node.leadingComments[i].value;
|
||||||
|
if (type === 'Block' &&
|
||||||
|
/^\s*(globals|jshint|falls through|umdutils)\b/.test(value)) {
|
||||||
|
node.leadingComments.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverseTree(ctx, node) {
|
||||||
|
// generic node processing
|
||||||
|
for (var i in node) {
|
||||||
|
var child = node[i];
|
||||||
|
if (typeof child === 'object' && child !== null && child.type) {
|
||||||
|
var result = traverseTree(ctx, child);
|
||||||
|
if (result !== child) {
|
||||||
|
node[i] = result;
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(child)) {
|
||||||
|
child.forEach(function (childItem, index) {
|
||||||
|
if (typeof childItem === 'object' && childItem !== null &&
|
||||||
|
childItem.type) {
|
||||||
|
var result = traverseTree(ctx, childItem);
|
||||||
|
if (result !== childItem) {
|
||||||
|
child[index] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = postprocessNode(ctx, node) || node;
|
||||||
|
|
||||||
|
fixComments(ctx, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function preprocessPDFJSCode(ctx, code) {
|
||||||
|
var saveComments = !!ctx.saveComments;
|
||||||
|
var format = ctx.format || {
|
||||||
|
indent: {
|
||||||
|
style: ' ',
|
||||||
|
adjustMultilineComment: saveComments,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var parseComment = {
|
||||||
|
loc: true,
|
||||||
|
attachComment: saveComments
|
||||||
|
};
|
||||||
|
var codegenOptions = {
|
||||||
|
format: format,
|
||||||
|
comment: saveComments,
|
||||||
|
parse: esprima.parse
|
||||||
|
};
|
||||||
|
var syntax = esprima.parse(code, parseComment);
|
||||||
|
traverseTree(ctx, syntax);
|
||||||
|
return escodegen.generate(syntax, codegenOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.preprocessPDFJSCode = preprocessPDFJSCode;
|
54
external/builder/test2.js
vendored
Normal file
54
external/builder/test2.js
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/* jshint node:true */
|
||||||
|
/* globals cat, cd, echo, ls */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('shelljs/make');
|
||||||
|
|
||||||
|
var p2 = require('./preprocessor2.js');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var errors = 0;
|
||||||
|
|
||||||
|
cd(__dirname);
|
||||||
|
cd('fixtures_esprima');
|
||||||
|
ls('*-expected.*').forEach(function(expectationFilename) {
|
||||||
|
var inFilename = expectationFilename.replace('-expected', '');
|
||||||
|
var expectation = cat(expectationFilename).trim()
|
||||||
|
.replace(/__filename/g, fs.realpathSync(inFilename));
|
||||||
|
var input = fs.readFileSync(inFilename).toString();
|
||||||
|
|
||||||
|
var defines = {
|
||||||
|
TRUE: true,
|
||||||
|
FALSE: false,
|
||||||
|
OBJ: {obj: {i: 1}, j: 2},
|
||||||
|
TEXT: 'text'
|
||||||
|
};
|
||||||
|
var ctx = {
|
||||||
|
defines: defines,
|
||||||
|
rootPath: __dirname + '/../..',
|
||||||
|
saveComments: true
|
||||||
|
};
|
||||||
|
var out;
|
||||||
|
try {
|
||||||
|
out = p2.preprocessPDFJSCode(ctx, input);
|
||||||
|
} catch (e) {
|
||||||
|
out = ('Error: ' + e.message).replace(/^/gm, '//');
|
||||||
|
}
|
||||||
|
if (out !== expectation) {
|
||||||
|
echo('Assertion failed for ' + inFilename);
|
||||||
|
echo('--------------------------------------------------');
|
||||||
|
echo('EXPECTED:');
|
||||||
|
echo(expectation);
|
||||||
|
echo('--------------------------------------------------');
|
||||||
|
echo('ACTUAL');
|
||||||
|
echo(out);
|
||||||
|
echo('--------------------------------------------------');
|
||||||
|
echo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
echo('Found ' + errors + ' expectation failures.');
|
||||||
|
} else {
|
||||||
|
echo('All tests completed without errors.');
|
||||||
|
}
|
@ -2,6 +2,8 @@
|
|||||||
"name": "pdf.js",
|
"name": "pdf.js",
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"escodegen": "^1.8.0",
|
||||||
|
"esprima": "^2.7.2",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-util": "^3.0.7",
|
"gulp-util": "^3.0.7",
|
||||||
"gulp-zip": "^3.2.0",
|
"gulp-zip": "^3.2.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user