lib/InternalBytecode/01-Promise.js (400 lines of code) (raw):

/* @nolint */var initPromise = (function () { 'use strict'; var useEngineQueue = HermesInternal.useEngineQueue(); function noop() {} // States: // // 0 - pending // 1 - fulfilled with _value // 2 - rejected with _value // 3 - adopted the state of another promise, _value // // once the state is no longer pending (0) it is immutable // All `_` prefixed properties will be reduced to `_{random number}` // at build time to obfuscate them and discourage their use. // We don't use symbols or Object.defineProperty to fully hide them // because the performance isn't good enough. // to avoid using try/catch inside critical functions, we // extract them to here. var LAST_ERROR = null; var IS_ERROR = {}; function getThen(obj) { try { return obj.then; } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } function tryCallOne(fn, a) { try { return fn(a); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } function tryCallTwo(fn, a, b) { try { fn(a, b); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } var core = Promise; function Promise(fn) { if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') { throw new TypeError('Promise constructor\'s argument is not a function'); } this._h = 0; this._i = 0; this._j = null; this._k = null; if (fn === noop) return; doResolve(fn, this); } Promise._l = null; Promise._m = null; Promise._n = noop; Promise.prototype.then = function(onFulfilled, onRejected) { if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } var res = new Promise(noop); handle(this, new Handler(onFulfilled, onRejected, res)); return res; }; function safeThen(self, onFulfilled, onRejected) { return new self.constructor(function (resolve, reject) { var res = new Promise(noop); res.then(resolve, reject); handle(self, new Handler(onFulfilled, onRejected, res)); }); } function handle(self, deferred) { while (self._i === 3) { self = self._j; } if (Promise._l) { Promise._l(self); } if (self._i === 0) { if (self._h === 0) { self._h = 1; self._k = deferred; return; } if (self._h === 1) { self._h = 2; self._k = [self._k, deferred]; return; } self._k.push(deferred); return; } handleResolved(self, deferred); } function handleResolved(self, deferred) { (useEngineQueue ? HermesInternal.enqueueJob : setImmediate)(function() { var cb = self._i === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { if (self._i === 1) { resolve(deferred.promise, self._j); } else { reject(deferred.promise, self._j); } return; } var ret = tryCallOne(cb, self._j); if (ret === IS_ERROR) { reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); } function resolve(self, newValue) { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) { return reject( self, new TypeError('A promise cannot be resolved with itself.') ); } if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = getThen(newValue); if (then === IS_ERROR) { return reject(self, LAST_ERROR); } if ( then === self.then && newValue instanceof Promise ) { self._i = 3; self._j = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(then.bind(newValue), self); return; } } self._i = 1; self._j = newValue; finale(self); } function reject(self, newValue) { self._i = 2; self._j = newValue; if (Promise._m) { Promise._m(self, newValue); } finale(self); } function finale(self) { if (self._h === 1) { handle(self, self._k); self._k = null; } if (self._h === 2) { for (var i = 0; i < self._k.length; i++) { handle(self, self._k[i]); } self._k = null; } } function Handler(onFulfilled, onRejected, promise){ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, promise) { var done = false; var res = tryCallTwo(fn, function (value) { if (done) return; done = true; resolve(promise, value); }, function (reason) { if (done) return; done = true; reject(promise, reason); }); if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } } //This file contains the ES6 extensions to the core Promises/A+ API var es6Extensions = core; /* Static Functions */ var TRUE = valuePromise(true); var FALSE = valuePromise(false); var NULL = valuePromise(null); var UNDEFINED = valuePromise(undefined); var ZERO = valuePromise(0); var EMPTYSTRING = valuePromise(''); function valuePromise(value) { var p = new core(core._n); p._i = 1; p._j = value; return p; } core.resolve = function (value) { if (value instanceof core) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === '') return EMPTYSTRING; if (typeof value === 'object' || typeof value === 'function') { try { var then = value.then; if (typeof then === 'function') { return new core(then.bind(value)); } } catch (ex) { return new core(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); }; core.all = function (arr) { var args = Array.prototype.slice.call(arr); return new core(function (resolve, reject) { if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { if (val instanceof core && val.then === core.prototype.then) { while (val._i === 3) { val = val._j; } if (val._i === 1) return res(i, val._j); if (val._i === 2) reject(val._j); val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === 'function') { var p = new core(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; core.reject = function (value) { return new core(function (resolve, reject) { reject(value); }); }; core.race = function (values) { return new core(function (resolve, reject) { values.forEach(function(value){ core.resolve(value).then(resolve, reject); }); }); }; /* Prototype Methods */ core.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); }; core.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this; self.then(null, function (err) { setTimeout(function () { throw err; }, 0); }); }; core.prototype.finally = function (f) { return this.then(function (value) { return core.resolve(f()).then(function () { return value; }); }, function (err) { return core.resolve(f()).then(function () { throw err; }); }); }; var DEFAULT_WHITELIST = [ ReferenceError, TypeError, RangeError ]; var enabled = false; var disable_1 = disable; function disable() { enabled = false; core._l = null; core._m = null; } var enable_1 = enable; function enable(options) { options = options || {}; if (enabled) disable(); enabled = true; var id = 0; var displayId = 0; var rejections = {}; core._l = function (promise) { if ( promise._i === 2 && // IS REJECTED rejections[promise._o] ) { if (rejections[promise._o].logged) { onHandled(promise._o); } else { clearTimeout(rejections[promise._o].timeout); } delete rejections[promise._o]; } }; core._m = function (promise, err) { if (promise._h === 0) { // not yet handled promise._o = id++; rejections[promise._o] = { displayId: null, error: err, timeout: setTimeout( onUnhandled.bind(null, promise._o), // For reference errors and type errors, this almost always // means the programmer made a mistake, so log them after just // 100ms // otherwise, wait 2 seconds to see if they get handled matchWhitelist(err, DEFAULT_WHITELIST) ? 100 : 2000 ), logged: false }; } }; function onUnhandled(id) { if ( options.allRejections || matchWhitelist( rejections[id].error, options.whitelist || DEFAULT_WHITELIST ) ) { rejections[id].displayId = displayId++; if (options.onUnhandled) { rejections[id].logged = true; options.onUnhandled( rejections[id].displayId, rejections[id].error ); } else { rejections[id].logged = true; logError( rejections[id].displayId, rejections[id].error ); } } } function onHandled(id) { if (rejections[id].logged) { if (options.onHandled) { options.onHandled(rejections[id].displayId, rejections[id].error); } else if (!rejections[id].onUnhandled) { console.warn( 'Promise Rejection Handled (id: ' + rejections[id].displayId + '):' ); console.warn( ' This means you can ignore any previous messages of the form "Possible Unhandled Promise Rejection" with id ' + rejections[id].displayId + '.' ); } } } } function logError(id, error) { console.warn('Possible Unhandled Promise Rejection (id: ' + id + '):'); var errStr = (error && (error.stack || error)) + ''; errStr.split('\n').forEach(function (line) { console.warn(' ' + line); }); } function matchWhitelist(error, list) { return list.some(function (cls) { return error instanceof cls; }); } var rejectionTracking = { disable: disable_1, enable: enable_1 }; // @nolint // This file is used to generate InternalBytecode/Promise.js // See InternalBytecode/README.md for more details. // expose Promise to global. globalThis.Promise = es6Extensions; // register the JavaScript implemented `enable` function into // the Hermes' internal promise rejection tracker. var enableHook = rejectionTracking.enable; HermesInternal?.setPromiseRejectionTrackingHook?.(enableHook); var promise = { }; return promise; }); if (HermesInternal?.hasPromise?.()) { initPromise(); }