mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
d7f5153cd8
Summary: Yesterday we shipped hermesengine.dev as part of the current 0.60 release. This PR brings those changes to master. ## Changelog [General] [Added] - Added support for Hermes Pull Request resolved: https://github.com/facebook/react-native/pull/25613 Test Plan: * CI is green both on GitHub and at FB * Creating a new app from source can use Hermes on Android Reviewed By: cpojer Differential Revision: D16221777 Pulled By: willholen fbshipit-source-id: aa6be10537863039cb666292465ba2e1d44b64ef
669 lines
21 KiB
C++
669 lines
21 KiB
C++
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
#include "Connection.h"
|
|
|
|
#include <cstdlib>
|
|
#include <mutex>
|
|
|
|
#include <folly/Conv.h>
|
|
#include <folly/Executor.h>
|
|
#include <folly/Function.h>
|
|
#include <glog/logging.h>
|
|
#include <hermes/inspector/Inspector.h>
|
|
#include <hermes/inspector/chrome/MessageConverters.h>
|
|
#include <hermes/inspector/chrome/RemoteObjectsTable.h>
|
|
#include <hermes/inspector/detail/SerialExecutor.h>
|
|
#include <hermes/inspector/detail/Thread.h>
|
|
|
|
namespace facebook {
|
|
namespace hermes {
|
|
namespace inspector {
|
|
namespace chrome {
|
|
|
|
using ::facebook::react::ILocalConnection;
|
|
using ::facebook::react::IRemoteConnection;
|
|
using ::folly::Unit;
|
|
|
|
namespace debugger = ::facebook::hermes::debugger;
|
|
namespace inspector = ::facebook::hermes::inspector;
|
|
namespace m = ::facebook::hermes::inspector::chrome::message;
|
|
|
|
/*
|
|
* Connection::Impl
|
|
*/
|
|
|
|
class Connection::Impl : public inspector::InspectorObserver,
|
|
public message::RequestHandler {
|
|
public:
|
|
Impl(
|
|
std::unique_ptr<RuntimeAdapter> adapter,
|
|
const std::string &title,
|
|
bool waitForDebugger);
|
|
~Impl();
|
|
|
|
HermesRuntime &getRuntime();
|
|
std::string getTitle() const;
|
|
|
|
bool connect(std::unique_ptr<IRemoteConnection> remoteConn);
|
|
bool disconnect();
|
|
void sendMessage(std::string str);
|
|
|
|
/* InspectorObserver overrides */
|
|
void onBreakpointResolved(
|
|
Inspector &inspector,
|
|
const debugger::BreakpointInfo &info) override;
|
|
void onContextCreated(Inspector &inspector) override;
|
|
void onPause(Inspector &inspector, const debugger::ProgramState &state)
|
|
override;
|
|
void onResume(Inspector &inspector) override;
|
|
void onScriptParsed(Inspector &inspector, const ScriptInfo &info) override;
|
|
void onMessageAdded(Inspector &inspector, const ConsoleMessageInfo &info)
|
|
override;
|
|
|
|
/* RequestHandler overrides */
|
|
void handle(const m::UnknownRequest &req) override;
|
|
void handle(const m::debugger::DisableRequest &req) override;
|
|
void handle(const m::debugger::EnableRequest &req) override;
|
|
void handle(const m::debugger::EvaluateOnCallFrameRequest &req) override;
|
|
void handle(const m::debugger::PauseRequest &req) override;
|
|
void handle(const m::debugger::RemoveBreakpointRequest &req) override;
|
|
void handle(const m::debugger::ResumeRequest &req) override;
|
|
void handle(const m::debugger::SetBreakpointByUrlRequest &req) override;
|
|
void handle(const m::debugger::SetPauseOnExceptionsRequest &req) override;
|
|
void handle(const m::debugger::StepIntoRequest &req) override;
|
|
void handle(const m::debugger::StepOutRequest &req) override;
|
|
void handle(const m::debugger::StepOverRequest &req) override;
|
|
void handle(const m::runtime::EvaluateRequest &req) override;
|
|
void handle(const m::runtime::GetPropertiesRequest &req) override;
|
|
|
|
private:
|
|
std::vector<m::runtime::PropertyDescriptor> makePropsFromScope(
|
|
std::pair<uint32_t, uint32_t> frameAndScopeIndex,
|
|
const std::string &objectGroup,
|
|
const debugger::ProgramState &state);
|
|
std::vector<m::runtime::PropertyDescriptor> makePropsFromValue(
|
|
const jsi::Value &value,
|
|
const std::string &objectGroup,
|
|
bool onlyOwnProperties);
|
|
|
|
void sendToClient(const std::string &str);
|
|
void sendResponseToClient(const m::Response &resp);
|
|
folly::Function<void(const std::exception &)> sendErrorToClient(int id);
|
|
void sendResponseToClientViaExecutor(int id);
|
|
void sendResponseToClientViaExecutor(folly::Future<Unit> future, int id);
|
|
void sendNotificationToClientViaExecutor(const m::Notification ¬e);
|
|
|
|
std::shared_ptr<RuntimeAdapter> runtimeAdapter_;
|
|
std::string title_;
|
|
|
|
// connected_ is protected by connectionMutex_.
|
|
std::mutex connectionMutex_;
|
|
bool connected_;
|
|
|
|
// parsedScripts_ list stores file names of all scripts that have been
|
|
// parsed so that we could find script's file name by regex.
|
|
// This is similar to Inspector's loadedScripts_ map but we want to
|
|
// store this info here because searching file name that matches
|
|
// given regex (on setBreakpointByUrl command) is more related to Chrome
|
|
// protocol than to Hermes inspector.
|
|
// Access is protected by parsedScriptsMutex_.
|
|
std::mutex parsedScriptsMutex_;
|
|
std::vector<std::string> parsedScripts_;
|
|
|
|
// The rest of these member variables are only accessed via executor_.
|
|
std::unique_ptr<folly::Executor> executor_;
|
|
std::unique_ptr<IRemoteConnection> remoteConn_;
|
|
std::shared_ptr<inspector::Inspector> inspector_;
|
|
|
|
// objTable_ is protected by the inspector lock. It should only be accessed
|
|
// when the VM is paused, e.g. in an InspectorObserver callback or in an
|
|
// executeIfEnabled callback.
|
|
RemoteObjectsTable objTable_;
|
|
};
|
|
|
|
Connection::Impl::Impl(
|
|
std::unique_ptr<RuntimeAdapter> adapter,
|
|
const std::string &title,
|
|
bool waitForDebugger)
|
|
: runtimeAdapter_(std::move(adapter)),
|
|
title_(title),
|
|
connected_(false),
|
|
executor_(std::make_unique<inspector::detail::SerialExecutor>(
|
|
"hermes-chrome-inspector-conn")),
|
|
remoteConn_(nullptr),
|
|
inspector_(std::make_shared<inspector::Inspector>(
|
|
runtimeAdapter_,
|
|
*this,
|
|
waitForDebugger)) {
|
|
inspector_->installLogHandler();
|
|
}
|
|
|
|
Connection::Impl::~Impl() = default;
|
|
|
|
HermesRuntime &Connection::Impl::getRuntime() {
|
|
return runtimeAdapter_->getRuntime();
|
|
}
|
|
|
|
std::string Connection::Impl::getTitle() const {
|
|
return title_;
|
|
}
|
|
|
|
bool Connection::Impl::connect(std::unique_ptr<IRemoteConnection> remoteConn) {
|
|
assert(remoteConn);
|
|
std::lock_guard<std::mutex> lock(connectionMutex_);
|
|
|
|
if (connected_) {
|
|
return false;
|
|
}
|
|
|
|
connected_ = true;
|
|
executor_->add([this, remoteConn = std::move(remoteConn)]() mutable {
|
|
remoteConn_ = std::move(remoteConn);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Connection::Impl::disconnect() {
|
|
std::lock_guard<std::mutex> lock(connectionMutex_);
|
|
|
|
if (!connected_) {
|
|
return false;
|
|
}
|
|
|
|
connected_ = false;
|
|
|
|
inspector_->disable().via(executor_.get()).thenValue([this](auto &&) {
|
|
// HACK: We purposely call RemoteConnection::onDisconnect on a *different*
|
|
// rather than on this thread (the executor thread). This is to prevent this
|
|
// scenario:
|
|
//
|
|
// 1. RemoteConnection::onDisconnect runs on the executor thread
|
|
// 2. onDisconnect through a long chain of calls causes the Connection
|
|
// destructor to run
|
|
// 3. The Connection destructor causes the SerialExecutor destructor to run.
|
|
// 4. The SerialExecutor destructor waits for all outstanding work items to
|
|
// finish via a call to join().
|
|
// 5. join() fails, since the executor thread is trying to join against
|
|
// itself.
|
|
//
|
|
// To prevent this chain of events, we always call onDisconnect on a
|
|
// different thread.
|
|
//
|
|
// See P59135203 for an example stack trace.
|
|
//
|
|
// One more hack: we use release() and delete instead of unique_ptr because
|
|
// detail::Thread expects a std::function, and std::function cannot capture
|
|
// move-only types like unique_ptr.
|
|
auto conn = remoteConn_.release();
|
|
inspector::detail::Thread disconnectLaterThread{
|
|
"hermes-chrome-inspector-conn-disconnect", [conn] {
|
|
conn->onDisconnect();
|
|
delete conn;
|
|
}};
|
|
disconnectLaterThread.detach();
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
void Connection::Impl::sendMessage(std::string str) {
|
|
executor_->add([this, str = std::move(str)]() mutable {
|
|
folly::Try<std::unique_ptr<m::Request>> maybeReq =
|
|
m::Request::fromJson(str);
|
|
|
|
if (maybeReq.hasException()) {
|
|
LOG(ERROR) << "Invalid request `" << str
|
|
<< "`: " << maybeReq.exception().what();
|
|
return;
|
|
}
|
|
|
|
auto &req = maybeReq.value();
|
|
if (req) {
|
|
req->accept(*this);
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
* InspectorObserver overrides
|
|
*/
|
|
|
|
void Connection::Impl::onBreakpointResolved(
|
|
Inspector &inspector,
|
|
const debugger::BreakpointInfo &info) {
|
|
m::debugger::BreakpointResolvedNotification note;
|
|
note.breakpointId = folly::to<std::string>(info.id);
|
|
note.location = m::debugger::makeLocation(info.resolvedLocation);
|
|
sendNotificationToClientViaExecutor(note);
|
|
}
|
|
|
|
void Connection::Impl::onContextCreated(Inspector &inspector) {
|
|
// Right now, Hermes only has the notion of one JS context per VM instance,
|
|
// so we just always name the single JS context with id=1 and name=hermes.
|
|
m::runtime::ExecutionContextCreatedNotification note;
|
|
note.context.id = 1;
|
|
note.context.name = "hermes";
|
|
|
|
// isDefault and isPageContext are custom properties that the legacy RN to
|
|
// JSC adapter set for some unknown reason.
|
|
note.context.isDefault = true;
|
|
note.context.isPageContext = true;
|
|
|
|
sendNotificationToClientViaExecutor(note);
|
|
}
|
|
|
|
void Connection::Impl::onPause(
|
|
Inspector &inspector,
|
|
const debugger::ProgramState &state) {
|
|
m::debugger::PausedNotification note;
|
|
note.callFrames = m::debugger::makeCallFrames(state, objTable_, getRuntime());
|
|
|
|
switch (state.getPauseReason()) {
|
|
case debugger::PauseReason::Breakpoint:
|
|
// use other, chrome protocol has no reason specifically for breakpoints
|
|
note.reason = "other";
|
|
|
|
// TODO: hermes hasn't implemented ProgramState::getBreakpoint yet
|
|
#if HERMES_SUPPORTS_STATE_GET_BREAKPOINT
|
|
note.hitBreakpoints = std::vector<m::debugger::BreakpointId>();
|
|
note.hitBreakpoints->emplace_back(
|
|
folly::to<std::string>(state.getBreakpoint()));
|
|
#endif
|
|
|
|
break;
|
|
case debugger::PauseReason::Exception:
|
|
note.reason = "exception";
|
|
break;
|
|
default:
|
|
note.reason = "other";
|
|
break;
|
|
}
|
|
|
|
sendNotificationToClientViaExecutor(note);
|
|
}
|
|
|
|
void Connection::Impl::onResume(Inspector &inspector) {
|
|
objTable_.releaseObjectGroup(BacktraceObjectGroup);
|
|
|
|
m::debugger::ResumedNotification note;
|
|
sendNotificationToClientViaExecutor(note);
|
|
}
|
|
|
|
void Connection::Impl::onScriptParsed(
|
|
Inspector &inspector,
|
|
const ScriptInfo &info) {
|
|
m::debugger::ScriptParsedNotification note;
|
|
note.scriptId = folly::to<std::string>(info.fileId);
|
|
note.url = info.fileName;
|
|
|
|
if (!info.sourceMappingUrl.empty()) {
|
|
note.sourceMapURL = info.sourceMappingUrl;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(parsedScriptsMutex_);
|
|
parsedScripts_.push_back(info.fileName);
|
|
}
|
|
|
|
sendNotificationToClientViaExecutor(note);
|
|
}
|
|
|
|
void Connection::Impl::onMessageAdded(
|
|
facebook::hermes::inspector::Inspector &inspector,
|
|
const ConsoleMessageInfo &info) {
|
|
m::runtime::ConsoleAPICalledNotification apiCalledNote;
|
|
apiCalledNote.type = info.level;
|
|
|
|
size_t argsSize = info.args.size(getRuntime());
|
|
for (size_t index = 0; index < argsSize; ++index) {
|
|
apiCalledNote.args.push_back(m::runtime::makeRemoteObject(
|
|
getRuntime(),
|
|
info.args.getValueAtIndex(getRuntime(), index),
|
|
objTable_,
|
|
"ConsoleObjectGroup"));
|
|
}
|
|
|
|
sendNotificationToClientViaExecutor(apiCalledNote);
|
|
}
|
|
|
|
/*
|
|
* RequestHandler overrides
|
|
*/
|
|
|
|
void Connection::Impl::handle(const m::UnknownRequest &req) {
|
|
LOG(INFO) << "responding ok to unknown request: " << req.toDynamic();
|
|
sendResponseToClientViaExecutor(req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::DisableRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->disable(), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::EnableRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->enable(), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(
|
|
const m::debugger::EvaluateOnCallFrameRequest &req) {
|
|
auto remoteObjPtr = std::make_shared<m::runtime::RemoteObject>();
|
|
|
|
inspector_
|
|
->evaluate(
|
|
atoi(req.callFrameId.c_str()),
|
|
req.expression,
|
|
[this, remoteObjPtr, objectGroup = req.objectGroup](
|
|
const facebook::hermes::debugger::EvalResult
|
|
&evalResult) mutable {
|
|
*remoteObjPtr = m::runtime::makeRemoteObject(
|
|
getRuntime(),
|
|
evalResult.value,
|
|
objTable_,
|
|
objectGroup.value_or(""));
|
|
})
|
|
.via(executor_.get())
|
|
.thenValue(
|
|
[this, id = req.id, remoteObjPtr](debugger::EvalResult result) {
|
|
m::debugger::EvaluateOnCallFrameResponse resp;
|
|
resp.id = id;
|
|
|
|
if (result.isException) {
|
|
resp.exceptionDetails =
|
|
m::runtime::makeExceptionDetails(result.exceptionDetails);
|
|
} else {
|
|
resp.result = *remoteObjPtr;
|
|
}
|
|
|
|
sendResponseToClient(resp);
|
|
})
|
|
.thenError<std::exception>(sendErrorToClient(req.id));
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::runtime::EvaluateRequest &req) {
|
|
auto remoteObjPtr = std::make_shared<m::runtime::RemoteObject>();
|
|
|
|
inspector_
|
|
->evaluate(
|
|
0, // Top of the stackframe
|
|
req.expression,
|
|
[this, remoteObjPtr, objectGroup = req.objectGroup](
|
|
const facebook::hermes::debugger::EvalResult
|
|
&evalResult) mutable {
|
|
*remoteObjPtr = m::runtime::makeRemoteObject(
|
|
getRuntime(),
|
|
evalResult.value,
|
|
objTable_,
|
|
objectGroup.value_or("ConsoleObjectGroup"));
|
|
})
|
|
.via(executor_.get())
|
|
.thenValue(
|
|
[this, id = req.id, remoteObjPtr](debugger::EvalResult result) {
|
|
m::debugger::EvaluateOnCallFrameResponse resp;
|
|
resp.id = id;
|
|
|
|
if (result.isException) {
|
|
resp.exceptionDetails =
|
|
m::runtime::makeExceptionDetails(result.exceptionDetails);
|
|
} else {
|
|
resp.result = *remoteObjPtr;
|
|
}
|
|
|
|
sendResponseToClient(resp);
|
|
})
|
|
.thenError<std::exception>(sendErrorToClient(req.id));
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::PauseRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->pause(), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::RemoveBreakpointRequest &req) {
|
|
auto breakpointId = folly::to<debugger::BreakpointID>(req.breakpointId);
|
|
sendResponseToClientViaExecutor(
|
|
inspector_->removeBreakpoint(breakpointId), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::ResumeRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->resume(), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(
|
|
const m::debugger::SetBreakpointByUrlRequest &req) {
|
|
debugger::SourceLocation loc;
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(parsedScriptsMutex_);
|
|
setHermesLocation(loc, req, parsedScripts_);
|
|
}
|
|
|
|
inspector_->setBreakpoint(loc, req.condition)
|
|
.via(executor_.get())
|
|
.thenValue([this, id = req.id](debugger::BreakpointInfo info) {
|
|
m::debugger::SetBreakpointByUrlResponse resp;
|
|
resp.id = id;
|
|
resp.breakpointId = folly::to<std::string>(info.id);
|
|
|
|
if (info.resolved) {
|
|
resp.locations.emplace_back(
|
|
m::debugger::makeLocation(info.resolvedLocation));
|
|
}
|
|
|
|
sendResponseToClient(resp);
|
|
})
|
|
.thenError<std::exception>(sendErrorToClient(req.id));
|
|
}
|
|
|
|
void Connection::Impl::handle(
|
|
const m::debugger::SetPauseOnExceptionsRequest &req) {
|
|
debugger::PauseOnThrowMode mode = debugger::PauseOnThrowMode::None;
|
|
|
|
if (req.state == "none") {
|
|
mode = debugger::PauseOnThrowMode::None;
|
|
} else if (req.state == "all") {
|
|
mode = debugger::PauseOnThrowMode::All;
|
|
} else if (req.state == "uncaught") {
|
|
mode = debugger::PauseOnThrowMode::Uncaught;
|
|
} else {
|
|
sendErrorToClient(req.id);
|
|
}
|
|
|
|
sendResponseToClientViaExecutor(
|
|
inspector_->setPauseOnExceptions(mode), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::StepIntoRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->stepIn(), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::StepOutRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->stepOut(), req.id);
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::debugger::StepOverRequest &req) {
|
|
sendResponseToClientViaExecutor(inspector_->stepOver(), req.id);
|
|
}
|
|
|
|
std::vector<m::runtime::PropertyDescriptor>
|
|
Connection::Impl::makePropsFromScope(
|
|
std::pair<uint32_t, uint32_t> frameAndScopeIndex,
|
|
const std::string &objectGroup,
|
|
const debugger::ProgramState &state) {
|
|
std::vector<m::runtime::PropertyDescriptor> result;
|
|
|
|
uint32_t frameIndex = frameAndScopeIndex.first;
|
|
uint32_t scopeIndex = frameAndScopeIndex.second;
|
|
debugger::LexicalInfo lexicalInfo = state.getLexicalInfo(frameIndex);
|
|
uint32_t varCount = lexicalInfo.getVariablesCountInScope(scopeIndex);
|
|
|
|
for (uint32_t varIndex = 0; varIndex < varCount; varIndex++) {
|
|
debugger::VariableInfo varInfo =
|
|
state.getVariableInfo(frameIndex, scopeIndex, varIndex);
|
|
|
|
m::runtime::PropertyDescriptor desc;
|
|
desc.name = varInfo.name;
|
|
desc.value = m::runtime::makeRemoteObject(
|
|
getRuntime(), varInfo.value, objTable_, objectGroup);
|
|
|
|
result.emplace_back(std::move(desc));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<m::runtime::PropertyDescriptor>
|
|
Connection::Impl::makePropsFromValue(
|
|
const jsi::Value &value,
|
|
const std::string &objectGroup,
|
|
bool onlyOwnProperties) {
|
|
std::vector<m::runtime::PropertyDescriptor> result;
|
|
|
|
if (value.isObject()) {
|
|
HermesRuntime &runtime = getRuntime();
|
|
jsi::Object obj = value.getObject(runtime);
|
|
|
|
// TODO(hypuk): obj.getPropertyNames only returns enumerable properties.
|
|
jsi::Array propNames = onlyOwnProperties
|
|
? runtime.global()
|
|
.getPropertyAsObject(runtime, "Object")
|
|
.getPropertyAsFunction(runtime, "getOwnPropertyNames")
|
|
.call(runtime, obj)
|
|
.getObject(runtime)
|
|
.getArray(runtime)
|
|
: obj.getPropertyNames(runtime);
|
|
|
|
size_t propCount = propNames.length(runtime);
|
|
for (size_t i = 0; i < propCount; i++) {
|
|
jsi::String propName =
|
|
propNames.getValueAtIndex(runtime, i).getString(runtime);
|
|
|
|
m::runtime::PropertyDescriptor desc;
|
|
desc.name = propName.utf8(runtime);
|
|
|
|
jsi::Value propValue = obj.getProperty(runtime, propName);
|
|
desc.value = m::runtime::makeRemoteObject(
|
|
runtime, propValue, objTable_, objectGroup);
|
|
|
|
result.emplace_back(std::move(desc));
|
|
}
|
|
|
|
if (onlyOwnProperties) {
|
|
jsi::Value proto = runtime.global()
|
|
.getPropertyAsObject(runtime, "Object")
|
|
.getPropertyAsFunction(runtime, "getPrototypeOf")
|
|
.call(runtime, obj);
|
|
if (!proto.isNull()) {
|
|
m::runtime::PropertyDescriptor desc;
|
|
desc.name = "__proto__";
|
|
desc.value = m::runtime::makeRemoteObject(
|
|
runtime, proto, objTable_, objectGroup);
|
|
result.emplace_back(std::move(desc));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) {
|
|
auto resp = std::make_shared<m::runtime::GetPropertiesResponse>();
|
|
resp->id = req.id;
|
|
|
|
inspector_
|
|
->executeIfEnabled(
|
|
"Runtime.getProperties",
|
|
[this, req, resp](const debugger::ProgramState &state) {
|
|
std::string objGroup = objTable_.getObjectGroup(req.objectId);
|
|
auto scopePtr = objTable_.getScope(req.objectId);
|
|
auto valuePtr = objTable_.getValue(req.objectId);
|
|
|
|
if (scopePtr != nullptr) {
|
|
resp->result = makePropsFromScope(*scopePtr, objGroup, state);
|
|
} else if (valuePtr != nullptr) {
|
|
resp->result = makePropsFromValue(
|
|
*valuePtr, objGroup, req.ownProperties.value_or(true));
|
|
}
|
|
})
|
|
.via(executor_.get())
|
|
.thenValue([this, resp](auto &&) { sendResponseToClient(*resp); })
|
|
.thenError<std::exception>(sendErrorToClient(req.id));
|
|
}
|
|
|
|
/*
|
|
* Send-to-client methods
|
|
*/
|
|
|
|
void Connection::Impl::sendToClient(const std::string &str) {
|
|
if (remoteConn_) {
|
|
remoteConn_->onMessage(str);
|
|
}
|
|
}
|
|
|
|
void Connection::Impl::sendResponseToClient(const m::Response &resp) {
|
|
sendToClient(resp.toJson());
|
|
}
|
|
|
|
folly::Function<void(const std::exception &)>
|
|
Connection::Impl::sendErrorToClient(int id) {
|
|
return [this, id](const std::exception &e) {
|
|
sendResponseToClient(
|
|
m::makeErrorResponse(id, m::ErrorCode::ServerError, e.what()));
|
|
};
|
|
}
|
|
|
|
void Connection::Impl::sendResponseToClientViaExecutor(int id) {
|
|
sendResponseToClientViaExecutor(folly::makeFuture(), id);
|
|
}
|
|
|
|
void Connection::Impl::sendResponseToClientViaExecutor(
|
|
folly::Future<Unit> future,
|
|
int id) {
|
|
future.via(executor_.get())
|
|
.thenValue([this, id](const Unit &unit) {
|
|
sendResponseToClient(m::makeOkResponse(id));
|
|
})
|
|
.thenError<std::exception>(sendErrorToClient(id));
|
|
}
|
|
|
|
void Connection::Impl::sendNotificationToClientViaExecutor(
|
|
const m::Notification ¬e) {
|
|
executor_->add(
|
|
[this, noteJson = note.toJson()]() { sendToClient(noteJson); });
|
|
}
|
|
|
|
/*
|
|
* Connection
|
|
*/
|
|
Connection::Connection(
|
|
std::unique_ptr<RuntimeAdapter> adapter,
|
|
const std::string &title,
|
|
bool waitForDebugger)
|
|
: impl_(
|
|
std::make_unique<Impl>(std::move(adapter), title, waitForDebugger)) {}
|
|
|
|
Connection::~Connection() = default;
|
|
|
|
HermesRuntime &Connection::getRuntime() {
|
|
return impl_->getRuntime();
|
|
}
|
|
|
|
std::string Connection::getTitle() const {
|
|
return impl_->getTitle();
|
|
}
|
|
|
|
bool Connection::connect(std::unique_ptr<IRemoteConnection> remoteConn) {
|
|
return impl_->connect(std::move(remoteConn));
|
|
}
|
|
|
|
bool Connection::disconnect() {
|
|
return impl_->disconnect();
|
|
}
|
|
|
|
void Connection::sendMessage(std::string str) {
|
|
impl_->sendMessage(std::move(str));
|
|
}
|
|
|
|
} // namespace chrome
|
|
} // namespace inspector
|
|
} // namespace hermes
|
|
} // namespace facebook
|