Skip to content

Commit 31ea0aa

Browse files
authored
Add a test for bad Map polyfill, and work around Rollup bug (#11745)
* Add a test for bad Map polyfill * Add a workaround for the Rollup bug * Add a link to the bug URL
1 parent 8540768 commit 31ea0aa

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

packages/react-reconciler/src/ReactFiber.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,13 @@ if (__DEV__) {
4141
var hasBadMapPolyfill = false;
4242
try {
4343
var nonExtensibleObject = Object.preventExtensions({});
44-
/* eslint-disable no-new */
45-
new Map([[nonExtensibleObject, null]]);
46-
new Set([nonExtensibleObject]);
47-
/* eslint-enable no-new */
44+
var testMap = new Map([[nonExtensibleObject, null]]);
45+
var testSet = new Set([nonExtensibleObject]);
46+
// This is necessary for Rollup to not consider these unused.
47+
// https://github.com/rollup/rollup/issues/1771
48+
// TODO: we can remove these if Rollup fixes the bug.
49+
testMap.set(0, 0);
50+
testSet.add(0);
4851
} catch (e) {
4952
// TODO: Consider warning about bad polyfills
5053
hasBadMapPolyfill = true;

packages/react-reconciler/src/__tests__/ReactIncremental-test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2715,4 +2715,77 @@ describe('ReactIncremental', () => {
27152715

27162716
expect(ReactNoop.flush()).toEqual([]);
27172717
});
2718+
2719+
it('does not break with a bad Map polyfill', () => {
2720+
const realMapSet = Map.prototype.set;
2721+
2722+
function triggerCodePathThatUsesFibersAsMapKeys() {
2723+
function Thing() {
2724+
throw new Error('No.');
2725+
}
2726+
class Boundary extends React.Component {
2727+
state = {didError: false};
2728+
componentDidCatch() {
2729+
this.setState({didError: true});
2730+
}
2731+
render() {
2732+
return this.state.didError ? null : <Thing />;
2733+
}
2734+
}
2735+
ReactNoop.render(<Boundary />);
2736+
ReactNoop.flush();
2737+
}
2738+
2739+
// First, verify that this code path normally receives Fibers as keys,
2740+
// and that they're not extensible.
2741+
jest.resetModules();
2742+
let receivedNonExtensibleObjects;
2743+
// eslint-disable-next-line no-extend-native
2744+
Map.prototype.set = function(key) {
2745+
if (typeof key === 'object' && key !== null) {
2746+
if (!Object.isExtensible(key)) {
2747+
receivedNonExtensibleObjects = true;
2748+
}
2749+
}
2750+
return realMapSet.apply(this, arguments);
2751+
};
2752+
React = require('react');
2753+
ReactNoop = require('react-noop-renderer');
2754+
try {
2755+
receivedNonExtensibleObjects = false;
2756+
triggerCodePathThatUsesFibersAsMapKeys();
2757+
} finally {
2758+
// eslint-disable-next-line no-extend-native
2759+
Map.prototype.set = realMapSet;
2760+
}
2761+
// If this fails, find another code path in Fiber
2762+
// that passes Fibers as keys to Maps.
2763+
// Note that we only expect them to be non-extensible
2764+
// in development.
2765+
expect(receivedNonExtensibleObjects).toBe(__DEV__);
2766+
2767+
// Next, verify that a Map polyfill that "writes" to keys
2768+
// doesn't cause a failure.
2769+
jest.resetModules();
2770+
// eslint-disable-next-line no-extend-native
2771+
Map.prototype.set = function(key, value) {
2772+
if (typeof key === 'object' && key !== null) {
2773+
// A polyfill could do something like this.
2774+
// It would throw if an object is not extensible.
2775+
key.__internalValueSlot = value;
2776+
}
2777+
return realMapSet.apply(this, arguments);
2778+
};
2779+
React = require('react');
2780+
ReactNoop = require('react-noop-renderer');
2781+
try {
2782+
triggerCodePathThatUsesFibersAsMapKeys();
2783+
} finally {
2784+
// eslint-disable-next-line no-extend-native
2785+
Map.prototype.set = realMapSet;
2786+
}
2787+
// If we got this far, our feature detection worked.
2788+
// We knew that Map#set() throws for non-extensible objects,
2789+
// so we didn't set them as non-extensible for that reason.
2790+
});
27182791
});

0 commit comments

Comments
 (0)