From 783d74ae3431e14882c0aae0c3300cb5226a2d14 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Mon, 30 Jul 2018 13:58:09 +0200 Subject: [PATCH] Add support for `async`/`await` using Babel For proof-of-concept, this patch converts a couple of `Promise` returning methods to use `async` instead. Please note that the `generic` build, based on this patch, has been successfully testing in IE11 (i.e. the viewer loads and nothing is obviously broken). Being able to use modern JavaScript features like `async`/`await` is a huge plus, but there's one (obvious) side-effect: The size of the built files will increase slightly (unless `SKIP_BABEL == true`). That's unavoidable, but seems like a small price to pay in the grand scheme of things. Finally, note that the `chromium` build target was changed to no longer skip Babel translation, since the Chrome extension still supports version `49` of the browser (where native `async` support isn't available). --- .eslintrc | 2 +- external/builder/preprocessor2.js | 1 + gulpfile.js | 16 +++++- package-lock.json | 32 ++++++----- package.json | 1 + src/core/evaluator.js | 20 ++++--- src/core/obj.js | 28 ++++------ src/core/pdf_manager.js | 55 ++++++++----------- src/display/fetch_stream.js | 56 +++++++++----------- src/display/network.js | 14 ++--- src/display/node_stream.js | 88 +++++++++++++++---------------- src/display/transport_stream.js | 12 ++--- 12 files changed, 155 insertions(+), 170 deletions(-) diff --git a/.eslintrc b/.eslintrc index 80211e6523f3c6..1306bc20f03698 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 8, "sourceType": "module", }, diff --git a/external/builder/preprocessor2.js b/external/builder/preprocessor2.js index d53b23726b2daa..eef28e939eda8f 100644 --- a/external/builder/preprocessor2.js +++ b/external/builder/preprocessor2.js @@ -288,6 +288,7 @@ function preprocessPDFJSCode(ctx, code) { }, }; var parseOptions = { + ecmaVersion: 8, locations: true, sourceFile: ctx.sourceFile, sourceType: 'module', diff --git a/gulpfile.js b/gulpfile.js index d419755eb1d485..5b2ed651c5ea64 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -177,7 +177,14 @@ function createWebpackConfig(defines, output) { exclude: /src[\\\/]core[\\\/](glyphlist|unicode)/, options: { presets: skipBabel ? undefined : ['env'], - plugins: ['transform-es2015-modules-commonjs'], + plugins: [ + 'transform-es2015-modules-commonjs', + ['transform-runtime', { + 'helpers': false, + 'polyfill': false, + 'regenerator': true, + }], + ], }, }, { @@ -808,7 +815,7 @@ gulp.task('mozcentral', ['mozcentral-pre']); gulp.task('chromium-pre', ['buildnumber', 'locale'], function () { console.log(); console.log('### Building Chromium extension'); - var defines = builder.merge(DEFINES, { CHROME: true, SKIP_BABEL: true, }); + var defines = builder.merge(DEFINES, { CHROME: true, }); var CHROME_BUILD_DIR = BUILD_DIR + '/chromium/', CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + '/content/'; @@ -897,6 +904,11 @@ gulp.task('lib', ['buildnumber'], function () { presets: noPreset ? undefined : ['env'], plugins: [ 'transform-es2015-modules-commonjs', + ['transform-runtime', { + 'helpers': false, + 'polyfill': false, + 'regenerator': true, + }], babelPluginReplaceNonWebPackRequire, ], }).code; diff --git a/package-lock.json b/package-lock.json index 75227470fe86c6..c6c0322047ac92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1210,6 +1210,15 @@ "regenerator-transform": "^0.10.0" } }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, "babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", @@ -3396,14 +3405,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3418,20 +3425,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3548,8 +3552,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3561,7 +3564,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3576,7 +3578,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3688,8 +3689,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3701,7 +3701,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3823,7 +3822,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/package.json b/package.json index e97d0bf66244ae..6902c74528a5ab 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", + "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "core-js": "^2.5.7", "escodegen": "^1.11.0", diff --git a/src/core/evaluator.js b/src/core/evaluator.js index d7bccc4013803e..baf86e72075cfa 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -131,19 +131,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { this.options = options || DefaultPartialEvaluatorOptions; this.pdfFunctionFactory = pdfFunctionFactory; - this.fetchBuiltInCMap = (name) => { + this.fetchBuiltInCMap = async (name) => { if (this.builtInCMapCache.has(name)) { - return Promise.resolve(this.builtInCMapCache.get(name)); + return this.builtInCMapCache.get(name); } - return this.handler.sendWithPromise('FetchBuiltInCMap', { - name, - }).then((data) => { - if (data.compressionType !== CMapCompressionType.NONE) { - // Given the size of uncompressed CMaps, only cache compressed ones. - this.builtInCMapCache.set(name, data); - } - return data; - }); + const data = await this.handler.sendWithPromise('FetchBuiltInCMap', + { name, }); + if (data.compressionType !== CMapCompressionType.NONE) { + // Given the size of uncompressed CMaps, only cache compressed ones. + this.builtInCMapCache.set(name, data); + } + return data; }; } diff --git a/src/core/obj.js b/src/core/obj.js index e345d23f65603f..82035a5104a9fe 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -1497,29 +1497,23 @@ var XRef = (function XRefClosure() { return xrefEntry; }, - fetchIfRefAsync: function XRef_fetchIfRefAsync(obj, suppressEncryption) { + async fetchIfRefAsync(obj, suppressEncryption) { if (!isRef(obj)) { - return Promise.resolve(obj); + return obj; } return this.fetchAsync(obj, suppressEncryption); }, - fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) { - var streamManager = this.stream.manager; - var xref = this; - return new Promise(function tryFetch(resolve, reject) { - try { - resolve(xref.fetch(ref, suppressEncryption)); - } catch (e) { - if (e instanceof MissingDataException) { - streamManager.requestRange(e.begin, e.end).then(function () { - tryFetch(resolve, reject); - }, reject); - return; - } - reject(e); + async fetchAsync(ref, suppressEncryption) { + try { + return this.fetch(ref, suppressEncryption); + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + throw ex; } - }); + await this.pdfManager.requestRange(ex.begin, ex.end); + return this.fetchAsync(ref, suppressEncryption); + } }, getCatalogObj: function XRef_getCatalogObj() { diff --git a/src/core/pdf_manager.js b/src/core/pdf_manager.js index 4368fffd1d317d..3981141213226e 100644 --- a/src/core/pdf_manager.js +++ b/src/core/pdf_manager.js @@ -72,7 +72,7 @@ class BasePdfManager { return this.pdfDocument.cleanup(); } - ensure(obj, prop, args) { + async ensure(obj, prop, args) { unreachable('Abstract method `ensure` called'); } @@ -111,15 +111,12 @@ class LocalPdfManager extends BasePdfManager { this._loadedStreamPromise = Promise.resolve(stream); } - ensure(obj, prop, args) { - return new Promise(function(resolve) { - const value = obj[prop]; - if (typeof value === 'function') { - resolve(value.apply(obj, args)); - } else { - resolve(value); - } - }); + async ensure(obj, prop, args) { + const value = obj[prop]; + if (typeof value === 'function') { + return value.apply(obj, args); + } + return value; } requestRange(begin, end) { @@ -155,30 +152,20 @@ class NetworkPdfManager extends BasePdfManager { this.pdfDocument = new PDFDocument(this, this.streamManager.getStream()); } - ensure(obj, prop, args) { - return new Promise((resolve, reject) => { - let ensureHelper = () => { - try { - const value = obj[prop]; - let result; - if (typeof value === 'function') { - result = value.apply(obj, args); - } else { - result = value; - } - resolve(result); - } catch (ex) { - if (!(ex instanceof MissingDataException)) { - reject(ex); - return; - } - this.streamManager.requestRange(ex.begin, ex.end) - .then(ensureHelper, reject); - } - }; - - ensureHelper(); - }); + async ensure(obj, prop, args) { + try { + const value = obj[prop]; + if (typeof value === 'function') { + return value.apply(obj, args); + } + return value; + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + throw ex; + } + await this.requestRange(ex.begin, ex.end); + return this.ensure(obj, prop, args); + } } requestRange(begin, end) { diff --git a/src/display/fetch_stream.js b/src/display/fetch_stream.js index e27fff77fbaf53..20ca3281f6651e 100644 --- a/src/display/fetch_stream.js +++ b/src/display/fetch_stream.js @@ -152,23 +152,21 @@ class PDFFetchStreamReader { return this._isStreamingSupported; } - read() { - return this._headersCapability.promise.then(() => { - return this._reader.read().then(({ value, done, }) => { - if (done) { - return Promise.resolve({ value, done, }); - } - this._loaded += value.byteLength; - if (this.onProgress) { - this.onProgress({ - loaded: this._loaded, - total: this._contentLength, - }); - } - let buffer = new Uint8Array(value).buffer; - return Promise.resolve({ value: buffer, done: false, }); + async read() { + await this._headersCapability.promise; + const { value, done, } = await this._reader.read(); + if (done) { + return { value, done, }; + } + this._loaded += value.byteLength; + if (this.onProgress) { + this.onProgress({ + loaded: this._loaded, + total: this._contentLength, }); - }); + } + let buffer = new Uint8Array(value).buffer; + return { value: buffer, done: false, }; } cancel(reason) { @@ -223,20 +221,18 @@ class PDFFetchStreamRangeReader { return this._isStreamingSupported; } - read() { - return this._readCapability.promise.then(() => { - return this._reader.read().then(({ value, done, }) => { - if (done) { - return Promise.resolve({ value, done, }); - } - this._loaded += value.byteLength; - if (this.onProgress) { - this.onProgress({ loaded: this._loaded, }); - } - let buffer = new Uint8Array(value).buffer; - return Promise.resolve({ value: buffer, done: false, }); - }); - }); + async read() { + await this._readCapability.promise; + const { value, done, } = await this._reader.read(); + if (done) { + return { value, done, }; + } + this._loaded += value.byteLength; + if (this.onProgress) { + this.onProgress({ loaded: this._loaded, }); + } + let buffer = new Uint8Array(value).buffer; + return { value: buffer, done: false, }; } cancel(reason) { diff --git a/src/display/network.js b/src/display/network.js index eba8a3f4aed1d4..1a75336219cb1a 100644 --- a/src/display/network.js +++ b/src/display/network.js @@ -453,16 +453,16 @@ PDFNetworkStreamFullRequestReader.prototype = { return this._headersReceivedCapability.promise; }, - read: function PDFNetworkStreamFullRequestReader_read() { + async read() { if (this._storedError) { - return Promise.reject(this._storedError); + throw this._storedError; } if (this._cachedChunks.length > 0) { var chunk = this._cachedChunks.shift(); - return Promise.resolve({ value: chunk, done: false, }); + return { value: chunk, done: false, }; } if (this._done) { - return Promise.resolve({ value: undefined, done: true, }); + return { value: undefined, done: true, }; } var requestCapability = createPromiseCapability(); this._requests.push(requestCapability); @@ -534,14 +534,14 @@ PDFNetworkStreamRangeRequestReader.prototype = { return false; // TODO allow progressive range bytes loading }, - read: function PDFNetworkStreamRangeRequestReader_read() { + async read() { if (this._queuedChunk !== null) { var chunk = this._queuedChunk; this._queuedChunk = null; - return Promise.resolve({ value: chunk, done: false, }); + return { value: chunk, done: false, }; } if (this._done) { - return Promise.resolve({ value: undefined, done: true, }); + return { value: undefined, done: true, }; } var requestCapability = createPromiseCapability(); this._requests.push(requestCapability); diff --git a/src/display/node_stream.js b/src/display/node_stream.js index f7b2f38af90916..5b744f867fba71 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -131,31 +131,30 @@ class BaseFullReader { return this._isStreamingSupported; } - read() { - return this._readCapability.promise.then(() => { - if (this._done) { - return Promise.resolve({ value: undefined, done: true, }); - } - if (this._storedError) { - return Promise.reject(this._storedError); - } + async read() { + await this._readCapability.promise; + if (this._done) { + return { value: undefined, done: true, }; + } + if (this._storedError) { + throw this._storedError; + } - let chunk = this._readableStream.read(); - if (chunk === null) { - this._readCapability = createPromiseCapability(); - return this.read(); - } - this._loaded += chunk.length; - if (this.onProgress) { - this.onProgress({ - loaded: this._loaded, - total: this._contentLength, - }); - } - // Ensure that `read()` method returns ArrayBuffer. - let buffer = new Uint8Array(chunk).buffer; - return Promise.resolve({ value: buffer, done: false, }); - }); + let chunk = this._readableStream.read(); + if (chunk === null) { + this._readCapability = createPromiseCapability(); + return this.read(); + } + this._loaded += chunk.length; + if (this.onProgress) { + this.onProgress({ + loaded: this._loaded, + total: this._contentLength, + }); + } + // Ensure that `read()` method returns ArrayBuffer. + let buffer = new Uint8Array(chunk).buffer; + return { value: buffer, done: false, }; } cancel(reason) { @@ -220,28 +219,27 @@ class BaseRangeReader { return this._isStreamingSupported; } - read() { - return this._readCapability.promise.then(() => { - if (this._done) { - return Promise.resolve({ value: undefined, done: true, }); - } - if (this._storedError) { - return Promise.reject(this._storedError); - } + async read() { + await this._readCapability.promise; + if (this._done) { + return { value: undefined, done: true, }; + } + if (this._storedError) { + throw this._storedError; + } - let chunk = this._readableStream.read(); - if (chunk === null) { - this._readCapability = createPromiseCapability(); - return this.read(); - } - this._loaded += chunk.length; - if (this.onProgress) { - this.onProgress({ loaded: this._loaded, }); - } - // Ensure that `read()` method returns ArrayBuffer. - let buffer = new Uint8Array(chunk).buffer; - return Promise.resolve({ value: buffer, done: false, }); - }); + let chunk = this._readableStream.read(); + if (chunk === null) { + this._readCapability = createPromiseCapability(); + return this.read(); + } + this._loaded += chunk.length; + if (this.onProgress) { + this.onProgress({ loaded: this._loaded, }); + } + // Ensure that `read()` method returns ArrayBuffer. + let buffer = new Uint8Array(chunk).buffer; + return { value: buffer, done: false, }; } cancel(reason) { diff --git a/src/display/transport_stream.js b/src/display/transport_stream.js index df2eb42c523567..fe02746b7a5b64 100644 --- a/src/display/transport_stream.js +++ b/src/display/transport_stream.js @@ -160,13 +160,13 @@ var PDFDataTransportStream = (function PDFDataTransportStreamClosure() { return this._stream._contentLength; }, - read: function PDFDataTransportStreamReader_read() { + async read() { if (this._queuedChunks.length > 0) { var chunk = this._queuedChunks.shift(); - return Promise.resolve({ value: chunk, done: false, }); + return { value: chunk, done: false, }; } if (this._done) { - return Promise.resolve({ value: undefined, done: true, }); + return { value: undefined, done: true, }; } var requestCapability = createPromiseCapability(); this._requests.push(requestCapability); @@ -216,14 +216,14 @@ var PDFDataTransportStream = (function PDFDataTransportStreamClosure() { return false; }, - read: function PDFDataTransportStreamRangeReader_read() { + async read() { if (this._queuedChunk) { let chunk = this._queuedChunk; this._queuedChunk = null; - return Promise.resolve({ value: chunk, done: false, }); + return { value: chunk, done: false, }; } if (this._done) { - return Promise.resolve({ value: undefined, done: true, }); + return { value: undefined, done: true, }; } var requestCapability = createPromiseCapability(); this._requests.push(requestCapability);