Skip to content

Commit 1aac962

Browse files
mdvaccafacebook-github-bot
authored andcommitted
Capture StackOverflowExceptions triggered when drawing a ReactViewGroup or ReactRootView
Reviewed By: achen1 Differential Revision: D6653395 fbshipit-source-id: 849b1a2ed6ab9bc057414d451e97a673178c30dd
1 parent 877f1cd commit 1aac962

File tree

6 files changed

+105
-48
lines changed

6 files changed

+105
-48
lines changed

ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java

+22
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
1313

1414
import android.content.Context;
15+
import android.graphics.Canvas;
1516
import android.graphics.Rect;
1617
import android.os.Build;
1718
import android.os.Bundle;
@@ -40,6 +41,7 @@
4041
import com.facebook.react.modules.core.DeviceEventManagerModule;
4142
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
4243
import com.facebook.react.uimanager.DisplayMetricsHolder;
44+
import com.facebook.react.uimanager.IllegalViewOperationException;
4345
import com.facebook.react.uimanager.JSTouchDispatcher;
4446
import com.facebook.react.uimanager.MeasureSpecProvider;
4547
import com.facebook.react.uimanager.PixelUtil;
@@ -201,6 +203,17 @@ public boolean onTouchEvent(MotionEvent ev) {
201203
return true;
202204
}
203205

