From ba387b91d3c7c9c1acd4b08f07fcd45629f3edfb Mon Sep 17 00:00:00 2001 From: Maxime Bertheau Date: Wed, 9 Jun 2021 11:26:46 -0700 Subject: [PATCH] Fix `setSnapToOffsets` crashing on Android if `snapToOffsets` is null (#31681) Summary: Fixes https://github.com/facebook/react-native/issues/30502. This PR fixes a crash caused by a `NullPointerException` when updating the `snapToOffsets` property. I noticed this crash on production, you'll find the stack trace below. Cause: `snapToOffsets` is annotated `Nullable` but there are no nullability check before calling `snapToOffsets.size()`. ## Changelog [Android] [Fixed] - Fixed a crash when updating `snapToOffsets` to a null value Pull Request resolved: https://github.com/facebook/react-native/pull/31681 Test Plan: Passing `snapToOffsets={undefined}` to a `FlatList` used to crash on Android. Not anymore. ## Stacktrace ``` com.facebook.react.bridge.JSApplicationIllegalArgumentException: Error while updating property 'snapToOffsets' of a view managed by: RCTScrollView at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:102) at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:136) at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:56) at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:49) at com.facebook.react.uimanager.NativeViewHierarchyManager.updateProperties(NativeViewHierarchyManager.java:143) at com.facebook.react.uimanager.UIViewOperationQueue$UpdatePropertiesOperation.execute(UIViewOperationQueue.java:93) at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:908) at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:1019) at com.facebook.react.uimanager.UIViewOperationQueue.access$2600(UIViewOperationQueue.java:47) at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:1079) at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29) at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:175) at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:85) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1008) at android.view.Choreographer.doCallbacks(Choreographer.java:809) at android.view.Choreographer.doFrame(Choreographer.java:740) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8512) at java.lang.reflect.Method.invoke(Method.java:-2) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130) Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Method.invoke(Method.java:-2) at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:87) at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:136) at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:56) at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:49) at com.facebook.react.uimanager.NativeViewHierarchyManager.updateProperties(NativeViewHierarchyManager.java:143) at com.facebook.react.uimanager.UIViewOperationQueue$UpdatePropertiesOperation.execute(UIViewOperationQueue.java:93) at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:908) at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:1019) at com.facebook.react.uimanager.UIViewOperationQueue.access$2600(UIViewOperationQueue.java:47) at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:1079) at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29) at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:175) at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:85) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1008) at android.view.Choreographer.doCallbacks(Choreographer.java:809) at android.view.Choreographer.doFrame(Choreographer.java:740) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8512) at java.lang.reflect.Method.invoke(Method.java:-2) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130) Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int com.facebook.react.bridge.ReadableArray.size()' on a null object reference at com.facebook.react.views.scroll.ReactScrollViewManager.setSnapToOffsets(ReactScrollViewManager.java:107) at java.lang.reflect.Method.invoke(Method.java:-2) at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:87) at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:136) at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:56) at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:49) at com.facebook.react.uimanager.NativeViewHierarchyManager.updateProperties(NativeViewHierarchyManager.java:143) at com.facebook.react.uimanager.UIViewOperationQueue$UpdatePropertiesOperation.execute(UIViewOperationQueue.java:93) at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:908) at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:1019) at com.facebook.react.uimanager.UIViewOperationQueue.access$2600(UIViewOperationQueue.java:47) at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:1079) at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29) at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:175) at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:85) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1008) at android.view.Choreographer.doCallbacks(Choreographer.java:809) at android.view.Choreographer.doFrame(Choreographer.java:740) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8512) at java.lang.reflect.Method.invoke(Method.java:-2) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130) ``` Reviewed By: ShikaSD Differential Revision: D28965864 Pulled By: JoshuaGross fbshipit-source-id: f08f1df371c1892935e1290806cc67d34394203b --- .../react/views/scroll/ReactHorizontalScrollViewManager.java | 5 +++++ .../facebook/react/views/scroll/ReactScrollViewManager.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index 38f025fbb60..05112f45e44 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -105,6 +105,11 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager offsets = new ArrayList(); for (int i = 0; i < snapToOffsets.size(); i++) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index c7eed9b6e28..8c6b6070a80 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -102,6 +102,11 @@ public class ReactScrollViewManager extends ViewGroupManager @ReactProp(name = "snapToOffsets") public void setSnapToOffsets(ReactScrollView view, @Nullable ReadableArray snapToOffsets) { + if (snapToOffsets == null) { + view.setSnapOffsets(null); + return; + } + DisplayMetrics screenDisplayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics(); List offsets = new ArrayList(); for (int i = 0; i < snapToOffsets.size(); i++) {