From 0a28b34dacb91a7e74cd5feec59cf8f8fb0487c9 Mon Sep 17 00:00:00 2001 From: Xuan Huang Date: Mon, 19 Oct 2020 15:22:36 -0700 Subject: [PATCH] Conditionalize Promise Polyfill for Hermes Summary: On Hermes, RN can directly use the Promise from global w/o the need of polyfilling it. PromiseRejectionTrackingOptions are extracted to its own file so it can be shared by both codepaths and preserve the behaviors that it's only imported on dev. Some zero-overhead type gymnastics are used to flow-type it properly. Changelog: [General] - made promise polyfill conditionalized on Hermes Reviewed By: cpojer Differential Revision: D24068716 fbshipit-source-id: 3e0b1675493908324f27cc5b7300d8cc42a03acc --- Libraries/Core/polyfillPromise.js | 18 ++++++- Libraries/Promise.js | 39 ++------------ Libraries/promiseRejectionTrackingOptions.js | 54 ++++++++++++++++++++ 3 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 Libraries/promiseRejectionTrackingOptions.js diff --git a/Libraries/Core/polyfillPromise.js b/Libraries/Core/polyfillPromise.js index b15ccc2b301d25..6551745bf52c5d 100644 --- a/Libraries/Core/polyfillPromise.js +++ b/Libraries/Core/polyfillPromise.js @@ -19,4 +19,20 @@ const {polyfillGlobal} = require('../Utilities/PolyfillFunctions'); * If you don't need these polyfills, don't use InitializeCore; just directly * require the modules you need from InitializeCore for setup. */ -polyfillGlobal('Promise', () => require('../Promise')); + +// If global.Promise is provided by Hermes, we are confident that it can provide +// all the methods needed by React Native, so we can directly use it. +if (global?.HermesInternal?.hasPromise?.()) { + const HermesPromise = global.Promise; + + if (__DEV__) { + if (typeof HermesPromise !== 'function') { + console.error('HermesPromise does not exist'); + } + global.HermesInternal.enablePromiseRejectionTracker( + require('../promiseRejectionTrackingOptions').default, + ); + } +} else { + polyfillGlobal('Promise', () => require('../Promise')); +} diff --git a/Libraries/Promise.js b/Libraries/Promise.js index 836c2978a73065..fc06e9f35db06d 100644 --- a/Libraries/Promise.js +++ b/Libraries/Promise.js @@ -16,42 +16,9 @@ require('promise/setimmediate/done'); require('promise/setimmediate/finally'); if (__DEV__) { - require('promise/setimmediate/rejection-tracking').enable({ - allRejections: true, - onUnhandled: (id, rejection = {}) => { - let message: string; - let stack: ?string; - - const stringValue = Object.prototype.toString.call(rejection); - if (stringValue === '[object Error]') { - message = Error.prototype.toString.call(rejection); - const error: Error = (rejection: $FlowFixMe); - stack = error.stack; - } else { - try { - message = require('pretty-format')(rejection); - } catch { - message = - typeof rejection === 'string' - ? rejection - : JSON.stringify((rejection: $FlowFixMe)); - } - } - - const warning = - `Possible Unhandled Promise Rejection (id: ${id}):\n` + - `${message ?? ''}\n` + - (stack == null ? '' : stack); - console.warn(warning); - }, - onHandled: id => { - const warning = - `Promise Rejection Handled (id: ${id})\n` + - 'This means you can ignore any previous messages of the form ' + - `"Possible Unhandled Promise Rejection (id: ${id}):"`; - console.warn(warning); - }, - }); + require('promise/setimmediate/rejection-tracking').enable( + require('./promiseRejectionTrackingOptions').default, + ); } module.exports = Promise; diff --git a/Libraries/promiseRejectionTrackingOptions.js b/Libraries/promiseRejectionTrackingOptions.js new file mode 100644 index 00000000000000..7b30a9c66bb4bd --- /dev/null +++ b/Libraries/promiseRejectionTrackingOptions.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict + */ + +'use strict'; + +import typeof {enable} from 'promise/setimmediate/rejection-tracking'; + +type ExtractOptionsType =

((options?: ?P) => void) => P; + +let rejectionTrackingOptions: $Call = { + allRejections: true, + onUnhandled: (id, rejection = {}) => { + let message: string; + let stack: ?string; + + const stringValue = Object.prototype.toString.call(rejection); + if (stringValue === '[object Error]') { + message = Error.prototype.toString.call(rejection); + const error: Error = (rejection: $FlowFixMe); + stack = error.stack; + } else { + try { + message = require('pretty-format')(rejection); + } catch { + message = + typeof rejection === 'string' + ? rejection + : JSON.stringify((rejection: $FlowFixMe)); + } + } + + const warning = + `Possible Unhandled Promise Rejection (id: ${id}):\n` + + `${message ?? ''}\n` + + (stack == null ? '' : stack); + console.warn(warning); + }, + onHandled: id => { + const warning = + `Promise Rejection Handled (id: ${id})\n` + + 'This means you can ignore any previous messages of the form ' + + `"Possible Unhandled Promise Rejection (id: ${id}):"`; + console.warn(warning); + }, +}; + +export default rejectionTrackingOptions;