Display JS exceptions and stacks in a red box

Reviewed By: astreet

Differential Revision: D3510875

fbshipit-source-id: a7042434b68cb849f5b0c4ef782befff6a27ef5c
This commit is contained in:
Marc Horowitz 2016-07-06 16:01:17 -07:00 committed by Facebook Github Bot 1
parent c4fc504094
commit a7d032b707
3 changed files with 73 additions and 3 deletions

View File

@ -79,6 +79,7 @@ import com.facebook.react.modules.debug.DeveloperSettings;
public class DevSupportManagerImpl implements DevSupportManager {
private static final int JAVA_ERROR_COOKIE = -1;
private static final int JSEXCEPTION_ERROR_COOKIE = -1;
private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js";
private static enum ErrorType {
JS,
@ -194,7 +195,13 @@ public class DevSupportManagerImpl implements DevSupportManager {
public void handleException(Exception e) {
if (mIsDevSupportEnabled) {
FLog.e(ReactConstants.TAG, "Exception in native call from JS", e);
showNewJavaError(e.getMessage(), e);
if (e instanceof JSException) {
// TODO #11638796: convert the stack into something useful
showNewError(e.getMessage() + "\n\n" + ((JSException) e).getStack(), new StackFrame[] {},
JSEXCEPTION_ERROR_COOKIE, ErrorType.JS);
} else {
showNewJavaError(e.getMessage(), e);
}
} else {
mDefaultNativeModuleCallExceptionHandler.handleException(e);
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.devsupport;
import com.facebook.proguard.annotations.DoNotStrip;
/**
* This represents an error evaluating JavaScript. It includes the usual
* message, and the raw JS stack where the error occurred (which may be empty).
*/
@DoNotStrip
public class JSException extends Exception {
private final String mStack;
@DoNotStrip
public JSException(String message, String stack, Throwable cause) {
super(message, cause);
mStack = stack;
}
public JSException(String message, String stack) {
super(message);
mStack = stack;
}
public String getStack() {
return mStack;
}
}

View File

@ -9,11 +9,37 @@
#include <folly/Memory.h>
#include <fb/fbjni.h>
#include <cxxreact/JSCHelpers.h>
#include "JNativeRunnable.h"
namespace facebook {
namespace react {
namespace {
struct JavaJSException : jni::JavaClass<JavaJSException, JThrowable> {
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/JSException;";
static local_ref<JavaJSException> create(const char* message, const char* stack,
const std::exception& ex) {
local_ref<jthrowable> cause = jni::JCppException::create(ex);
return newInstance(make_jstring(message), make_jstring(stack), cause.get());
}
};
std::function<void()> wrapRunnable(std::function<void()>&& runnable) {
return [runnable=std::move(runnable)] {
try {
runnable();
} catch (const JSException& ex) {
throwNewJavaException(JavaJSException::create(ex.what(), ex.getStack().c_str(), ex).get());
}
};
}
}
JMessageQueueThread::JMessageQueueThread(alias_ref<JavaMessageQueueThread::javaobject> jobj) :
m_jobj(make_global(jobj)) {
}
@ -21,7 +47,7 @@ JMessageQueueThread::JMessageQueueThread(alias_ref<JavaMessageQueueThread::javao
void JMessageQueueThread::runOnQueue(std::function<void()>&& runnable) {
static auto method = JavaMessageQueueThread::javaClassStatic()->
getMethod<void(Runnable::javaobject)>("runOnQueue");
method(m_jobj, JNativeRunnable::newObjectCxxArgs(runnable).get());
method(m_jobj, JNativeRunnable::newObjectCxxArgs(wrapRunnable(std::move(runnable))).get());
}
void JMessageQueueThread::runOnQueueSync(std::function<void()>&& runnable) {
@ -29,7 +55,7 @@ void JMessageQueueThread::runOnQueueSync(std::function<void()>&& runnable) {
getMethod<jboolean()>("isOnThread");
if (jIsOnThread(m_jobj)) {
runnable();
wrapRunnable(std::move(runnable))();
} else {
std::mutex signalMutex;
std::condition_variable signalCv;