mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-09 17:55:37 +02:00
Adds esprima-based preprocessor.
This commit is contained in:
parent
bc3bcebde2
commit
bf52ff156d
15 changed files with 502 additions and 0 deletions
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;
|
Loading…
Add table
Add a link
Reference in a new issue