mirror of
https://github.com/status-im/react-native.git
synced 2025-01-09 09:12:02 +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
163 lines
4.6 KiB
C++
163 lines
4.6 KiB
C++
// 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();
|
|
}
|
|
|
|
}
|
|
}
|