Add on-device JSC inspector

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
This commit is contained in:
Alexander Blom 2016-11-02 12:18:11 -07:00 committed by Facebook Github Bot
parent 9bfd95c8aa
commit 156e5d9837
22 changed files with 1007 additions and 0 deletions

View File

@ -28,6 +28,7 @@ if THIS_IS_FBANDROID:
],
deps = [
'//xplat/folly:molly',
react_native_xplat_target('inspector:inspector'),
])
cxx_library(
@ -39,6 +40,7 @@ if THIS_IS_FBANDROID:
'-DWITH_JSC_MEMORY_PRESSURE=1',
'-DWITH_REACT_INTERNAL_SETTINGS=1',
'-DWITH_FB_MEMORY_PROFILING=1',
'-DWITH_INSPECTOR=1',
],
deps = JSC_DEPS
)

View File

@ -19,6 +19,10 @@
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#ifdef WITH_INSPECTOR
#include <inspector/Inspector.h>
#endif
#include "Platform.h"
#include "SystraceSection.h"
#include "JSCNativeModules.h"
@ -229,6 +233,10 @@ void JSCExecutor::initOnJSVMThread() throw(JSException) {
// Add a pointer to ourselves so we can retrieve it later in our hooks
JSObjectSetPrivate(JSContextGetGlobalObject(m_context), this);
#ifdef WITH_INSPECTOR
Inspector::instance().registerGlobalContext("main", m_context);
#endif
installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");
installNativeHook<&JSCExecutor::nativeCallSyncHook>("nativeCallSyncHook");
@ -283,6 +291,10 @@ void JSCExecutor::terminateOnJSVMThread() {
m_nativeModules.reset();
#ifdef WITH_INSPECTOR
Inspector::instance().unregisterGlobalContext(m_context);
#endif
JSGlobalContextRelease(m_context);
m_context = nullptr;
}

View File

@ -0,0 +1,50 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Agent.h"
#include "Error.h"
#include "Protocol.h"
#include <folly/json.h>
namespace facebook {
namespace react {
void Agent::onConnect(std::shared_ptr<Channel> channel) {
channel_ = std::move(channel);
channel_->registerDomain(getDomain(), [this](std::string, int callId, const std::string& method, folly::dynamic args) {
auto result = handle(method, std::move(args));
if (result.isNull()) {
result = folly::dynamic::object;
}
auto message = folly::dynamic::object("id", callId)("result", std::move(result));
channel_->sendMessage(folly::toJson(std::move(message)));
});
}
void Agent::onDisconnect() {
channel_.reset();
}
folly::dynamic Agent::handle(const std::string& method, folly::dynamic args) {
try {
return methods_.at(method)(std::move(args));
} catch (const std::out_of_range& e) {
throw InspectorException(ErrorCode::MethodNotFound, "Unknown method: '" + method + "'");
}
}
void Agent::registerMethod(std::string name, Method method) {
methods_.emplace(std::move(name), std::move(method));
}
void Agent::sendEvent(std::string name, folly::dynamic params) {
if (!channel_) {
return;
}
channel_->sendMessage(Event(getDomain(), std::move(name), std::move(params)));
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Dispatcher.h"
#include <unordered_map>
namespace facebook {
namespace react {
class Agent : public Dispatcher {
public:
void onConnect(std::shared_ptr<Channel> channel) override;
void onDisconnect() override;
protected:
using Method = std::function<folly::dynamic(folly::dynamic)>;
void registerMethod(std::string name, Method method);
void sendEvent(std::string name, folly::dynamic params = nullptr);
virtual std::string getDomain() = 0;
private:
folly::dynamic handle(const std::string& method, folly::dynamic args);
std::shared_ptr<Channel> channel_;
std::unordered_map<std::string, Method> methods_;
};
}
}

View File

@ -0,0 +1,28 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := inspector
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := \
-DLOG_TAG=\"ReactNative\"
LOCAL_CFLAGS += -Wall -Werror -fexceptions -frtti
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_STATIC_LIBRARIES := jschelpers
LOCAL_SHARED_LIBRARIES := libfolly_json libjsc libglog
include $(BUILD_STATIC_LIBRARY)
$(call import-module,folly)
$(call import-module,jsc)
$(call import-module,glog)
$(call import-module,jschelpers)

View File

@ -0,0 +1,54 @@
EXPORTED_HEADERS = [
'Inspector.h',
]
def library(**kwargs):
if THIS_IS_FBANDROID:
include_defs('//ReactAndroid/DEFS')
cxx_library(
force_static = True,
# We depend on JSC, support the same platforms
supported_platforms_regex = '^android-(armv7|x86)$',
deps = JSC_DEPS + [
'//xplat/folly:molly',
react_native_xplat_target('jschelpers:jschelpers'),
],
**kwargs
)
elif THIS_IS_FBOBJC:
pass
# TODO: Currently untested on iOS, but might require linking against a
# custom version to ensure binary stability?
# ios_library(
# inherited_buck_flags = STATIC_LIBRARY_IOS_FLAGS,
# frameworks = [
# '$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework',
# ],
# deps = [
# '//xplat/folly:molly',
# react_native_xplat_target('jschelpers:jschelpers'),
# ],
# **kwargs
# )
else:
raise Error('Unknown repo')
library(
name = 'inspector',
preprocessor_flags = [
'-DENABLE_INSPECTOR=1',
],
compiler_flags = [
'-Wall',
'-fexceptions',
'-fvisibility=hidden',
'-std=gnu++1y',
],
exported_headers = EXPORTED_HEADERS,
headers = glob(['*.h'], excludes=EXPORTED_HEADERS),
header_namespace = 'inspector',
srcs = glob(['*.cpp']),
visibility = [
'PUBLIC',
],
)

View File

@ -0,0 +1,3 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Dispatcher.h"

View File

@ -0,0 +1,33 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <folly/dynamic.h>
// Both double-conversions and WTF define the ASSERT macro
#undef ASSERT
namespace facebook {
namespace react {
class Channel {
public:
using MessageHandler = std::function<void(std::string message, int callId, const std::string& methodName, folly::dynamic args)>;
virtual ~Channel() = default;
virtual void sendMessage(std::string message) = 0;
virtual void registerDomain(std::string domain, MessageHandler handler) = 0;
};
class Dispatcher {
public:
virtual ~Dispatcher() {}
virtual void onConnect(std::shared_ptr<Channel> channel) = 0;
virtual void onDisconnect() = 0;
};
}
}

View File

@ -0,0 +1,19 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Error.h"
namespace facebook {
namespace react {
InspectorException::InspectorException(int callId, ErrorCode code, std::string message)
: error_(callId, code, std::move(message)) {}
InspectorException::InspectorException(ErrorCode code, std::string message)
: error_(code, std::move(message)) {}
InspectorException InspectorException::withCallId(int callId) const {
return InspectorException(callId, error_.code(), error_.message());
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Protocol.h"
#include <string>
#include <exception>
#include <folly/Optional.h>
namespace facebook {
namespace react {
class InspectorException : public std::exception {
public:
InspectorException(int callId, ErrorCode code, std::string message);
explicit InspectorException(ErrorCode code, std::string message);
const char* what() const throw() override {
return error_.message().c_str();
}
const Error& error() const {
return error_;
}
InspectorException withCallId(int callId) const;
private:
Error error_;
};
}
}

View File

@ -0,0 +1,162 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Inspector.h"
#include "InspectorController.h"
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include <JavaScriptCore/JSLock.h>
#include <folly/Memory.h>
namespace facebook {
namespace react {
namespace {
JSC::JSGlobalObject& getGlobalObject(JSContextRef ctx) {
JSC::ExecState* exec = toJS(ctx);
JSC::JSLockHolder locker(exec);
JSC::JSGlobalObject* globalObject = exec->vmEntryGlobalObject();
return *globalObject;
}
}
Inspector::LocalConnection::LocalConnection(std::shared_ptr<Inspector::DuplexConnection> duplexConnection)
: duplexConnection_(std::move(duplexConnection)) {}
void Inspector::LocalConnection::sendMessage(std::string message) {
duplexConnection_->sendToLocal(std::move(message));
}
void Inspector::LocalConnection::disconnect() {
duplexConnection_->terminate(false);
}
Inspector::PageHolder::PageHolder(std::string name, std::unique_ptr<InspectorController> controller)
: name(name)
, controller(std::move(controller)) {}
Inspector::PageHolder::~PageHolder() = default;
Inspector& Inspector::instance() {
static Inspector inspector;
return inspector;
}
std::vector<Inspector::Page> Inspector::getPages() const {
std::lock_guard<std::mutex> lock(registrationMutex_);
std::vector<Page> pages;
pages.reserve(pages_.size());
for (auto& entry : pages_) {
pages.emplace_back(Page{entry.first, entry.second.name});
}
return pages;
}
void Inspector::registerGlobalContext(std::string title, JSGlobalContextRef ctx) {
std::lock_guard<std::mutex> lock(registrationMutex_);
auto controller = folly::make_unique<InspectorController>(getGlobalObject(ctx));
auto pageId = numPages_++;
pages_.emplace(
std::piecewise_construct,
std::forward_as_tuple(pageId),
std::forward_as_tuple(std::move(title), std::move(controller)));
}
void Inspector::unregisterGlobalContext(JSGlobalContextRef ctx) {
std::lock_guard<std::mutex> lock(registrationMutex_);
auto& globalObject = getGlobalObject(ctx);
for (auto it = pages_.begin(); it != pages_.end(); it++) {
auto& page = it->second;
if (page.controller->getGlobalObject().globalExec() == globalObject.globalExec()) {
if (page.connection_) {
page.connection_->terminate(true);
}
pages_.erase(it);
return;
}
}
}
std::unique_ptr<Inspector::LocalConnection> Inspector::connect(int pageId, std::unique_ptr<RemoteConnection> remote) {
std::lock_guard<std::mutex> lock(registrationMutex_);
return folly::make_unique<LocalConnection>(pages_.at(pageId).connect(std::move(remote)));
}
void Inspector::disconnect(int pageId) {
std::lock_guard<std::mutex> lock(registrationMutex_);
pages_.at(pageId).controller->onDisconnect();
}
std::shared_ptr<Inspector::DuplexConnection> Inspector::PageHolder::connect(std::unique_ptr<RemoteConnection> remote) {
if (connection_) {
throw std::runtime_error("Already connected");
}
connection_ = std::make_shared<DuplexConnection>(*this, std::move(remote));
controller->onConnect([connection = connection_](std::string message) {
connection->sendToRemote(std::move(message));
});
return connection_;
}
Inspector::DuplexConnection::DuplexConnection(PageHolder& page, std::unique_ptr<RemoteConnection> remoteConnection)
: page_(page)
, remoteConnection_(std::move(remoteConnection)) {}
Inspector::DuplexConnection::~DuplexConnection() {
if (remoteConnection_) {
LOG(FATAL) << "DuplexConnection wasn't terminated before destruction";
}
}
void Inspector::DuplexConnection::sendToRemote(std::string message) {
std::lock_guard<std::mutex> lock(remoteMutex_);
if (!remoteConnection_) {
return;
}
remoteConnection_->onMessage(std::move(message));
}
void Inspector::DuplexConnection::sendToLocal(std::string message) {
std::lock_guard<std::mutex> lock(localMutex_);
if (!remoteConnection_) {
return;
}
page_.controller->onMessage(std::move(message));
}
void Inspector::DuplexConnection::terminate(bool local) {
std::lock_guard<std::mutex> lockLocal(localMutex_);
{
// Temp lock here so we can still send going away message
std::lock_guard<std::mutex> lockRemote(remoteMutex_);
if (!remoteConnection_) {
// Already disconnected
return;
}
}
if (local) {
page_.controller->onGoingAway();
}
std::lock_guard<std::mutex> lockRemote(remoteMutex_);
auto remoteConnection = std::move(remoteConnection_);
if (local) {
remoteConnection->onDisconnect();
}
page_.controller->onDisconnect();
// This kills us
page_.connection_.reset();
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <JavaScriptCore/JSBase.h>
#undef WTF_EXPORT_PRIVATE
namespace facebook {
namespace react {
class InspectorController;
class Sender;
class Inspector {
private:
class DuplexConnection;
public:
struct Page {
const int id;
const std::string title;
};
struct RemoteConnection {
virtual ~RemoteConnection() = default;
virtual void onMessage(std::string message) = 0;
virtual void onDisconnect() = 0;
};
class LocalConnection {
public:
void sendMessage(std::string message);
void disconnect();
LocalConnection(std::shared_ptr<DuplexConnection> duplexConnection);
private:
std::shared_ptr<DuplexConnection> duplexConnection_;
};
static Inspector& instance();
void registerGlobalContext(std::string title, JSGlobalContextRef ctx);
void unregisterGlobalContext(JSGlobalContextRef ctx);
std::vector<Page> getPages() const;
std::unique_ptr<LocalConnection> connect(int pageId, std::unique_ptr<RemoteConnection> remote);
private:
struct PageHolder;
class DuplexConnection {
public:
DuplexConnection(PageHolder& page, std::unique_ptr<RemoteConnection> remoteConnection);
~DuplexConnection();
void sendToRemote(std::string message);
void sendToLocal(std::string message);
void terminate(bool local);
private:
PageHolder& page_;
std::unique_ptr<RemoteConnection> remoteConnection_;
std::mutex localMutex_;
std::mutex remoteMutex_;
};
struct PageHolder {
PageHolder(std::string name, std::unique_ptr<InspectorController> controller);
~PageHolder();
std::shared_ptr<DuplexConnection> connect(std::unique_ptr<RemoteConnection> remote);
const std::string name;
std::unique_ptr<InspectorController> controller;
std::shared_ptr<DuplexConnection> connection_;
};
Inspector() {};
void disconnect(int pageId);
int numPages_ = 0;
std::unordered_map<int, PageHolder> pages_;
mutable std::mutex registrationMutex_;
};
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "InspectorAgent.h"
namespace facebook {
namespace react {
InspectorAgent::InspectorAgent() {
auto emptyMethod = [](folly::dynamic) -> folly::dynamic {
return nullptr;
};
registerMethod("enable", emptyMethod);
registerMethod("disable", emptyMethod);
}
void InspectorAgent::detach() {
sendEvent("detached", folly::dynamic::object("reason", "target_closed"));
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Agent.h"
namespace facebook {
namespace react {
class InspectorAgent : public Agent {
public:
InspectorAgent();
void detach();
private:
std::string getDomain() override {
return "Inspector";
}
};
}
}

View File

@ -0,0 +1,178 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "InspectorController.h"
#include "Error.h"
#include "Agent.h"
#include "InspectorAgent.h"
#include <folly/Memory.h>
#include <folly/Conv.h>
#include <folly/json.h>
namespace facebook {
namespace react {
class ConcreteChannel : public Channel {
public:
ConcreteChannel(Receiver receiver)
: receiver_(std::move(receiver)) {}
void sendMessage(std::string message) override {
receiver_(std::move(message));
}
void registerDomain(std::string domain, MessageHandler handler) override {
domains_.emplace(std::move(domain), std::move(handler));
}
std::unordered_map<std::string, MessageHandler>& getDomains() {
return domains_;
}
private:
std::unordered_map<std::string, MessageHandler> domains_;
Receiver receiver_;
};
class MessageRouter {
public:
MessageRouter(ConcreteChannel* channel)
: channel_(channel) {
CHECK(channel_) << "Channel is null";
}
/*
* Messages are in JSON, formatted like:
* {
* "id": 1,
* "method": "Debugger.removeBreakpoint",
* "params": { "removeBreakpoint": "xyz" }
* }
*/
void route(std::string message) {
try {
auto json = parseJson(message);
auto callId = getCallId(json);
receive(callId, std::move(message), std::move(json));
} catch (const InspectorException& e) {
channel_->sendMessage(e.error());
}
}
private:
void receive(int callId, std::string message, folly::dynamic json) {
try {
auto method = Method::parse(json["method"].asString());
auto& handler = getHandler(method.domain());
handler(std::move(message), callId, method.name(), std::move(json["params"]));
} catch (const InspectorException& e) {
throw e.withCallId(callId);
} catch (const std::exception& e) {
LOG(ERROR) << "Dispatcher failed: " << e.what();
throw InspectorException(callId, ErrorCode::ServerError, "Internal error");
} catch (...) {
throw InspectorException(callId, ErrorCode::ServerError, "Internal error");
}
}
folly::dynamic parseJson(const std::string& message) {
try {
return folly::parseJson(message);
} catch (const std::runtime_error& e) {
throw InspectorException(ErrorCode::ParseError, "Message must be in JSON format");
}
}
int getCallId(folly::dynamic& json) {
auto& id = json["id"];
if (!id.isInt()) {
throw InspectorException(ErrorCode::InvalidRequest, "The type of 'id' property must be number");
} else {
return id.asInt();
}
}
Channel::MessageHandler& getHandler(const std::string& domain) {
try {
auto& domains = channel_->getDomains();
return domains.at(domain);
} catch (const std::out_of_range& e) {
throw InspectorException(ErrorCode::MethodNotFound, folly::to<std::string>("Unknown domain: '", domain, "'"));
}
}
ConcreteChannel* channel_;
};
class SchemaAgent : public Agent {
public:
SchemaAgent() {
registerMethod("getDomains", [this](folly::dynamic) -> folly::dynamic {
CHECK(channel_) << "Channel is null";
folly::dynamic names = folly::dynamic::array;
auto& domains = channel_->getDomains();
for (auto& entry : domains) {
// TODO(blom): Actually get version?
names.push_back(folly::dynamic::object("name", entry.first)("version", "1.0"));
}
return names;
});
}
void onConnect(std::shared_ptr<Channel> channel) override {
Agent::onConnect(channel);
channel_ = std::static_pointer_cast<ConcreteChannel>(channel);
}
private:
std::shared_ptr<ConcreteChannel> channel_;
std::string getDomain() override {
return "Schema";
}
};
InspectorController::InspectorController(JSC::JSGlobalObject& globalObject)
: globalObject_(globalObject) {
auto inspectorAgent = folly::make_unique<InspectorAgent>();
inspectorAgent_ = inspectorAgent.get();
dispatchers_.push_back(std::move(inspectorAgent));
dispatchers_.push_back(folly::make_unique<SchemaAgent>());
}
InspectorController::~InspectorController() {
CHECK(!channel_) << "Wasn't disconnected";
}
void InspectorController::onConnect(Receiver receiver) {
CHECK(!channel_) << "Already connected";
channel_ = std::make_shared<ConcreteChannel>(std::move(receiver));
for (auto& dispatcher : dispatchers_) {
dispatcher->onConnect(channel_);
}
}
void InspectorController::onMessage(std::string message) {
CHECK(channel_) << "Not connected";
MessageRouter(channel_.get()).route(message);
}
void InspectorController::onGoingAway() {
CHECK(channel_) << "Not connected";
inspectorAgent_->detach();
}
void InspectorController::onDisconnect() {
CHECK(channel_) << "Not connected";
for (auto& dispatcher : dispatchers_) {
dispatcher->onDisconnect();
}
channel_.reset();
}
}
}

View File

@ -0,0 +1,42 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Dispatcher.h"
#include <memory>
#include <vector>
#include <unordered_map>
namespace JSC {
class JSGlobalObject;
}
namespace facebook {
namespace react {
class ConcreteChannel;
class InspectorAgent;
using Receiver = std::function<void(std::string)>;
class InspectorController {
public:
InspectorController(JSC::JSGlobalObject& globalObject);
~InspectorController();
JSC::JSGlobalObject& getGlobalObject() const { return globalObject_; }
void onConnect(Receiver receiver);
void onMessage(std::string message);
void onGoingAway();
void onDisconnect();
private:
JSC::JSGlobalObject& globalObject_;
std::shared_ptr<ConcreteChannel> channel_;
std::vector<std::unique_ptr<Dispatcher>> dispatchers_;
InspectorAgent* inspectorAgent_;
};
}
}

View File

@ -0,0 +1,86 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Protocol.h"
#include "Error.h"
#include <folly/dynamic.h>
#include <folly/json.h>
#include <folly/Conv.h>
#include <chrono>
namespace facebook {
namespace react {
namespace {
folly::dynamic getCallId(const folly::Optional<int>& callId) {
if (callId.hasValue()) {
return callId.value();
} else {
return nullptr;
}
}
}
Method Method::parse(const std::string& formatted) {
auto splitPos = formatted.find_first_of('.');
if (splitPos == std::string::npos) {
throw InspectorException(ErrorCode::InvalidRequest, "Invalid method format");
}
return Method(formatted.substr(0, splitPos), formatted.substr(splitPos + 1));
}
Method::Method(std::string domain, std::string name)
: domain_(std::move(domain))
, name_(std::move(name)) {}
std::string Method::formatted() const {
return folly::to<std::string>(domain_, '.', name_);
}
Event::Event(std::string domain, std::string method, folly::dynamic params)
: method_(std::move(domain), std::move(method))
, params_(std::move(params)) {}
Event::operator std::string() const {
auto event = folly::dynamic::object("method", method_.formatted());
if (!params_.isNull()) {
event("params", params_);
}
return folly::toJson(std::move(event));
}
namespace Timestamp {
double now() {
using duration = std::chrono::duration<double, std::ratio<1>>;
auto epoch = std::chrono::duration_cast<duration>(
std::chrono::system_clock::now().time_since_epoch()
);
return epoch.count();
}
}
Error::Error(int callId, ErrorCode code, std::string message)
: callId_(callId)
, code_(code)
, message_(std::move(message)) {}
Error::Error(ErrorCode code, std::string message)
: code_(code)
, message_(std::move(message)) {}
Error::operator std::string() const {
auto errorCode = static_cast<int>(code_);
return folly::toJson(
folly::dynamic::object("id", getCallId(callId_))
("error", folly::dynamic::object("code", errorCode)("message", message_))
);
}
}
}

View File

@ -0,0 +1,77 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <string>
#include <folly/dynamic.h>
#include <folly/Optional.h>
namespace facebook {
namespace react {
class Method {
public:
static Method parse(const std::string& formatted);
Method(std::string domain, std::string name);
const std::string& domain() const {
return domain_;
}
const std::string& name() const {
return name_;
}
std::string formatted() const;
private:
std::string domain_;
std::string name_;
};
class Event {
public:
explicit Event(std::string domain, std::string method, folly::dynamic params);
operator std::string() const;
private:
Method method_;
folly::dynamic params_;
};
namespace Timestamp {
double now();
}
enum class ErrorCode {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
ServerError = -32000,
};
class Error {
public:
Error(int callId, ErrorCode code, std::string message);
Error(ErrorCode code, std::string message);
const std::string& message() const {
return message_;
}
ErrorCode code() const {
return code_;
}
operator std::string() const;
private:
folly::Optional<int> callId_;
ErrorCode code_;
std::string message_;
};
}
}

View File

@ -0,0 +1,9 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Util.h"
namespace facebook {
namespace react {
}
}

View File

@ -0,0 +1,45 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/config.h>
#include <wtf/text/WTFString.h>
#include <wtf/text/CString.h>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <string>
#include <unordered_map>
namespace facebook {
namespace react {
inline std::string toStdString(const WTF::String& str) {
return std::string(str.utf8().data());
}
template <typename T>
class AgentMap {
public:
void add(JSGlobalContextRef ctx, T* agent) {
map_[ctx] = agent;
}
void remove(T* agent) {
auto it = std::find_if(
map_.begin(),
map_.end(),
[agent](const typename MapType::value_type& entry) { return entry.second == agent; });
map_.erase(it);
}
T* get(JSGlobalContextRef ctx) {
return map_.at(ctx);
}
private:
using MapType = std::unordered_map<JSGlobalContextRef, T*>;
MapType map_;
};
}
}

View File

@ -108,6 +108,13 @@ void installGlobalProxy(
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) {

View File

@ -60,6 +60,8 @@ void installGlobalProxy(
const char* name,
JSObjectGetPropertyCallback callback);
void removeGlobal(JSGlobalContextRef ctx, const char* name);
JSValueRef makeJSCException(
JSContextRef ctx,
const char* exception_text);