123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- "use strict";
- const acorn = require("acorn");
- const escodegen = require("@javascript-obfuscator/escodegen");
- const vm = require("vm");
- const fs = require("fs");
- const path = require("path");
- const PDFJS_PREPROCESSOR_NAME = "PDFJSDev";
- const ROOT_PREFIX = "$ROOT/";
- const ACORN_ECMA_VERSION = 2022;
- 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 {
- let 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");
- }
- const isTrue = !!evalWithDefines(arg.value, ctx.defines);
- return { type: "Literal", value: isTrue, loc };
- case "eval":
- arg = args[0];
- if (!arg || arg.type !== "Literal" || typeof arg.value !== "string") {
- throw new Error("No code for eval is given");
- }
- const result = evalWithDefines(arg.value, ctx.defines);
- if (
- typeof result === "boolean" ||
- typeof result === "string" ||
- typeof result === "number"
- ) {
- return { type: "Literal", value: result, loc };
- }
- if (typeof result === "object") {
- const parsedObj = acorn.parse("(" + JSON.stringify(result) + ")", {
- ecmaVersion: ACORN_ECMA_VERSION,
- });
- 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");
- }
- let jsonPath = arg.value;
- if (jsonPath.indexOf(ROOT_PREFIX) === 0) {
- jsonPath = path.join(
- ctx.rootPath,
- jsonPath.substring(ROOT_PREFIX.length)
- );
- }
- const jsonContent = fs.readFileSync(jsonPath).toString();
- const parsedJSON = acorn.parse("(" + jsonContent + ")", {
- ecmaVersion: ACORN_ECMA_VERSION,
- });
- 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 "ExportNamedDeclaration":
- case "ImportDeclaration":
- if (
- node.source &&
- node.source.type === "Literal" &&
- ctx.map &&
- ctx.map[node.source.value]
- ) {
- const newValue = ctx.map[node.source.value];
- node.source.value = node.source.raw = newValue;
- }
- break;
- 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":
- const 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, ...) => transform
- const action = node.callee.property.name;
- return handlePreprocessorAction(ctx, action, node.arguments, node.loc);
- }
- // require('string')
- if (
- node.callee.type === "Identifier" &&
- node.callee.name === "require" &&
- node.arguments.length === 1 &&
- node.arguments[0].type === "Literal" &&
- ctx.map &&
- ctx.map[node.arguments[0].value]
- ) {
- const requireName = node.arguments[0];
- requireName.value = requireName.raw = ctx.map[requireName.value];
- }
- break;
- case "BlockStatement":
- let subExpressionIndex = 0;
- while (subExpressionIndex < node.body.length) {
- switch (node.body[subExpressionIndex].type) {
- case "EmptyStatement":
- // Removing empty statements from the blocks.
- node.body.splice(subExpressionIndex, 1);
- continue;
- case "BlockStatement":
- // Block statements inside a block are moved to the parent one.
- const subChildren = node.body[subExpressionIndex].body;
- Array.prototype.splice.apply(node.body, [
- subExpressionIndex,
- 1,
- ...subChildren,
- ]);
- subExpressionIndex += Math.max(subChildren.length - 1, 0);
- continue;
- case "ReturnStatement":
- case "ThrowStatement":
- // Removing dead code after return or throw.
- node.body.splice(
- subExpressionIndex + 1,
- node.body.length - subExpressionIndex - 1
- );
- break;
- }
- subExpressionIndex++;
- }
- break;
- case "FunctionDeclaration":
- case "FunctionExpression":
- const block = node.body;
- if (
- block.body.length > 0 &&
- block.body[block.body.length - 1].type === "ReturnStatement" &&
- !block.body[block.body.length - 1].argument
- ) {
- // Function body ends with return without arg -- removing it.
- block.body.pop();
- }
- break;
- }
- return node;
- }
- function fixComments(ctx, node) {
- if (!ctx.saveComments) {
- return;
- }
- // Fixes double comments in the escodegen output.
- delete node.trailingComments;
- // Removes ESLint and other service comments.
- if (node.leadingComments) {
- const CopyrightRegExp = /\bcopyright\b/i;
- const BlockCommentRegExp = /^\s*(globals|eslint|falls through)\b/;
- const LineCommentRegExp = /^\s*eslint\b/;
- let i = 0;
- while (i < node.leadingComments.length) {
- const type = node.leadingComments[i].type;
- const value = node.leadingComments[i].value;
- if (ctx.saveComments === "copyright") {
- // Remove all comments, except Copyright notices and License headers.
- if (!(type === "Block" && CopyrightRegExp.test(value))) {
- node.leadingComments.splice(i, 1);
- continue;
- }
- } else if (
- (type === "Block" && BlockCommentRegExp.test(value)) ||
- (type === "Line" && LineCommentRegExp.test(value))
- ) {
- node.leadingComments.splice(i, 1);
- continue;
- }
- i++;
- }
- }
- }
- function traverseTree(ctx, node) {
- // generic node processing
- for (const i in node) {
- const child = node[i];
- if (typeof child === "object" && child !== null && child.type) {
- const 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
- ) {
- const result = traverseTree(ctx, childItem);
- if (result !== childItem) {
- child[index] = result;
- }
- }
- });
- }
- }
- node = postprocessNode(ctx, node) || node;
- fixComments(ctx, node);
- return node;
- }
- function preprocessPDFJSCode(ctx, code) {
- const format = ctx.format || {
- indent: {
- style: " ",
- },
- };
- const parseOptions = {
- ecmaVersion: ACORN_ECMA_VERSION,
- locations: true,
- sourceFile: ctx.sourceFile,
- sourceType: "module",
- };
- const codegenOptions = {
- format,
- parse(input) {
- return acorn.parse(input, { ecmaVersion: ACORN_ECMA_VERSION });
- },
- sourceMap: ctx.sourceMap,
- sourceMapWithCode: ctx.sourceMap,
- };
- const syntax = acorn.parse(code, parseOptions);
- traverseTree(ctx, syntax);
- return escodegen.generate(syntax, codegenOptions);
- }
- exports.preprocessPDFJSCode = preprocessPDFJSCode;
|