206+
@Override
207+
protected void dispatchDraw(Canvas canvas) {
208+
try {
209+
super.dispatchDraw(canvas);
210+
} catch (StackOverflowError e) {
211+
// Adding special exception management for StackOverflowError for logging purposes.
212+
// This will be removed in the future.
213+
handleException(new IllegalViewOperationException("StackOverflowError", e));
214+
}
215+
}
216+
204217
private void dispatchJSTouchEvent(MotionEvent event) {
205218
if (mReactInstanceManager == null || !mIsAttachedToInstance ||
206219
mReactInstanceManager.getCurrentReactContext() == null) {
@@ -496,6 +509,15 @@ public void setRootViewTag(int rootViewTag) {
496509
mRootViewTag = rootViewTag;
497510
}
498511

512+
@Override
513+
public void handleException(Exception e) {
514+
if (mReactInstanceManager != null && mReactInstanceManager.getCurrentReactContext() != null) {
515+
mReactInstanceManager.getCurrentReactContext().handleException(e);
516+
} else {
517+
throw new RuntimeException(e);
518+
}
519+
}
520+
499521
@Nullable
500522
public ReactInstanceManager getReactInstanceManager() {
501523
return mReactInstanceManager;

ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,13 @@ public void runOnJSQueueThread(Runnable runnable) {
304304
* {@link com.facebook.react.bridge.NativeModuleCallExceptionHandler} if one exists, rethrowing
305305
* otherwise.
306306
*/
307-
public void handleException(RuntimeException e) {
307+
public void handleException(Exception e) {
308308
if (mCatalystInstance != null &&
309309
!mCatalystInstance.isDestroyed() &&
310310
mNativeModuleCallExceptionHandler != null) {
311311
mNativeModuleCallExceptionHandler.handleException(e);
312312
} else {
313-
throw e;
313+
throw new RuntimeException(e);
314314
}
315315
}
316316

ReactAndroid/src/main/java/com/facebook/react/uimanager/IllegalViewOperationException.java

+4
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ public class IllegalViewOperationException extends JSApplicationCausedNativeExce
1919
public IllegalViewOperationException(String msg) {
2020
super(msg);
2121
}
22+
23+
public IllegalViewOperationException(String msg, Throwable cause) {
24+
super(msg, cause);
25+
}
2226
}

ReactAndroid/src/main/java/com/facebook/react/uimanager/RootView.java

+2
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ public interface RootView {
2121
* from the child's onTouchIntercepted implementation.
2222
*/
2323
void onChildStartedNativeGesture(MotionEvent androidEvent);
24+
25+
void handleException(Exception e);
2426
}

ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -315,18 +315,27 @@ protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
315315
super.onSizeChanged(w, h, oldw, oldh);
316316
if (getChildCount() > 0) {
317317
final int viewTag = getChildAt(0).getId();
318-
ReactContext reactContext = (ReactContext) getContext();
318+
ReactContext reactContext = getReactContext();
319319
reactContext.runOnNativeModulesQueueThread(
320320
new GuardedRunnable(reactContext) {
321321
@Override
322322
public void runGuarded() {
323-
((ReactContext) getContext()).getNativeModule(UIManagerModule.class)
323+
(getReactContext()).getNativeModule(UIManagerModule.class)
324324
.updateNodeSize(viewTag, w, h);
325325
}
326326
});
327327
}
328328
}
329329

330+
@Override
331+
public void handleException(Exception e) {
332+
getReactContext().handleException(e);
333+
}
334+
335+
private ReactContext getReactContext() {
336+
return (ReactContext) getContext();
337+
}
338+
330339
@Override
331340
public boolean onInterceptTouchEvent(MotionEvent event) {
332341
mJSTouchDispatcher.handleTouchEvent(event, getEventDispatcher());
@@ -354,7 +363,7 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
354363
}
355364

356365
private EventDispatcher getEventDispatcher() {
357-
ReactContext reactContext = (ReactContext) getContext();
366+
ReactContext reactContext = getReactContext();
358367
return reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
359368
}
360369
}

ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java

+63-43
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@
2828
import com.facebook.react.touch.OnInterceptTouchEventListener;
2929
import com.facebook.react.touch.ReactHitSlopView;
3030
import com.facebook.react.touch.ReactInterceptingViewGroup;
31+
import com.facebook.react.uimanager.IllegalViewOperationException;
3132
import com.facebook.react.uimanager.MeasureSpecAssertions;
3233
import com.facebook.react.uimanager.PointerEvents;
3334
import com.facebook.react.uimanager.ReactClippingViewGroup;
3435
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
3536
import com.facebook.react.uimanager.ReactPointerEventsView;
3637
import com.facebook.react.uimanager.ReactZIndexedViewGroup;
38+
import com.facebook.react.uimanager.RootView;
39+
import com.facebook.react.uimanager.RootViewUtil;
3740
import com.facebook.react.uimanager.ViewGroupDrawingOrderHelper;
3841
import com.facebook.yoga.YogaConstants;
3942
import javax.annotation.Nullable;
@@ -657,6 +660,24 @@ private void updateBackgroundDrawable(Drawable drawable) {
657660

658661
@Override
659662
protected void dispatchDraw(Canvas canvas) {
663+
try {
664+
dispatchOverflowDraw(canvas);
665+
super.dispatchDraw(canvas);
666+
} catch (StackOverflowError e) {
667+
// Adding special exception management for StackOverflowError for logging purposes.
668+
// This will be removed in the future.
669+
RootView rootView = RootViewUtil.getRootView(ReactViewGroup.this);
670+
IllegalViewOperationException wrappedException =
671+
new IllegalViewOperationException("StackOverflowError", e);
672+
if (rootView != null) {
673+
rootView.handleException(wrappedException);
674+
} else {
675+
throw wrappedException;
676+
}
677+
}
678+
}
679+
680+
private void dispatchOverflowDraw(Canvas canvas) {
660681
if (mOverflow != null) {
661682
switch (mOverflow) {
662683
case "visible":
@@ -674,9 +695,9 @@ protected void dispatchDraw(Canvas canvas) {
674695
final RectF borderWidth = mReactBackgroundDrawable.getDirectionAwareBorderInsets();
675696

676697
if (borderWidth.top > 0
677-
|| borderWidth.left > 0
678-
|| borderWidth.bottom > 0
679-
|| borderWidth.right > 0) {
698+
|| borderWidth.left > 0
699+
|| borderWidth.bottom > 0
700+
|| borderWidth.right > 0) {
680701
left += borderWidth.left;
681702
top += borderWidth.top;
682703
right -= borderWidth.right;
@@ -685,32 +706,32 @@ protected void dispatchDraw(Canvas canvas) {
685706

686707
final float borderRadius = mReactBackgroundDrawable.getFullBorderRadius();
687708
float topLeftBorderRadius =
688-
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
689-
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_LEFT);
709+
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
710+
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_LEFT);
690711
float topRightBorderRadius =
691-
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
692-
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_RIGHT);
712+
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
713+
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_RIGHT);
693714
float bottomLeftBorderRadius =
694-
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
695-
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_LEFT);
715+
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
716+
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_LEFT);
696717
float bottomRightBorderRadius =
697-
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
698-
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_RIGHT);
718+
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
719+
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_RIGHT);
699720

700721
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
701722
final boolean isRTL = mLayoutDirection == View.LAYOUT_DIRECTION_RTL;
702723
float topStartBorderRadius =
703-
mReactBackgroundDrawable.getBorderRadius(
704-
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_START);
724+
mReactBackgroundDrawable.getBorderRadius(
725+
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_START);
705726
float topEndBorderRadius =
706-
mReactBackgroundDrawable.getBorderRadius(
707-
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_END);
727+
mReactBackgroundDrawable.getBorderRadius(
728+
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_END);
708729
float bottomStartBorderRadius =
709-
mReactBackgroundDrawable.getBorderRadius(
710-
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_START);
730+
mReactBackgroundDrawable.getBorderRadius(
731+
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_START);
711732
float bottomEndBorderRadius =
712-
mReactBackgroundDrawable.getBorderRadius(
713-
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_END);
733+
mReactBackgroundDrawable.getBorderRadius(
734+
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_END);
714735

715736
if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(getContext())) {
716737
if (YogaConstants.isUndefined(topStartBorderRadius)) {
@@ -730,27 +751,27 @@ protected void dispatchDraw(Canvas canvas) {
730751
}
731752

732753
final float directionAwareTopLeftRadius =
733-
isRTL ? topEndBorderRadius : topStartBorderRadius;
754+
isRTL ? topEndBorderRadius : topStartBorderRadius;
734755
final float directionAwareTopRightRadius =
735-
isRTL ? topStartBorderRadius : topEndBorderRadius;
756+
isRTL ? topStartBorderRadius : topEndBorderRadius;
736757
final float directionAwareBottomLeftRadius =
737-
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
758+
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
738759
final float directionAwareBottomRightRadius =
739-
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
760+
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
740761

741762
topLeftBorderRadius = directionAwareTopLeftRadius;
742763
topRightBorderRadius = directionAwareTopRightRadius;
743764
bottomLeftBorderRadius = directionAwareBottomLeftRadius;
744765
bottomRightBorderRadius = directionAwareBottomRightRadius;
745766
} else {
746767
final float directionAwareTopLeftRadius =
747-
isRTL ? topEndBorderRadius : topStartBorderRadius;
768+
isRTL ? topEndBorderRadius : topStartBorderRadius;
748769
final float directionAwareTopRightRadius =
749-
isRTL ? topStartBorderRadius : topEndBorderRadius;
770+
isRTL ? topStartBorderRadius : topEndBorderRadius;
750771
final float directionAwareBottomLeftRadius =
751-
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
772+
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
752773
final float directionAwareBottomRightRadius =
753-
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
774+
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
754775

755776
if (!YogaConstants.isUndefined(directionAwareTopLeftRadius)) {
756777
topLeftBorderRadius = directionAwareTopLeftRadius;
@@ -771,27 +792,27 @@ protected void dispatchDraw(Canvas canvas) {
771792
}
772793

773794
if (topLeftBorderRadius > 0
774-
|| topRightBorderRadius > 0
775-
|| bottomRightBorderRadius > 0
776-
|| bottomLeftBorderRadius > 0) {
795+
|| topRightBorderRadius > 0
796+
|| bottomRightBorderRadius > 0
797+
|| bottomLeftBorderRadius > 0) {
777798
if (mPath == null) {
778799
mPath = new Path();
779800
}
780801

781802
mPath.rewind();
782803
mPath.addRoundRect(
783-
new RectF(left, top, right, bottom),
784-
new float[] {
785-
Math.max(topLeftBorderRadius - borderWidth.left, 0),
786-
Math.max(topLeftBorderRadius - borderWidth.top, 0),
787-
Math.max(topRightBorderRadius - borderWidth.right, 0),
788-
Math.max(topRightBorderRadius - borderWidth.top, 0),
789-
Math.max(bottomRightBorderRadius - borderWidth.right, 0),
790-
Math.max(bottomRightBorderRadius - borderWidth.bottom, 0),
791-
Math.max(bottomLeftBorderRadius - borderWidth.left, 0),
792-
Math.max(bottomLeftBorderRadius - borderWidth.bottom, 0),
793-
},
794-
Path.Direction.CW);
804+
new RectF(left, top, right, bottom),
805+
new float[]{
806+
Math.max(topLeftBorderRadius - borderWidth.left, 0),
807+
Math.max(topLeftBorderRadius - borderWidth.top, 0),
808+
Math.max(topRightBorderRadius - borderWidth.right, 0),
809+
Math.max(topRightBorderRadius - borderWidth.top, 0),
810+
Math.max(bottomRightBorderRadius - borderWidth.right, 0),
811+
Math.max(bottomRightBorderRadius - borderWidth.bottom, 0),
812+
Math.max(bottomLeftBorderRadius - borderWidth.left, 0),
813+
Math.max(bottomLeftBorderRadius - borderWidth.bottom, 0),
814+
},
815+
Path.Direction.CW);
795816
canvas.clipPath(mPath);
796817
} else {
797818
canvas.clipRect(new RectF(left, top, right, bottom));
@@ -802,6 +823,5 @@ protected void dispatchDraw(Canvas canvas) {
802823
break;
803824
}
804825
}
805-
super.dispatchDraw(canvas);
806826
}
807827
}

0 commit comments

Comments
 (0)