mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
206bb6d3b9
Summary: Update the "nativeStackAndroid" frame limit to 50 and include the class name on the "nativeStackAndroid". nativeStackAndroid only contains up to 10 lines of stack traces. This is due to the "ERROR_STACK_FRAME_LIMIT" set to 10 on https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/PromiseImpl.java.  nativeStackAndroid should contain a more reasonable number of the native stack traces. (nativeStackIOS includes all of them). another improvement could be adding the "declaringClass" on top of the "methodName", "LineNumber", and "file" on the stack trace frameMap.  ## Changelog [Android] [Added] - Update the "nativeStackAndroid" frame limit to 50 and include class name Pull Request resolved: https://github.com/facebook/react-native/pull/25014 Differential Revision: D15503022 Pulled By: cpojer fbshipit-source-id: 6f1bc25ea739715d0e7589d430bf9cf72da305b2
249 lines
7.7 KiB
Java
249 lines
7.7 KiB
Java
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
package com.facebook.react.bridge;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
/*
|
|
* Implementation of {@link Promise} that represents a JavaScript Promise which can be passed to the
|
|
* native module as a method parameter.
|
|
*
|
|
* Methods annotated with {@link ReactMethod} that use a {@link Promise} as the last parameter
|
|
* will be marked as "promise" and will return a promise when invoked from JavaScript.
|
|
*/
|
|
public class PromiseImpl implements Promise {
|
|
// Number of stack frames to parse and return to mReject.invoke
|
|
// for ERROR_MAP_KEY_NATIVE_STACK
|
|
private static final int ERROR_STACK_FRAME_LIMIT = 50;
|
|
|
|
private static final String ERROR_DEFAULT_CODE = "EUNSPECIFIED";
|
|
private static final String ERROR_DEFAULT_MESSAGE = "Error not specified.";
|
|
|
|
// Keys for mReject's WritableMap
|
|
private static final String ERROR_MAP_KEY_CODE = "code";
|
|
private static final String ERROR_MAP_KEY_MESSAGE = "message";
|
|
private static final String ERROR_MAP_KEY_USER_INFO = "userInfo";
|
|
private static final String ERROR_MAP_KEY_NATIVE_STACK = "nativeStackAndroid";
|
|
|
|
// Keys for ERROR_MAP_KEY_NATIVE_STACK's StackFrame maps
|
|
private static final String STACK_FRAME_KEY_CLASS = "class";
|
|
private static final String STACK_FRAME_KEY_FILE = "file";
|
|
private static final String STACK_FRAME_KEY_LINE_NUMBER = "lineNumber";
|
|
private static final String STACK_FRAME_KEY_METHOD_NAME = "methodName";
|
|
|
|
private @Nullable
|
|
Callback mResolve;
|
|
private @Nullable
|
|
Callback mReject;
|
|
|
|
public PromiseImpl(@Nullable Callback resolve, @Nullable Callback reject) {
|
|
mResolve = resolve;
|
|
mReject = reject;
|
|
}
|
|
|
|
/**
|
|
* Successfully resolve the Promise with an optional value.
|
|
*
|
|
* @param value Object
|
|
*/
|
|
@Override
|
|
public void resolve(Object value) {
|
|
if (mResolve != null) {
|
|
mResolve.invoke(value);
|
|
mResolve = null;
|
|
mReject = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report an error without an exception using a custom code and error message.
|
|
*
|
|
* @param code String
|
|
* @param message String
|
|
*/
|
|
@Override
|
|
public void reject(String code, String message) {
|
|
reject(code, message, /*Throwable*/null, /*WritableMap*/null);
|
|
}
|
|
|
|
/**
|
|
* Report an exception with a custom code.
|
|
*
|
|
* @param code String
|
|
* @param throwable Throwable
|
|
*/
|
|
@Override
|
|
public void reject(String code, Throwable throwable) {
|
|
reject(code, /*Message*/null, throwable, /*WritableMap*/null);
|
|
}
|
|
|
|
/**
|
|
* Report an exception with a custom code and error message.
|
|
*
|
|
* @param code String
|
|
* @param message String
|
|
* @param throwable Throwable
|
|
*/
|
|
@Override
|
|
public void reject(String code, String message, Throwable throwable) {
|
|
reject(code, message, throwable, /*WritableMap*/null);
|
|
}
|
|
|
|
/**
|
|
* Report an exception, with default error code.
|
|
* Useful in catch-all scenarios where it's unclear why the error occurred.
|
|
*
|
|
* @param throwable Throwable
|
|
*/
|
|
@Override
|
|
public void reject(Throwable throwable) {
|
|
reject(/*Code*/null, /*Message*/null, throwable, /*WritableMap*/null);
|
|
}
|
|
|
|
/* ---------------------------
|
|
* With userInfo WritableMap
|
|
* --------------------------- */
|
|
|
|
/**
|
|
* Report an exception, with default error code, with userInfo.
|
|
* Useful in catch-all scenarios where it's unclear why the error occurred.
|
|
*
|
|
* @param throwable Throwable
|
|
* @param userInfo WritableMap
|
|
*/
|
|
@Override
|
|
public void reject(Throwable throwable, WritableMap userInfo) {
|
|
reject(/*Code*/null, /*Message*/null, throwable, userInfo);
|
|
}
|
|
|
|
/**
|
|
* Reject with a code and userInfo WritableMap.
|
|
*
|
|
* @param code String
|
|
* @param userInfo WritableMap
|
|
*/
|
|
@Override
|
|
public void reject(String code, @Nonnull WritableMap userInfo) {
|
|
reject(code, /*Message*/null, /*Throwable*/null, userInfo);
|
|
}
|
|
|
|
/**
|
|
* Report an exception with a custom code and userInfo.
|
|
*
|
|
* @param code String
|
|
* @param throwable Throwable
|
|
* @param userInfo WritableMap
|
|
*/
|
|
@Override
|
|
public void reject(String code, Throwable throwable, WritableMap userInfo) {
|
|
reject(code, /*Message*/null, throwable, userInfo);
|
|
}
|
|
|
|
/**
|
|
* Report an error with a custom code, error message and userInfo,
|
|
* an error not caused by an exception.
|
|
*
|
|
* @param code String
|
|
* @param message String
|
|
* @param userInfo WritableMap
|
|
*/
|
|
@Override
|
|
public void reject(String code, String message, @Nonnull WritableMap userInfo) {
|
|
reject(code, message, /*Throwable*/null, userInfo);
|
|
}
|
|
|
|
/**
|
|
* Report an exception with a custom code, error message and userInfo.
|
|
*
|
|
* @param code String
|
|
* @param message String
|
|
* @param throwable Throwable
|
|
* @param userInfo WritableMap
|
|
*/
|
|
@Override
|
|
public void reject(
|
|
@Nullable String code,
|
|
@Nullable String message,
|
|
@Nullable Throwable throwable,
|
|
@Nullable WritableMap userInfo
|
|
) {
|
|
if (mReject == null) {
|
|
mResolve = null;
|
|
return;
|
|
}
|
|
|
|
WritableNativeMap errorInfo = new WritableNativeMap();
|
|
|
|
if (code == null) {
|
|
errorInfo.putString(ERROR_MAP_KEY_CODE, ERROR_DEFAULT_CODE);
|
|
} else {
|
|
errorInfo.putString(ERROR_MAP_KEY_CODE, code);
|
|
}
|
|
|
|
// Use the custom message if provided otherwise use the throwable message.
|
|
if (message != null) {
|
|
errorInfo.putString(ERROR_MAP_KEY_MESSAGE, message);
|
|
} else if (throwable != null) {
|
|
errorInfo.putString(ERROR_MAP_KEY_MESSAGE, throwable.getMessage());
|
|
} else {
|
|
// The JavaScript side expects a map with at least an error message.
|
|
// /Libraries/BatchedBridge/NativeModules.js -> createErrorFromErrorData
|
|
// TYPE: (errorData: { message: string })
|
|
errorInfo.putString(ERROR_MAP_KEY_MESSAGE, ERROR_DEFAULT_MESSAGE);
|
|
}
|
|
|
|
// For consistency with iOS ensure userInfo key exists, even if we null it.
|
|
// iOS: /React/Base/RCTUtils.m -> RCTJSErrorFromCodeMessageAndNSError
|
|
if (userInfo != null) {
|
|
errorInfo.putMap(ERROR_MAP_KEY_USER_INFO, userInfo);
|
|
} else {
|
|
errorInfo.putNull(ERROR_MAP_KEY_USER_INFO);
|
|
}
|
|
|
|
// Attach a nativeStackAndroid array if a throwable was passed
|
|
// this matches iOS behavior - iOS adds a `nativeStackIOS` property
|
|
// iOS: /React/Base/RCTUtils.m -> RCTJSErrorFromCodeMessageAndNSError
|
|
if (throwable != null) {
|
|
StackTraceElement[] stackTrace = throwable.getStackTrace();
|
|
WritableNativeArray nativeStackAndroid = new WritableNativeArray();
|
|
|
|
// Build an an Array of StackFrames to match JavaScript:
|
|
// iOS: /Libraries/Core/Devtools/parseErrorStack.js -> StackFrame
|
|
for (int i = 0; i < stackTrace.length && i < ERROR_STACK_FRAME_LIMIT; i++) {
|
|
StackTraceElement frame = stackTrace[i];
|
|
WritableMap frameMap = new WritableNativeMap();
|
|
// NOTE: no column number exists StackTraceElement
|
|
frameMap.putString(STACK_FRAME_KEY_CLASS, frame.getClassName());
|
|
frameMap.putString(STACK_FRAME_KEY_FILE, frame.getFileName());
|
|
frameMap.putInt(STACK_FRAME_KEY_LINE_NUMBER, frame.getLineNumber());
|
|
frameMap.putString(STACK_FRAME_KEY_METHOD_NAME, frame.getMethodName());
|
|
nativeStackAndroid.pushMap(frameMap);
|
|
}
|
|
|
|
errorInfo.putArray(ERROR_MAP_KEY_NATIVE_STACK, nativeStackAndroid);
|
|
} else {
|
|
errorInfo.putArray(ERROR_MAP_KEY_NATIVE_STACK, new WritableNativeArray());
|
|
}
|
|
|
|
mReject.invoke(errorInfo);
|
|
mResolve = null;
|
|
mReject = null;
|
|
}
|
|
|
|
/* ------------
|
|
* Deprecated
|
|
* ------------ */
|
|
|
|
@Override
|
|
@Deprecated
|
|
public void reject(String message) {
|
|
reject(/*Code*/null, message, /*Throwable*/null, /*WritableMap*/null);
|
|
}
|
|
}
|