From dc926ffc0f22a588e4e6a6808d69d3c02c958233 Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Thu, 14 Sep 2017 15:38:57 +0200
Subject: [PATCH] Check `isEvalSupported`, and test that `eval` is actually
 supported, before attempting to use the `PostScriptCompiler` (issue 5573)

Currently `PDFFunction` is implemented (basically) like a class with only `static` methods. Since it's used directly in a number of different `src/core/` files, attempting to pass in `isEvalSupported` would result in code that's *very* messy, not to mention difficult to maintain (since *every* single `PDFFunction` method call would need to include a `isEvalSupported` argument).

Rather than having to wait for a possible re-factoring of `PDFFunction` that would avoid the above problems by design, it probably makes sense to at least set `isEvalSupported` globally for `PDFFunction`.

Please note that there's one caveat with this solution: If `PDFJS.getDocument` is used to open multiple files simultaneously, with *different* `PDFJS.isEvalSupported` values set before each call, then the last one will always win.
However, that seems like enough of an edge-case that we shouldn't have to worry about it. Besides, since we'll also test that `eval` is actually supported, it should be fine.

Fixes 5573.
---
 src/core/document.js  |  4 ++++
 src/core/evaluator.js |  1 +
 src/core/function.js  | 34 +++++++++++++++++++++++++---------
 src/core/worker.js    |  1 +
 src/display/api.js    |  1 +
 5 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/src/core/document.js b/src/core/document.js
index 1294b22d1..4d8b066fe 100644
--- a/src/core/document.js
+++ b/src/core/document.js
@@ -24,6 +24,7 @@ import { OperatorList, PartialEvaluator } from './evaluator';
 import { AnnotationFactory } from './annotation';
 import { calculateMD5 } from './crypto';
 import { Linearization } from './parser';
+import { PDFFunction } from './function';
 
 var Page = (function PageClosure() {
 
@@ -532,6 +533,9 @@ var PDFDocument = (function PDFDocumentClosure() {
         },
       };
       this.catalog = new Catalog(this.pdfManager, this.xref, pageFactory);
+
+      let evaluatorOptions = this.pdfManager.evaluatorOptions;
+      PDFFunction.setIsEvalSupported(evaluatorOptions.isEvalSupported);
     },
     get numPages() {
       var linearization = this.linearization;
diff --git a/src/core/evaluator.js b/src/core/evaluator.js
index 4719107ef..796a33258 100644
--- a/src/core/evaluator.js
+++ b/src/core/evaluator.js
@@ -54,6 +54,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
     disableFontFace: false,
     nativeImageDecoderSupport: NativeImageDecoding.DECODE,
     ignoreErrors: false,
+    isEvalSupported: true,
   };
 
   function NativeImageDecoder(xref, resources, handler, forceDataSchema) {
diff --git a/src/core/function.js b/src/core/function.js
index 86f5b424e..a07f4c27a 100644
--- a/src/core/function.js
+++ b/src/core/function.js
@@ -13,17 +13,31 @@
  * limitations under the License.
  */
 
-import { FormatError, info, isBool } from '../shared/util';
+import {
+  FormatError, info, isBool, isEvalSupported, shadow
+} from '../shared/util';
 import { isDict, isStream } from './primitives';
 import { PostScriptLexer, PostScriptParser } from './ps_parser';
 
+let IsEvalSupportedCached = {
+  get value() {
+    return shadow(this, 'value', isEvalSupported());
+  },
+};
+
 var PDFFunction = (function PDFFunctionClosure() {
   var CONSTRUCT_SAMPLED = 0;
   var CONSTRUCT_INTERPOLATED = 2;
   var CONSTRUCT_STICHED = 3;
   var CONSTRUCT_POSTSCRIPT = 4;
 
+  let isEvalSupported = true;
+
   return {
+    setIsEvalSupported(support = true) {
+      isEvalSupported = support !== false;
+    },
+
     getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps,
                                                        str) {
       var i, ii;
@@ -399,15 +413,17 @@ var PDFFunction = (function PDFFunctionClosure() {
       var range = IR[2];
       var code = IR[3];
 
-      var compiled = (new PostScriptCompiler()).compile(code, domain, range);
-      if (compiled) {
-        // Compiled function consists of simple expressions such as addition,
-        // subtraction, Math.max, and also contains 'var' and 'return'
-        // statements. See the generation in the PostScriptCompiler below.
-        // eslint-disable-next-line no-new-func
-        return new Function('src', 'srcOffset', 'dest', 'destOffset', compiled);
+      if (isEvalSupported && IsEvalSupportedCached.value) {
+        let compiled = (new PostScriptCompiler()).compile(code, domain, range);
+        if (compiled) {
+          // Compiled function consists of simple expressions such as addition,
+          // subtraction, Math.max, and also contains 'var' and 'return'
+          // statements. See the generation in the PostScriptCompiler below.
+          // eslint-disable-next-line no-new-func
+          return new Function('src', 'srcOffset', 'dest', 'destOffset',
+                              compiled);
+        }
       }
-
       info('Unable to compile PS function');
 
       var numOutputs = range.length >> 1;
diff --git a/src/core/worker.js b/src/core/worker.js
index 4156e4813..262a29048 100644
--- a/src/core/worker.js
+++ b/src/core/worker.js
@@ -602,6 +602,7 @@ var WorkerMessageHandler = {
         disableFontFace: data.disableFontFace,
         nativeImageDecoderSupport: data.nativeImageDecoderSupport,
         ignoreErrors: data.ignoreErrors,
+        isEvalSupported: data.isEvalSupported,
       };
 
       getPdfManager(data, evaluatorOptions).then(function (newPdfManager) {
diff --git a/src/display/api.js b/src/display/api.js
index ddd764d5f..ec08f8204 100644
--- a/src/display/api.js
+++ b/src/display/api.js
@@ -357,6 +357,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
     docBaseUrl: source.docBaseUrl,
     nativeImageDecoderSupport: source.nativeImageDecoderSupport,
     ignoreErrors: source.ignoreErrors,
+    isEvalSupported: getDefaultSetting('isEvalSupported'),
   }).then(function (workerId) {
     if (worker.destroyed) {
       throw new Error('Worker was destroyed');