@@ -2715,4 +2715,77 @@ describe('ReactIncremental', () => {
2715
2715
2716
2716
expect ( ReactNoop . flush ( ) ) . toEqual ( [ ] ) ;
2717
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
+ } ) ;
2718
2791
} ) ;
0 commit comments