Remove Inspector Logic (cannot work on iOS / System JSC)

Reviewed By: javache

Differential Revision: D4620530

fbshipit-source-id: 52abc6178b1ad1b52ba1b1825702c9c254a04520
This commit is contained in:
Theo Yaung
2017-03-14 11:40:20 -07:00
committed by Facebook Github Bot
parent 4ac585b34b
commit 3e528b1014
41 changed files with 9 additions and 2003 deletions

View File

@@ -51,6 +51,5 @@ cxx_library(
"//xplat/fbsystrace:fbsystrace",
react_native_xplat_target("cxxreact:bridge"),
react_native_xplat_target("cxxreact:module"),
react_native_xplat_target("inspector:inspector"),
],
)

View File

@@ -2,6 +2,8 @@
#include "JInspector.h"
#ifdef WITH_INSPECTOR
namespace facebook {
namespace react {
@@ -88,3 +90,5 @@ void JInspector::registerNatives() {
}
}
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#ifdef WITH_INSPECTOR
#include <inspector/Inspector.h>
#include <fb/fbjni.h>
@@ -59,3 +61,5 @@ private:
}
}
#endif

View File

@@ -27,8 +27,7 @@ if THIS_IS_FBANDROID:
'-Wno-pessimizing-move',
],
deps = [
'//xplat/folly:molly',
react_native_xplat_target('inspector:inspector'),
'//xplat/folly:molly'
])
cxx_library(

View File

@@ -1,50 +0,0 @@
// 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

@@ -1,33 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Dispatcher.h"
#include <unordered_map>
namespace facebook {
namespace react {
/*
* An dispatcher that makes it simple to implement an agent that serves a single domain.
*/
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

@@ -1,28 +0,0 @@
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

@@ -1,60 +0,0 @@
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 = [
'//xplat/folly:molly',
react_native_xplat_target('jschelpers:jschelpers'),
],
**kwargs
)
elif THIS_IS_FBOBJC:
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",
srcs = glob(["*.cpp"]),
compiler_flags = [
"-Wall",
"-Wno-shadow",
"-Wno-inconsistent-missing-override",
"-Wno-unused-local-typedef",
"-Wno-unused-private-field",
"-Wno-undefined-bool-conversion",
"-fexceptions",
"-fvisibility=hidden",
"-std=gnu++1y",
],
exported_headers = EXPORTED_HEADERS,
header_namespace = "inspector",
headers = subdir_glob(
[("inspector", "*.h")],
excludes = EXPORTED_HEADERS,
),
preprocessor_flags = [
"-DENABLE_INSPECTOR=1",
],
visibility = [
"PUBLIC",
],
)

View File

@@ -1,152 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "ConsoleAgent.h"
#include "Protocol.h"
#include "Util.h"
#include <jschelpers/JSCHelpers.h>
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSValueRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <JavaScriptCore/JSLock.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include <JavaScriptCore/JSArray.h>
#include <JavaScriptCore/InjectedScriptManager.h>
#include <JavaScriptCore/ScriptArguments.h>
#include <JavaScriptCore/ScriptCallStack.h>
#include <JavaScriptCore/ScriptCallStackFactory.h>
#include <folly/json.h>
namespace facebook {
namespace react {
namespace {
static JSValueRef inspectorLog(
ConsoleAgent* agent,
JSContextRef ctx,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[]) {
CHECK(argumentCount == 4) << "__inspectorLog takes 4 args";
auto execState = toJS(ctx);
JSC::JSLockHolder lock(execState);
auto params = toJS(execState, arguments[2]);
agent->log(
execState,
Value(ctx, arguments[0]).toString().str(),
Value(ctx, arguments[1]).toString().str(),
JSC::asArray(params),
Value(ctx, arguments[3]).asUnsignedInteger());
return JSValueMakeUndefined(ctx);
}
size_t skipNativeCode(const Inspector::ScriptCallStack& callStack, size_t offset) {
for (; offset < callStack.size(); offset++) {
auto& frame = callStack.at(offset);
if (frame.sourceURL() != "[native code]") {
return offset;
}
}
return callStack.size();
}
const Inspector::ScriptCallFrame* firstUserFrame(const Inspector::ScriptCallStack& callStack, size_t framesToSkip) {
// Skip out of native code
size_t offset = skipNativeCode(callStack, 0);
// Skip frames of console polyfill
offset = skipNativeCode(callStack, offset + framesToSkip);
if (offset >= callStack.size()) {
return nullptr;
}
if (callStack.at(offset).functionName() == "infoLog") {
offset += 1;
}
if (offset >= callStack.size()) {
return nullptr;
}
return &callStack.at(offset);
}
}
ConsoleAgent::ConsoleAgent(JSC::JSGlobalObject& globalObject, Inspector::InjectedScriptManager* injectedScriptManager)
: globalObject_(globalObject)
, injectedScriptManager_(injectedScriptManager) {
registerMethod("enable", [this](folly::dynamic) -> folly::dynamic {
using namespace std::placeholders;
enabled_ = true;
JSGlobalContextRef context = toGlobalRef(globalObject_.globalExec());
installGlobalFunction(context, "__inspectorLog", std::bind(&inspectorLog, this, _1, _2, _3, _4));
return nullptr;
});
registerMethod("disable", [this](folly::dynamic) -> folly::dynamic {
JSGlobalContextRef context = toGlobalRef(globalObject_.globalExec());
removeGlobal(context, "__inspectorLog");
enabled_ = false;
return nullptr;
});
}
void ConsoleAgent::log(JSC::ExecState* execState, std::string message) {
log(execState, "log", std::move(message), nullptr, 0);
}
void ConsoleAgent::log(JSC::ExecState* execState, std::string level, std::string message, JSC::JSArray* params, size_t framesToSkip) {
if (!enabled_) {
return;
}
auto callStack = Inspector::createScriptCallStack(execState, Inspector::ScriptCallStack::maxCallStackSizeToCapture);
auto logEntry = folly::dynamic::object
("source", "console-api")
("level", level)
("text", std::move(message))
("timestamp", Timestamp::now())
("stackTrace", folly::parseJson(toStdString(callStack->buildInspectorArray()->toJSONString())));
if (params) {
logEntry("parameters", convertParams(execState, params));
}
if (auto frame = firstUserFrame(*callStack, framesToSkip)) {
logEntry
("url", toStdString(frame->sourceURL()))
("line", frame->lineNumber())
("column", frame->columnNumber());
}
sendEvent("messageAdded", folly::dynamic::object("message", std::move(logEntry)));
}
folly::dynamic ConsoleAgent::convertParams(JSC::ExecState* execState, JSC::JSArray* params) {
auto injectedScript = injectedScriptManager_->injectedScriptFor(execState->lexicalGlobalObject()->globalExec());
if (injectedScript.hasNoValue()) {
return nullptr;
}
folly::dynamic remoteParams = folly::dynamic::array;
for (size_t i = 0, size = params->length(); i < size; i++) {
auto scriptValue = Deprecated::ScriptValue(execState->vm(), params->getIndex(execState, i));
auto remoteValue = injectedScript.wrapObject(std::move(scriptValue), "console", true);
remoteParams.push_back(folly::parseJson(toStdString(remoteValue->toJSONString())));
}
return remoteParams;
}
}
}

View File

@@ -1,43 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Agent.h"
namespace JSC {
class JSGlobalObject;
class ExecState;
class JSArray;
}
namespace Inspector {
class InjectedScriptManager;
}
namespace facebook {
namespace react {
/**
* Implements the Console agent. Relies on Javascript to call the globally exposed method __inspectorLog
* to send logging events.
*/
class ConsoleAgent : public Agent {
public:
ConsoleAgent(JSC::JSGlobalObject& globalObject, Inspector::InjectedScriptManager* injectedScriptManager);
void log(JSC::ExecState* execState, std::string message);
void log(JSC::ExecState* execState, std::string level, std::string message, JSC::JSArray* params, size_t framesToSkip);
private:
bool enabled_{false};
JSC::JSGlobalObject& globalObject_;
Inspector::InjectedScriptManager* injectedScriptManager_;
folly::dynamic convertParams(JSC::ExecState* execState, JSC::JSArray* params);
std::string getDomain() override {
return "Console";
}
};
}
}

View File

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

View File

@@ -1,41 +0,0 @@
// 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 {
/*
* A bidrectional channel that allows both sending events to the remote inspector and registering
* to receive events for a specific domain.
*/
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;
};
/*
* A dispatcher is responsible for one or multiple domains and registering them with the Channel
* when it is connected.
*/
class Dispatcher {
public:
virtual ~Dispatcher() {}
virtual void onConnect(std::shared_ptr<Channel> channel) = 0;
virtual void onDisconnect() = 0;
};
}
}

