@samitouri / QOS-React / commits / 31ea0aa6d7

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

Dan Abramov committed Dec 2, 2017 at 00:00 UTC 31ea0aa6d701df811fb586fa7545a376e5135440
2 files changed +80 -4
packages/react-reconciler/src/ReactFiber.js
+7 -4
@@ -41,10 +41,13 @@ if (__DEV__) {
41 var hasBadMapPolyfill = false;
42 try {
43 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);
51 } catch (e) {
52 // TODO: Consider warning about bad polyfills
53 hasBadMapPolyfill = true;
packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+73
@@ -2715,4 +2715,77 @@ describe('ReactIncremental', () => {
2715
2716 expect(ReactNoop.flush()).toEqual([]);
2717 });
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 + });
2791 });