Move stack trace formatting in own class

Summary: Moves stack trace formatting logic into its own class to facilitate reusage from custom exception managers.

Reviewed By: javache

Differential Revision: D5086336

fbshipit-source-id: f434a1621c599c5c49991b3bfe5f66d05f84b5c9
This commit is contained in:
David Aurelio 2017-05-18 05:21:59 -07:00 committed by Facebook Github Bot
parent 056d075ef2
commit d7659a0ec6
4 changed files with 76 additions and 47 deletions

View File

@ -15,5 +15,6 @@ android_library(
react_native_target("java/com/facebook/react/devsupport:interfaces"), react_native_target("java/com/facebook/react/devsupport:interfaces"),
react_native_target("java/com/facebook/react/jstasks:jstasks"), react_native_target("java/com/facebook/react/jstasks:jstasks"),
react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/util:util"),
], ],
) )

View File

@ -9,26 +9,21 @@
package com.facebook.react.modules.core; package com.facebook.react.modules.core;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.BaseJavaModule; import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.common.JavascriptException; import com.facebook.react.common.JavascriptException;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.util.JSStackTrace;
@ReactModule(name = ExceptionsManagerModule.NAME) @ReactModule(name = ExceptionsManagerModule.NAME)
public class ExceptionsManagerModule extends BaseJavaModule { public class ExceptionsManagerModule extends BaseJavaModule {
protected static final String NAME = "ExceptionsManager"; protected static final String NAME = "ExceptionsManager";
static private final Pattern mJsModuleIdPattern = Pattern.compile("(?:^|[/\\\\])(\\d+\\.js)$");
private final DevSupportManager mDevSupportManager; private final DevSupportManager mDevSupportManager;
public ExceptionsManagerModule(DevSupportManager devSupportManager) { public ExceptionsManagerModule(DevSupportManager devSupportManager) {
@ -40,44 +35,6 @@ public class ExceptionsManagerModule extends BaseJavaModule {
return NAME; return NAME;
} }
// If the file name of a stack frame is numeric (+ ".js"), we assume it's a lazily injected module
// coming from a "random access bundle". We are using special source maps for these bundles, so
// that we can symbolicate stack traces for multiple injected files with a single source map.
// We have to include the module id in the stack for that, though. The ".js" suffix is kept to
// avoid ambiguities between "module-id:line" and "line:column".
static private String stackFrameToModuleId(ReadableMap frame) {
if (frame.hasKey("file") &&
!frame.isNull("file") &&
frame.getType("file") == ReadableType.String) {
final Matcher matcher = mJsModuleIdPattern.matcher(frame.getString("file"));
if (matcher.find()) {
return matcher.group(1) + ":";
}
}
return "";
}
private String stackTraceToString(String message, ReadableArray stack) {
StringBuilder stringBuilder = new StringBuilder(message).append(", stack:\n");
for (int i = 0; i < stack.size(); i++) {
ReadableMap frame = stack.getMap(i);
stringBuilder
.append(frame.getString("methodName"))
.append("@")
.append(stackFrameToModuleId(frame))
.append(frame.getInt("lineNumber"));
if (frame.hasKey("column") &&
!frame.isNull("column") &&
frame.getType("column") == ReadableType.Number) {
stringBuilder
.append(":")
.append(frame.getInt("column"));
}
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
@ReactMethod @ReactMethod
public void reportFatalException(String title, ReadableArray details, int exceptionId) { public void reportFatalException(String title, ReadableArray details, int exceptionId) {
showOrThrowError(title, details, exceptionId); showOrThrowError(title, details, exceptionId);
@ -88,7 +45,7 @@ public class ExceptionsManagerModule extends BaseJavaModule {
if (mDevSupportManager.getDevSupportEnabled()) { if (mDevSupportManager.getDevSupportEnabled()) {
mDevSupportManager.showNewJSError(title, details, exceptionId); mDevSupportManager.showNewJSError(title, details, exceptionId);
} else { } else {
FLog.e(ReactConstants.TAG, stackTraceToString(title, details)); FLog.e(ReactConstants.TAG, JSStackTrace.format(title, details));
} }
} }
@ -96,7 +53,7 @@ public class ExceptionsManagerModule extends BaseJavaModule {
if (mDevSupportManager.getDevSupportEnabled()) { if (mDevSupportManager.getDevSupportEnabled()) {
mDevSupportManager.showNewJSError(title, details, exceptionId); mDevSupportManager.showNewJSError(title, details, exceptionId);
} else { } else {
throw new JavascriptException(stackTraceToString(title, details)); throw new JavascriptException(JSStackTrace.format(title, details));
} }
} }

View File

@ -0,0 +1,12 @@
include_defs("//ReactAndroid/DEFS")
android_library(
name = "util",
srcs = glob(["**/*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_target("java/com/facebook/react/bridge:bridge"),
],
)

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2017-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.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
public class JSStackTrace {
final private static Pattern mJsModuleIdPattern = Pattern.compile("(?:^|[/\\\\])(\\d+\\.js)$");
public static String format(String message, ReadableArray stack) {
StringBuilder stringBuilder = new StringBuilder(message).append(", stack:\n");
for (int i = 0; i < stack.size(); i++) {
ReadableMap frame = stack.getMap(i);
stringBuilder
.append(frame.getString("methodName"))
.append("@")
.append(stackFrameToModuleId(frame))
.append(frame.getInt("lineNumber"));
if (frame.hasKey("column") &&
!frame.isNull("column") &&
frame.getType("column") == ReadableType.Number) {
stringBuilder
.append(":")
.append(frame.getInt("column"));
}
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
// If the file name of a stack frame is numeric (+ ".js"), we assume it's a lazily injected module
// coming from a "random access bundle". We are using special source maps for these bundles, so
// that we can symbolicate stack traces for multiple injected files with a single source map.
// We have to include the module id in the stack for that, though. The ".js" suffix is kept to
// avoid ambiguities between "module-id:line" and "line:column".
private static String stackFrameToModuleId(ReadableMap frame) {
if (frame.hasKey("file") &&
!frame.isNull("file") &&
frame.getType("file") == ReadableType.String) {
final Matcher matcher = mJsModuleIdPattern.matcher(frame.getString("file"));
if (matcher.find()) {
return matcher.group(1) + ":";
}
}
return "";
}
}