View File

@@ -1,19 +0,0 @@
// 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

@@ -1,34 +0,0 @@
// 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

@@ -1,162 +0,0 @@
// 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

@@ -1,94 +0,0 @@
// 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;
/**
* The inspector exposes method to query for available 'pages' and connect to a specific one.
* Available Javascript contextes needs to be registered when they are created and removed when
* they are torn down.
*/
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

@@ -1,22 +0,0 @@
// 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

@@ -1,22 +0,0 @@
// 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

@@ -1,192 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "InspectorController.h"
#include "Error.h"
#include "Agent.h"
#include "LegacyInspectorEnvironment.h"
#include "InspectorAgent.h"
#include "PageAgent.h"
#include "ConsoleAgent.h"
#include "JSDispatcher.h"
#include "LegacyAgents.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 environment = folly::make_unique<LegacyInspectorEnvironment>();
auto inspectorAgent = folly::make_unique<InspectorAgent>();
inspectorAgent_ = inspectorAgent.get();
dispatchers_.push_back(std::move(inspectorAgent));
dispatchers_.push_back(folly::make_unique<SchemaAgent>());
dispatchers_.push_back(folly::make_unique<PageAgent>());
dispatchers_.push_back(folly::make_unique<JSDispatcher>(globalObject));
auto consoleAgent = folly::make_unique<ConsoleAgent>(globalObject, environment->injectedScriptManager());
auto legacyAgents = folly::make_unique<LegacyAgents>(globalObject, std::move(environment), consoleAgent.get());
dispatchers_.push_back(std::move(consoleAgent));
dispatchers_.push_back(std::move(legacyAgents));
}
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

