diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js
index 941247574f..257dcd65a2 100644
--- a/packages/react-reconciler/src/ReactFiber.js
+++ b/packages/react-reconciler/src/ReactFiber.js
@@ -41,10 +41,13 @@ if (__DEV__) {
var hasBadMapPolyfill = false;
try {
var nonExtensibleObject = Object.preventExtensions({});
- /* eslint-disable no-new */
- new Map([[nonExtensibleObject, null]]);
- new Set([nonExtensibleObject]);
- /* eslint-enable no-new */
+ var testMap = new Map([[nonExtensibleObject, null]]);
+ var testSet = new Set([nonExtensibleObject]);
+ // This is necessary for Rollup to not consider these unused.
+ // https://github.com/rollup/rollup/issues/1771
+ // TODO: we can remove these if Rollup fixes the bug.
+ testMap.set(0, 0);
+ testSet.add(0);
} catch (e) {
// TODO: Consider warning about bad polyfills
hasBadMapPolyfill = true;
diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
index ce98b306cd..72ba93fc09 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
@@ -2715,4 +2715,77 @@ describe('ReactIncremental', () => {
expect(ReactNoop.flush()).toEqual([]);
});
+
+ it('does not break with a bad Map polyfill', () => {
+ const realMapSet = Map.prototype.set;
+
+ function triggerCodePathThatUsesFibersAsMapKeys() {
+ function Thing() {
+ throw new Error('No.');
+ }
+ class Boundary extends React.Component {
+ state = {didError: false};
+ componentDidCatch() {
+ this.setState({didError: true});
+ }
+ render() {
+ return this.state.didError ? null : ;
+ }
+ }
+ ReactNoop.render();
+ ReactNoop.flush();
+ }
+
+ // First, verify that this code path normally receives Fibers as keys,
+ // and that they're not extensible.
+ jest.resetModules();
+ let receivedNonExtensibleObjects;
+ // eslint-disable-next-line no-extend-native
+ Map.prototype.set = function(key) {
+ if (typeof key === 'object' && key !== null) {
+ if (!Object.isExtensible(key)) {
+ receivedNonExtensibleObjects = true;
+ }
+ }
+ return realMapSet.apply(this, arguments);
+ };
+ React = require('react');
+ ReactNoop = require('react-noop-renderer');
+ try {
+ receivedNonExtensibleObjects = false;
+ triggerCodePathThatUsesFibersAsMapKeys();
+ } finally {
+ // eslint-disable-next-line no-extend-native
+ Map.prototype.set = realMapSet;
+ }
+ // If this fails, find another code path in Fiber
+ // that passes Fibers as keys to Maps.
+ // Note that we only expect them to be non-extensible
+ // in development.
+ expect(receivedNonExtensibleObjects).toBe(__DEV__);
+
+ // Next, verify that a Map polyfill that "writes" to keys
+ // doesn't cause a failure.
+ jest.resetModules();
+ // eslint-disable-next-line no-extend-native
+ Map.prototype.set = function(key, value) {
+ if (typeof key === 'object' && key !== null) {
+ // A polyfill could do something like this.
+ // It would throw if an object is not extensible.
+ key.__internalValueSlot = value;
+ }
+ return realMapSet.apply(this, arguments);
+ };
+ React = require('react');
+ ReactNoop = require('react-noop-renderer');
+ try {
+ triggerCodePathThatUsesFibersAsMapKeys();
+ } finally {
+ // eslint-disable-next-line no-extend-native
+ Map.prototype.set = realMapSet;
+ }
+ // If we got this far, our feature detection worked.
+ // We knew that Map#set() throws for non-extensible objects,
+ // so we didn't set them as non-extensible for that reason.
+ });
});