From 2a7e02edf64c20410b2f95f35e313279545b40db Mon Sep 17 00:00:00 2001 From: Rafael Oleza Date: Mon, 22 Oct 2018 07:27:00 -0700 Subject: [PATCH] Fix polyfilling of regeneratorRuntime to avoid setting it to undefined in some situations Summary: This diff fixes an issue that caused the problem with `regeneratorRuntime` last Friday (more info: https://fb.facebook.com/groups/frontendsupport/permalink/2427883350560427/). The root issue is that both `InitializeCore` and `FBInitializeCore` are included in the same bundle, this fix just prevents the bundle from being invalid once this happens.. *copied from: https://our.intern.facebook.com/intern/diff/D10444264/?transaction_id=595485237532887* The way that `regeneratorRuntime` is polyfilled is not correct: ``` polyfillGlobal('regeneratorRuntime', () => { // The require just sets up the global, so make sure when we first // invoke it the global does not exist delete global.regeneratorRuntime; require('regenerator-runtime/runtime'); return global.regeneratorRuntime; }); ``` Since a `require`d module is only evaluated once (no matter how many times it's required), defining (and calling) a getter for `global.regeneratorRuntime` twice will end up storing `undefined` to `global.regeneratorRuntime`. There were no issues before this diff because the ordering of requires made things work by coincidence, e.g the following code will work: ``` // Set up regenerator for the first time polyfillGlobal('regeneratorRuntime', () => { delete global.regeneratorRuntime; require('regenerator-runtime/runtime'); return global.regeneratorRuntime; }); // Set up regenerator for the second time. This will just override the previous getter (which has not even got executed so // the `regenerator-runtime/runtime` module has not been evaluated yet. polyfillGlobal('regeneratorRuntime', () => { // The require just sets up the global, so make sure when we first // invoke it the global does not exist delete global.regeneratorRuntime; require('regenerator-runtime/runtime'); return global.regeneratorRuntime; }); // Now access regenerator global.regeneratorRuntime; ``` But the following code won't work: ``` // Set up regenerator for the first time polyfillGlobal('regeneratorRuntime', () => { delete global.regeneratorRuntime; require('regenerator-runtime/runtime'); return global.regeneratorRuntime; }); // Access regenerator. This will cause the previous getter to be called, and the `regenerator-runtime/runtime` module will get evaluated. // Here, `global.regeneratorRuntime` will have a correct value. global.regeneratorRuntime; // Set up regenerator for the second time. This will define a new getter for `global.regeneratorRuntime`, which will delete `delete global.regeneratorRuntime` // and return undefined (note that `require('regenerator-runtime/runtime');` is a noop since the module has been already evaluated). polyfillGlobal('regeneratorRuntime', () => { // The require just sets up the global, so make sure when we first // invoke it the global does not exist delete global.regeneratorRuntime; require('regenerator-runtime/runtime'); return global.regeneratorRuntime; }); ``` Reviewed By: fromcelticpark Differential Revision: D10483975 fbshipit-source-id: 5b3ef6e11c4fc4f79e3857c1ade9e7bc2beb6a39 --- Libraries/Core/InitializeCore.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Libraries/Core/InitializeCore.js b/Libraries/Core/InitializeCore.js index c55163981c8e4b..64af03b6b702e2 100644 --- a/Libraries/Core/InitializeCore.js +++ b/Libraries/Core/InitializeCore.js @@ -91,8 +91,10 @@ polyfillGlobal('regeneratorRuntime', () => { // The require just sets up the global, so make sure when we first // invoke it the global does not exist delete global.regeneratorRuntime; - require('regenerator-runtime/runtime'); - return global.regeneratorRuntime; + + // regenerator-runtime/runtime exports the regeneratorRuntime object, so we + // can return it safely. + return require('regenerator-runtime/runtime'); }); // Set up timers