@@ -1,42 +0,0 @@
// 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

@@ -1,135 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSDispatcher.h"
#include "Protocol.h"
#include "Util.h"
#include <jschelpers/JSCHelpers.h>
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <folly/json.h>
#include <exception>
namespace facebook {
namespace react {
namespace {
static JSValueRef nativeRegisterAgent(
JSDispatcher* agent,
JSContextRef ctx,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[]) {
CHECK(argumentCount == 1) << "__registerInspectorAgent takes 1 arg";
auto execState = toJS(ctx);
JSC::JSLockHolder lock(execState);
auto globalContext = JSContextGetGlobalContext(ctx);
agent->addAgent(
execState,
Value(globalContext, arguments[0]).asObject());
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeSendEvent(
JSDispatcher* agent,
const std::string& domain,
JSContextRef ctx,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[]) {
CHECK(argumentCount == 2) << "sendEvent takes 2 args";
auto execState = toJS(ctx);
JSC::JSLockHolder lock(execState);
auto globalContext = JSContextGetGlobalContext(ctx);
auto params = folly::parseJson(Value(globalContext, arguments[1]).toJSONString());
agent->sendEvent(
domain,
Value(globalContext, arguments[0]).toString().str(),
std::move(params));
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeInspectorTimestamp(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
return JSValueMakeNumber(ctx, Timestamp::now());
}
}
JSDispatcher::JSDispatcher(JSC::JSGlobalObject& globalObject) {
using namespace std::placeholders;
JSGlobalContextRef context = toGlobalRef(globalObject.globalExec());
installGlobalFunction(context, "__registerInspectorAgent", std::bind(&nativeRegisterAgent, this, _1, _2, _3, _4));
installGlobalFunction(context, "__inspectorTimestamp", &nativeInspectorTimestamp);
}
void JSDispatcher::onConnect(std::shared_ptr<Channel> channel) {
channel_ = std::move(channel);
for (auto& pair : agents_) {
registerAgent(pair.first);
}
}
void JSDispatcher::onDisconnect() {
channel_.reset();
}
void JSDispatcher::addAgent(JSC::ExecState* execState, Object agentType) {
auto context = agentType.context();
auto domainObject = agentType.getProperty("DOMAIN");
if (domainObject.isUndefined()) {
throw std::invalid_argument("DOMAIN should be string");
}
auto domain = domainObject.toString().str();
// Bind the domain to the send event function
using namespace std::placeholders;
Value sendEventFunction = Object(
context,
makeFunction(context, "sendEvent", std::bind(&nativeSendEvent, this, domain, _1, _2, _3, _4)));
auto agent = agentType.callAsConstructor({ sendEventFunction });
agent.makeProtected();
if (channel_) {
registerAgent(domain);
}
agents_.emplace(std::move(domain), std::move(agent));
}
void JSDispatcher::registerAgent(const std::string& name) {
channel_->registerDomain(
name,
[this, name](std::string, int callId, const std::string& methodName, folly::dynamic args) {
Object& agent = agents_.at(name);
auto context = agent.context();
JSC::JSLockHolder lock(toJS(context));
// TODO(blom): Check undefined before asObject
auto method = agent.getProperty(methodName.c_str()).asObject();
if (args.isNull()) {
args = folly::dynamic::object;
}
auto response = method.callAsFunction(agent, { Value::fromDynamic(context, args) });
auto result = (response.isUndefined() || response.isNull()) ? folly::dynamic::object() : folly::parseJson(response.toJSONString());
auto message = folly::dynamic::object("id", callId)("result", std::move(result));
channel_->sendMessage(folly::toJson(std::move(message)));
});
}
void JSDispatcher::sendEvent(std::string domain, std::string name, folly::dynamic params) {
channel_->sendMessage(Event(std::move(domain), std::move(name), std::move(params)));
}
}
}

View File

@@ -1,46 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Dispatcher.h"
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/JSValueRef.h>
#include <jschelpers/Value.h>
#include <unordered_map>
namespace JSC {
class JSGlobalObject;
class JSObject;
class ExecState;
class JSArray;
}
namespace facebook {
namespace react {
/*
* A dispatcher that allows agents to be implemented in Javascript. Provides the global method
* __registerInspectorAgent to register a JS agent.
*/
class JSDispatcher : public Dispatcher {
public:
JSDispatcher(JSC::JSGlobalObject& globalObject);
void onConnect(std::shared_ptr<Channel> channel) override;
void onDisconnect() override;
void addAgent(JSC::ExecState* execState, Object agentType);
void registerAgent(const std::string& name);
void sendEvent(std::string domain, std::string name, folly::dynamic params);
private:
std::shared_ptr<Channel> channel_;
std::unordered_map<std::string, Object> agents_;
};
}
}

View File

@@ -1,35 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "LegacyAgents.h"
#include "LegacyInspectorEnvironment.h"
#include "LegacyRuntimeAgent.h"
#include "LegacyDebuggerAgent.h"
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include <folly/Memory.h>
namespace facebook {
namespace react {
using namespace Inspector;
LegacyAgents::LegacyAgents(
JSC::JSGlobalObject& globalObject,
std::unique_ptr<LegacyInspectorEnvironment> environment,
ConsoleAgent* consoleAgent)
: LegacyDispatcher(globalObject)
, environment_(std::move(environment)) {
auto injectedScriptManager = environment_->injectedScriptManager();
auto runtimeAgent = folly::make_unique<LegacyRuntimeAgent>(injectedScriptManager, globalObject);
auto debuggerAgent = folly::make_unique<LegacyDebuggerAgent>(injectedScriptManager, globalObject, consoleAgent);
runtimeAgent->setScriptDebugServer(&debuggerAgent->scriptDebugServer());
addAgent("Runtime", std::move(runtimeAgent));
addAgent("Debugger", std::move(debuggerAgent));
}
}
}

View File

@@ -1,32 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "LegacyDispatcher.h"
namespace JSC {
class JSGlobalObject;
}
namespace facebook {
namespace react {
class LegacyInspectorEnvironment;
class ConsoleAgent;
/*
* An dispatcher that provides the existing agents in JavaScriptCore.
*/
class LegacyAgents : public LegacyDispatcher {
public:
LegacyAgents(
JSC::JSGlobalObject& globalObject,
std::unique_ptr<LegacyInspectorEnvironment> environment,
ConsoleAgent* consoleAgent);
private:
std::unique_ptr<LegacyInspectorEnvironment> environment_;
ConsoleAgent* consoleAgent_;
};
}
}

View File

@@ -1,44 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "LegacyDebuggerAgent.h"
#include "Util.h"
#include <JavaScriptCore/InjectedScript.h>
#include <JavaScriptCore/InjectedScriptManager.h>
#include <JavaScriptCore/JSGlobalObject.h>
namespace facebook {
namespace react {
using namespace Inspector;
LegacyDebuggerAgent::LegacyDebuggerAgent(InjectedScriptManager* injectedScriptManager, JSC::JSGlobalObject& globalObject, ConsoleAgent* consoleAgent)
: InspectorDebuggerAgent(injectedScriptManager)
, scriptDebugServer_(globalObject)
, consoleAgent_(consoleAgent) {}
void LegacyDebuggerAgent::startListeningScriptDebugServer() {
scriptDebugServer().addListener(this);
}
void LegacyDebuggerAgent::stopListeningScriptDebugServer(bool isBeingDestroyed) {
scriptDebugServer().removeListener(this, isBeingDestroyed);
}
InjectedScript LegacyDebuggerAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) {
if (executionContextId) {
*error = ASCIILiteral("Execution context id is not supported for JSContext inspection as there is only one execution context.");
return InjectedScript();
}
JSC::ExecState* exec = scriptDebugServer_.globalObject().globalExec();
return injectedScriptManager()->injectedScriptFor(exec);
}
void LegacyDebuggerAgent::breakpointActionLog(JSC::ExecState* exec, const String& message) {
consoleAgent_->log(exec, toStdString(message));
}
}
}

View File

@@ -1,40 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "ConsoleAgent.h"
#include "LegacyScriptDebugServer.h"
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/InspectorDebuggerAgent.h>
#include <JavaScriptCore/InspectorConsoleAgent.h>
namespace JSC {
class JSGlobalObject;
}
namespace facebook {
namespace react {
class LegacyDebuggerAgent : public Inspector::InspectorDebuggerAgent {
public:
LegacyDebuggerAgent(Inspector::InjectedScriptManager*, JSC::JSGlobalObject&, ConsoleAgent*);
virtual LegacyScriptDebugServer& scriptDebugServer() override { return scriptDebugServer_; }
virtual void startListeningScriptDebugServer() override;
virtual void stopListeningScriptDebugServer(bool isBeingDestroyed) override;
virtual Inspector::InjectedScript injectedScriptForEval(Inspector::ErrorString*, const int* executionContextId) override;
virtual void breakpointActionLog(JSC::ExecState*, const String&) override;
virtual void muteConsole() override { }
virtual void unmuteConsole() override { }
private:
LegacyScriptDebugServer scriptDebugServer_;
ConsoleAgent* consoleAgent_;
};
}
}

View File

@@ -1,54 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "LegacyDispatcher.h"
#include "Util.h"
#include <wtf/text/CString.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include <JavaScriptCore/JSLock.h>
namespace facebook {
namespace react {
using namespace Inspector;
LegacyDispatcher::FrontendChannel::FrontendChannel(std::shared_ptr<Channel> channel)
: channel_(channel) {}
bool LegacyDispatcher::FrontendChannel::sendMessageToFrontend(const WTF::String& message) {
channel_->sendMessage(toStdString(message));
return true;
}
LegacyDispatcher::LegacyDispatcher(JSC::JSGlobalObject& globalObject)
: globalObject_(globalObject) {}
void LegacyDispatcher::addAgent(std::string domain, std::unique_ptr<InspectorAgentBase> agent) {
domains_.emplace_back(std::move(domain));
agents_.append(std::move(agent));
}
void LegacyDispatcher::onConnect(std::shared_ptr<Channel> channel) {
// TODO: Should perhaps only create this once and then connect each time instead
frontendChannel_ = std::make_unique<FrontendChannel>(channel);
dispatcher_.reset(InspectorBackendDispatcher::create(frontendChannel_.get()).leakRef());
auto messageHandler = [this](std::string message, int, const std::string&, folly::dynamic) {
JSC::JSLockHolder lock(globalObject_.globalExec());
dispatcher_->dispatch(message.c_str());
};
for (auto& domain : domains_) {
channel->registerDomain(domain, messageHandler);
}
agents_.didCreateFrontendAndBackend(frontendChannel_.get(), dispatcher_.get());
}
void LegacyDispatcher::onDisconnect() {
// TODO: Perhaps support InspectedTargetDestroyed
agents_.willDestroyFrontendAndBackend(InspectorDisconnectReason::InspectorDestroyed);
}
}
}

View File

@@ -1,52 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Dispatcher.h"
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/InspectorAgentRegistry.h>
#include <JavaScriptCore/InspectorAgentBase.h>
#include <JavaScriptCore/InspectorFrontendChannel.h>
#include <JavaScriptCore/InspectorBackendDispatcher.h>
#include <memory>
#include <vector>
namespace JSC {
class JSGlobalObject;
}
namespace facebook {
namespace react {
/*
* An dispatcher that is able to register JavaScriptCore agents that extend the InspectorAgentBase
* base class.
*/
class LegacyDispatcher : public Dispatcher {
public:
LegacyDispatcher(JSC::JSGlobalObject& globalObject);
void addAgent(std::string domain, std::unique_ptr<Inspector::InspectorAgentBase> agent);
void onConnect(std::shared_ptr<Channel> channel) override;
void onDisconnect() override;
private:
class FrontendChannel : public Inspector::InspectorFrontendChannel {
public:
FrontendChannel(std::shared_ptr<Channel> channel);
bool sendMessageToFrontend(const WTF::String& message) override;
private:
std::shared_ptr<Channel> channel_;
};
JSC::JSGlobalObject& globalObject_;
std::vector<std::string> domains_;
Inspector::InspectorAgentRegistry agents_;
std::unique_ptr<FrontendChannel> frontendChannel_;
std::unique_ptr<Inspector::InspectorBackendDispatcher> dispatcher_;
};
}
}

View File

@@ -1,34 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "LegacyInspectorEnvironment.h"
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/Completion.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include <JavaScriptCore/InjectedScriptManager.h>
#include <JavaScriptCore/InjectedScriptHost.h>
#include <folly/Memory.h>
namespace facebook {
namespace react {
using namespace Inspector;
LegacyInspectorEnvironment::LegacyInspectorEnvironment()
: injectedScriptManager_(folly::make_unique<InjectedScriptManager>(*this, InjectedScriptHost::create())) {}
LegacyInspectorEnvironment::~LegacyInspectorEnvironment() {
injectedScriptManager_->disconnect();
}
InspectorFunctionCallHandler LegacyInspectorEnvironment::functionCallHandler() const {
return JSC::call;
}
InspectorEvaluateHandler LegacyInspectorEnvironment::evaluateHandler() const {
return JSC::evaluate;
}
}
}

View File

@@ -1,35 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/InspectorEnvironment.h>
namespace Inspector {
class InjectedScriptManager;
}
namespace facebook {
namespace react {
class LegacyInspectorEnvironment : public Inspector::InspectorEnvironment {
public:
LegacyInspectorEnvironment();
~LegacyInspectorEnvironment();
Inspector::InjectedScriptManager* injectedScriptManager() const {
return injectedScriptManager_.get();
}
private:
std::unique_ptr<Inspector::InjectedScriptManager> injectedScriptManager_;
bool developerExtrasEnabled() const override { return true; }
bool canAccessInspectedScriptState(JSC::ExecState*) const override { return true; }
Inspector::InspectorFunctionCallHandler functionCallHandler() const override;
Inspector::InspectorEvaluateHandler evaluateHandler() const override;
void willCallInjectedScriptFunction(JSC::ExecState*, const WTF::String& scriptName, int scriptLine) override {};
void didCallInjectedScriptFunction(JSC::ExecState*) override {}
};
}
}

View File

@@ -1,63 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "LegacyRuntimeAgent.h"
#include <JavaScriptCore/InjectedScript.h>
#include <JavaScriptCore/InjectedScriptManager.h>
#include <JavaScriptCore/JSGlobalObject.h>
namespace facebook {
namespace react {
using namespace Inspector;
LegacyRuntimeAgent::LegacyRuntimeAgent(InjectedScriptManager* injectedScriptManager, JSC::JSGlobalObject& globalObject)
: InspectorRuntimeAgent(injectedScriptManager)
, m_globalObject(globalObject) {}
void LegacyRuntimeAgent::didCreateFrontendAndBackend(InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) {
// m_frontendDispatcher = folly::make_unique<InspectorRuntimeFrontendDispatcher>(frontendChannel);
frontendChannel_ = frontendChannel;
m_backendDispatcher.reset(InspectorRuntimeBackendDispatcher::create(backendDispatcher, this).leakRef());
}
void LegacyRuntimeAgent::enable(ErrorString* error) {
InspectorRuntimeAgent::enable(error);
auto contextObject = InspectorObject::create();
contextObject->setNumber(ASCIILiteral("id"), 1);
contextObject->setBoolean(ASCIILiteral("isDefault"), true);
contextObject->setBoolean(ASCIILiteral("isPageContext"), true);
contextObject->setString(ASCIILiteral("origin"), ASCIILiteral(""));
contextObject->setString(ASCIILiteral("name"), ASCIILiteral("React Native"));
auto jsonMessage = InspectorObject::create();
jsonMessage->setString(ASCIILiteral("method"), ASCIILiteral("Runtime.executionContextCreated"));
auto paramsObject = InspectorObject::create();
paramsObject->setValue(ASCIILiteral("context"), contextObject);
jsonMessage->setObject(ASCIILiteral("params"), paramsObject);
frontendChannel_->sendMessageToFrontend(jsonMessage->toJSONString());
}
void LegacyRuntimeAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) {
frontendChannel_ = nullptr;
m_backendDispatcher = nullptr;
}
JSC::VM& LegacyRuntimeAgent::globalVM() {
return m_globalObject.vm();
}
InjectedScript LegacyRuntimeAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) {
JSC::ExecState* scriptState = m_globalObject.globalExec();
InjectedScript injectedScript = injectedScriptManager()->injectedScriptFor(scriptState);
if (injectedScript.hasNoValue()) {
*error = ASCIILiteral("Internal error: main world execution context not found.");
}
return injectedScript;
}
}
}

