mirror of
https://github.com/status-im/react-native.git
synced 2025-01-10 17:45:59 +00:00
156e5d9837
Summary: Introduces the inspector library supporting the Chrome Debugging Protocol for JavaScriptCore. Eventually this will mean that it is possible to attach the Chrome inspector directly to the JSC instance running on the device. This library doesn't define the actual transport but leaves that up to the platform layer. The main entry point (and the only exported header) is `Inspector.h`. This diff only introduces the basics supporting the `Schema` and `Inspector` domains meaning it doesn't have any features yet. These will come in following diffs. Reviewed By: michalgr Differential Revision: D4021490 fbshipit-source-id: 517fd9033051c11ba97d312b16382445ae85d3f3
229 lines
7.3 KiB
C++
229 lines
7.3 KiB
C++
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
#include "JSCHelpers.h"
|
|
|
|
#ifdef WITH_FBSYSTRACE
|
|
#include <fbsystrace.h>
|
|
#endif
|
|
|
|
#include <JavaScriptCore/JSStringRef.h>
|
|
#include <folly/String.h>
|
|
#include <glog/logging.h>
|
|
|
|
#include "Value.h"
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
namespace {
|
|
|
|
JSValueRef functionCaller(
|
|
JSContextRef ctx,
|
|
JSObjectRef function,
|
|
JSObjectRef thisObject,
|
|
size_t argumentCount,
|
|
const JSValueRef arguments[],
|
|
JSValueRef* exception) {
|
|
auto* f = static_cast<JSFunction*>(JSObjectGetPrivate(function));
|
|
return (*f)(ctx, thisObject, argumentCount, arguments);
|
|
}
|
|
|
|
JSClassRef createFuncClass() {
|
|
auto definition = kJSClassDefinitionEmpty;
|
|
definition.finalize = [](JSObjectRef object) {
|
|
auto* function = static_cast<JSFunction*>(JSObjectGetPrivate(object));
|
|
delete function;
|
|
};
|
|
definition.callAsFunction = exceptionWrapMethod<&functionCaller>();
|
|
|
|
return JSClassCreate(&definition);
|
|
}
|
|
|
|
JSObjectRef makeFunction(
|
|
JSContextRef ctx,
|
|
JSStringRef name,
|
|
JSFunction function) {
|
|
static auto kClassDef = createFuncClass();
|
|
auto functionObject = Object(ctx, JSObjectMake(ctx, kClassDef, new JSFunction(std::move(function))));
|
|
functionObject.setProperty("name", Value(ctx, name));
|
|
return functionObject;
|
|
}
|
|
|
|
}
|
|
|
|
JSObjectRef makeFunction(
|
|
JSContextRef ctx,
|
|
const char* name,
|
|
JSFunction function) {
|
|
return makeFunction(ctx, JSStringCreateWithUTF8CString(name), std::move(function));
|
|
}
|
|
|
|
void installGlobalFunction(
|
|
JSGlobalContextRef ctx,
|
|
const char* name,
|
|
JSFunction function) {
|
|
auto jsName = JSStringCreateWithUTF8CString(name);
|
|
auto functionObj = makeFunction(ctx, jsName, std::move(function));
|
|
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
|
|
JSObjectSetProperty(ctx, globalObject, jsName, functionObj, 0, NULL);
|
|
JSStringRelease(jsName);
|
|
}
|
|
|
|
JSObjectRef makeFunction(
|
|
JSGlobalContextRef ctx,
|
|
const char* name,
|
|
JSObjectCallAsFunctionCallback callback) {
|
|
auto jsName = String(name);
|
|
return JSObjectMakeFunctionWithCallback(ctx, jsName, callback);
|
|
}
|
|
|
|
void installGlobalFunction(
|
|
JSGlobalContextRef ctx,
|
|
const char* name,
|
|
JSObjectCallAsFunctionCallback callback) {
|
|
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
|
|
JSObjectRef functionObj = JSObjectMakeFunctionWithCallback(
|
|
ctx, jsName, callback);
|
|
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
|
|
JSObjectSetProperty(ctx, globalObject, jsName, functionObj, 0, NULL);
|
|
JSStringRelease(jsName);
|
|
}
|
|
|
|
void installGlobalProxy(
|
|
JSGlobalContextRef ctx,
|
|
const char* name,
|
|
JSObjectGetPropertyCallback callback) {
|
|
JSClassDefinition proxyClassDefintion = kJSClassDefinitionEmpty;
|
|
proxyClassDefintion.className = "_FBProxyClass";
|
|
proxyClassDefintion.getProperty = callback;
|
|
JSClassRef proxyClass = JSClassCreate(&proxyClassDefintion);
|
|
|
|
JSObjectRef proxyObj = JSObjectMake(ctx, proxyClass, nullptr);
|
|
|
|
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
|
|
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
|
|
JSObjectSetProperty(ctx, globalObject, jsName, proxyObj, 0, NULL);
|
|
|
|
JSStringRelease(jsName);
|
|
JSClassRelease(proxyClass);
|
|
}
|
|
|
|
void removeGlobal(JSGlobalContextRef ctx, const char* name) {
|
|
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
|
|
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
|
|
JSObjectSetProperty(ctx, globalObject, jsName, nullptr, 0, nullptr);
|
|
JSStringRelease(jsName);
|
|
}
|
|
|
|
JSValueRef makeJSCException(
|
|
JSContextRef ctx,
|
|
const char* exception_text) {
|
|
JSStringRef message = JSStringCreateWithUTF8CString(exception_text);
|
|
JSValueRef exceptionString = JSValueMakeString(ctx, message);
|
|
JSStringRelease(message);
|
|
return JSValueToObject(ctx, exceptionString, NULL);
|
|
}
|
|
|
|
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source) {
|
|
#ifdef WITH_FBSYSTRACE
|
|
fbsystrace::FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "evaluateScript");
|
|
#endif
|
|
JSValueRef exn, result;
|
|
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
|
|
if (result == nullptr) {
|
|
formatAndThrowJSException(context, exn, source);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#if WITH_FBJSCEXTENSIONS
|
|
JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) {
|
|
JSValueRef exn, result;
|
|
result = JSEvaluateSourceCode(context, source, NULL, &exn);
|
|
if (result == nullptr) {
|
|
formatAndThrowJSException(context, exn, sourceURL);
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
void formatAndThrowJSException(JSContextRef context, JSValueRef exn, JSStringRef source) {
|
|
Value exception = Value(context, exn);
|
|
|
|
std::string exceptionText = exception.toString().str();
|
|
|
|
// The null/empty-ness of source tells us if the JS came from a
|
|
// file/resource, or was a constructed statement. The location
|
|
// info will include that source, if any.
|
|
std::string locationInfo = source != nullptr ? String::ref(source).str() : "";
|
|
Object exObject = exception.asObject();
|
|
auto line = exObject.getProperty("line");
|
|
if (line != nullptr && line.isNumber()) {
|
|
if (locationInfo.empty() && line.asInteger() != 1) {
|
|
// If there is a non-trivial line number, but there was no
|
|
// location info, we include a placeholder, and the line
|
|
// number.
|
|
locationInfo = folly::to<std::string>("<unknown file>:", line.asInteger());
|
|
} else if (!locationInfo.empty()) {
|
|
// If there is location info, we always include the line
|
|
// number, regardless of its value.
|
|
locationInfo += folly::to<std::string>(":", line.asInteger());
|
|
}
|
|
}
|
|
|
|
if (!locationInfo.empty()) {
|
|
exceptionText += " (" + locationInfo + ")";
|
|
}
|
|
|
|
LOG(ERROR) << "Got JS Exception: " << exceptionText;
|
|
|
|
Value jsStack = exObject.getProperty("stack");
|
|
if (jsStack.isNull() || !jsStack.isString()) {
|
|
throwJSExecutionException("%s", exceptionText.c_str());
|
|
} else {
|
|
LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str();
|
|
throwJSExecutionExceptionWithStack(
|
|
exceptionText.c_str(), jsStack.toString().str().c_str());
|
|
}
|
|
}
|
|
|
|
|
|
JSValueRef makeJSError(JSContextRef ctx, const char *error) {
|
|
JSValueRef nestedException = nullptr;
|
|
JSValueRef args[] = { Value(ctx, String(error)) };
|
|
JSObjectRef errorObj = JSObjectMakeError(ctx, 1, args, &nestedException);
|
|
if (nestedException != nullptr) {
|
|
return std::move(args[0]);
|
|
}
|
|
return errorObj;
|
|
}
|
|
|
|
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) {
|
|
std::ostringstream msg;
|
|
try {
|
|
throw;
|
|
} catch (const std::bad_alloc& ex) {
|
|
throw; // We probably shouldn't try to handle this in JS
|
|
} catch (const std::exception& ex) {
|
|
msg << "C++ Exception in '" << exceptionLocation << "': " << ex.what();
|
|
return makeJSError(ctx, msg.str().c_str());
|
|
} catch (const char* ex) {
|
|
msg << "C++ Exception (thrown as a char*) in '" << exceptionLocation << "': " << ex;
|
|
return makeJSError(ctx, msg.str().c_str());
|
|
} catch (...) {
|
|
msg << "Unknown C++ Exception in '" << exceptionLocation << "'";
|
|
return makeJSError(ctx, msg.str().c_str());
|
|
}
|
|
}
|
|
|
|
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause) {
|
|
try {
|
|
auto functionName = Object(ctx, jsFunctionCause).getProperty("name").toString().str();
|
|
return translatePendingCppExceptionToJSError(ctx, functionName.c_str());
|
|
} catch (...) {
|
|
return makeJSError(ctx, "Failed to get function name while handling exception");
|
|
}
|
|
}
|
|
|
|
} }
|