@@ -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