View File

@@ -1,39 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/InspectorRuntimeAgent.h>
namespace JSC {
class JSGlobalObject;
}
namespace facebook {
namespace react {
class LegacyRuntimeAgent : public Inspector::InspectorRuntimeAgent {
public:
LegacyRuntimeAgent(Inspector::InjectedScriptManager*, JSC::JSGlobalObject&);
void enable(Inspector::ErrorString* error) override;
void didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel*, Inspector::InspectorBackendDispatcher*) override;
void willDestroyFrontendAndBackend(Inspector::InspectorDisconnectReason) override;
JSC::VM& globalVM() override;
Inspector::InjectedScript injectedScriptForEval(Inspector::ErrorString* error, const int* executionContextId) override;
void muteConsole() override { }
void unmuteConsole() override { }
private:
Inspector::InspectorFrontendChannel* frontendChannel_;
// std::unique_ptr<Inspector::InspectorRuntimeFrontendDispatcher> m_frontendDispatcher;
std::unique_ptr<Inspector::InspectorRuntimeBackendDispatcher> m_backendDispatcher;
JSC::JSGlobalObject& m_globalObject;
};
}
}

View File

@@ -1,64 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "LegacyScriptDebugServer.h"
#include <JavaScriptCore/JSGlobalObject.h>
namespace facebook {
namespace react {
using namespace Inspector;
LegacyScriptDebugServer::LegacyScriptDebugServer(JSC::JSGlobalObject& globalObject)
: Inspector::ScriptDebugServer(false)
, globalObject_(globalObject) {}
void LegacyScriptDebugServer::addListener(ScriptDebugListener* listener)
{
if (!listener) {
return;
}
bool wasEmpty = listeners_.isEmpty();
listeners_.add(listener);
// First listener. Attach the debugger to the JSGlobalObject.
if (wasEmpty) {
attach(&globalObject_);
recompileAllJSFunctions();
}
}
void LegacyScriptDebugServer::removeListener(ScriptDebugListener* listener, bool isBeingDestroyed) {
if (!listener) {
return;
}
listeners_.remove(listener);
// Last listener. Detach the debugger from the JSGlobalObject.
if (listeners_.isEmpty()) {
detach(&globalObject_, isBeingDestroyed ? Debugger::GlobalObjectIsDestructing : Debugger::TerminatingDebuggingSession);
if (!isBeingDestroyed) {
recompileAllJSFunctions();
}
}
}
void LegacyScriptDebugServer::recompileAllJSFunctions() {
JSC::Debugger::recompileAllJSFunctions(&globalObject_.vm());
}
void LegacyScriptDebugServer::runEventLoopWhilePaused() {
// Drop all locks so another thread can work in the VM while we are nested.
JSC::JSLock::DropAllLocks dropAllLocks(&globalObject_.vm());
// Spinning here is our best option, we could override the method
// notifyDoneProcessingDebuggerEvents but it's marked as final :(
while (!m_doneProcessingDebuggerEvents) {
usleep(10 * 1000);
}
}
}
}

View File

@@ -1,45 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/config.h>
#include <JavaScriptCore/ScriptDebugServer.h>
namespace JSC {
class JSGlobalObject;
}
namespace facebook {
namespace react {
class LegacyScriptDebugServer : public Inspector::ScriptDebugServer {
public:
LegacyScriptDebugServer(JSC::JSGlobalObject& object);
void addListener(Inspector::ScriptDebugListener* listener);
void removeListener(Inspector::ScriptDebugListener* listener, bool isBeingDestroyed);
JSC::JSGlobalObject& globalObject() const { return globalObject_; }
void recompileAllJSFunctions() override;
private:
ListenerSet* getListenersForGlobalObject(JSC::JSGlobalObject*) override { return &listeners_; }
void didPause(JSC::JSGlobalObject*) override { }
void didContinue(JSC::JSGlobalObject*) override { }
void runEventLoopWhilePaused() override;
bool isContentScript(JSC::ExecState*) const override { return false; }
// NOTE: Currently all exceptions are reported at the API boundary through reportAPIException.
// Until a time comes where an exception can be caused outside of the API (e.g. setTimeout
// or some other async operation in a pure JSContext) we can ignore exceptions reported here.
// TODO: Should we actually ignore them?
void reportException(JSC::ExecState*, JSC::JSValue) const override { }
ListenerSet listeners_;
JSC::JSGlobalObject& globalObject_;
};
}
}

View File

@@ -1,31 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "PageAgent.h"
namespace facebook {
namespace react {
PageAgent::PageAgent() {
auto emptyMethod = [](folly::dynamic args) -> folly::dynamic {
return nullptr;
};
registerMethod("enable", emptyMethod);
registerMethod("disable", emptyMethod);
registerMethod("getResourceTree", [](folly::dynamic args) -> folly::dynamic {
return folly::dynamic::object
("frameTree", folly::dynamic::object
("childFrames", folly::dynamic::array)
("resources", folly::dynamic::array)
("frame", folly::dynamic::object
("id", "1")
("loaderId", "1")
("name", "main")
("url", "")
("securityOrigin", "")
("mimeType", "application/octet-stream")));
});
}
}
}

View File

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

View File

@@ -1,86 +0,0 @@
// 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

@@ -1,77 +0,0 @@
// 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

@@ -1,17 +0,0 @@
# Inspector
This directory implements an the Chrome debugging protocol [1]. The version used is roughly 1.1 of
the protocol. The code here doesn't specify a transport and doesn't implement an actual server. This
is left up to higher parts of the stack.
The implementation uses multiple "dispatchers" to route messages for a specific domain. It reuses
existing code in JavaScriptCore to handle the domains for Debugger and Runtime. For Console, Page
and Inspector there are new implementations.
## Open source
The inspector currently doesn't compile in open source. This is due to how the build on Android
where we download the JSC sources and build an artifact separately, later download the headers we
need. The number of headers download would have to be expanded and verify that it builds correctly.
[1]: https://developer.chrome.com/devtools/docs/debugger-protocol

View File

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

View File

@@ -1,45 +0,0 @@
// 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_;
};
}
}