mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
RuntimeTarget refactor - Create RuntimeTarget, RuntimeTargetDelegate
Summary:
Changelog: [Internal]
I'm refactoring the way the Runtime concept works in the modern CDP backend to bring it in line with the Page/Instance concepts.
Overall, this will let us:
* Integrate with engines that require us to instantiate a shared Target-like object (e.g. Hermes AsyncDebuggingAPI) in addition to an per-session Agent-like object.
* Access JSI in a CDP context (both at target setup/teardown time and during a CDP session) to implement our own engine-agnostic functionality (`console` interception, `Runtime.addBinding`, etc).
* Manage CDP execution contexts natively in RN, and (down the line) enable first-class debugging support for multiple Runtimes in an Instance.
The core diffs in this stack will:
* Introduce a `RuntimeTarget` class similar to `{Page,Instance}Target`. *← This diff*
* Make runtime registration explicit (`InstanceTarget::registerRuntime` similar to `PageTarget::registerInstance`). *← Also in this diff*
* Rename the existing `RuntimeAgent` interface to `RuntimeAgentDelegate`.
* Create a new concrete `RuntimeAgent` class similar to `{Page,Instance}Agent`.
* Provide `RuntimeTarget` and `RuntimeAgent` with primitives for safe JSI access, namely a `RuntimeExecutor` for scheduling work on the JS thread.
* We'll likely develop a similar mechanism for scheduling work on the "main" thread from the JS thread, for when we need to do more than just send a CDP message (which we can already do with the thread-safe `FrontendChannel`) in response to a JS event.
## Architecture diagrams
Before this stack:
https://pxl.cl/4h7m0
After this stack:
https://pxl.cl/4h7m7
Reviewed By: hoxyq
Differential Revision: D53233914
fbshipit-source-id: 166ae3e25059bd9c9c051a0a3312a3ba78a3935a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b366b4b42e
commit
415bb718ff
@@ -40,6 +40,8 @@ Instance::~Instance() {
|
||||
|
||||
void Instance::unregisterFromInspector() {
|
||||
if (inspectorTarget_) {
|
||||
assert(runtimeInspectorTarget_);
|
||||
inspectorTarget_->unregisterRuntime(*runtimeInspectorTarget_);
|
||||
assert(parentInspectorTarget_);
|
||||
parentInspectorTarget_->unregisterInstance(*inspectorTarget_);
|
||||
parentInspectorTarget_ = nullptr;
|
||||
@@ -65,6 +67,8 @@ void Instance::initializeBridge(
|
||||
|
||||
if (parentInspectorTarget) {
|
||||
inspectorTarget_ = &parentInspectorTarget->registerInstance(*this);
|
||||
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(
|
||||
nativeToJsBridge_->getInspectorTargetDelegate());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,10 +320,4 @@ void Instance::JSCallInvoker::scheduleAsync(
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> Instance::createRuntimeAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) {
|
||||
return nativeToJsBridge_->createRuntimeAgent(frontendChannel, sessionState);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -151,11 +151,6 @@ class RN_EXPORT Instance : private jsinspector_modern::InstanceTargetDelegate {
|
||||
std::unique_ptr<const JSBigString> startupScript,
|
||||
std::string startupScriptSourceURL);
|
||||
|
||||
// From InstanceTargetDelegate
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> createRuntimeAgent(
|
||||
jsinspector_modern::FrontendChannel channel,
|
||||
jsinspector_modern::SessionState& sessionState) override;
|
||||
|
||||
std::shared_ptr<InstanceCallback> callback_;
|
||||
std::shared_ptr<NativeToJsBridge> nativeToJsBridge_;
|
||||
std::shared_ptr<ModuleRegistry> moduleRegistry_;
|
||||
@@ -185,6 +180,7 @@ class RN_EXPORT Instance : private jsinspector_modern::InstanceTargetDelegate {
|
||||
|
||||
jsinspector_modern::PageTarget* parentInspectorTarget_{nullptr};
|
||||
jsinspector_modern::InstanceTarget* inspectorTarget_{nullptr};
|
||||
jsinspector_modern::RuntimeTarget* runtimeInspectorTarget_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -35,8 +35,7 @@ double JSExecutor::performanceNow() {
|
||||
return duration / NANOSECONDS_IN_MILLISECOND;
|
||||
}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent>
|
||||
JSExecutor::createRuntimeAgent(
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> JSExecutor::createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) {
|
||||
return std::make_unique<jsinspector_modern::FallbackRuntimeAgent>(
|
||||
|
||||
@@ -55,7 +55,7 @@ class JSExecutorFactory {
|
||||
virtual ~JSExecutorFactory() {}
|
||||
};
|
||||
|
||||
class RN_EXPORT JSExecutor {
|
||||
class RN_EXPORT JSExecutor : public jsinspector_modern::RuntimeTargetDelegate {
|
||||
public:
|
||||
/**
|
||||
* Prepares the JS runtime for React Native by installing global variables.
|
||||
@@ -114,7 +114,7 @@ class RN_EXPORT JSExecutor {
|
||||
/**
|
||||
* Returns whether or not the underlying executor supports debugging via the
|
||||
* Chrome remote debugging protocol. If true, the executor should also
|
||||
* override the \c createRuntimeAgent method.
|
||||
* override the \c createAgent method.
|
||||
*/
|
||||
virtual bool isInspectable() {
|
||||
return false;
|
||||
@@ -143,9 +143,9 @@ class RN_EXPORT JSExecutor {
|
||||
/**
|
||||
* Create a RuntimeAgent that can be used to debug the JS VM instance.
|
||||
*/
|
||||
virtual std::unique_ptr<jsinspector_modern::RuntimeAgent> createRuntimeAgent(
|
||||
virtual std::unique_ptr<jsinspector_modern::RuntimeAgent> createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState);
|
||||
jsinspector_modern::SessionState& sessionState) override;
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -343,13 +343,9 @@ NativeToJsBridge::getDecoratedNativeMethodCallInvoker(
|
||||
m_delegate, std::move(nativeMethodCallInvoker));
|
||||
}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent>
|
||||
NativeToJsBridge::createRuntimeAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) {
|
||||
auto agent =
|
||||
m_executor->createRuntimeAgent(std::move(frontendChannel), sessionState);
|
||||
return agent;
|
||||
jsinspector_modern::RuntimeTargetDelegate&
|
||||
NativeToJsBridge::getInspectorTargetDelegate() {
|
||||
return *m_executor;
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -107,13 +107,7 @@ class NativeToJsBridge {
|
||||
std::shared_ptr<NativeMethodCallInvoker> getDecoratedNativeMethodCallInvoker(
|
||||
std::shared_ptr<NativeMethodCallInvoker> nativeInvoker) const;
|
||||
|
||||
/**
|
||||
* Create a RuntimeAgent that can be used to debug the underlying JS VM
|
||||
* instance.
|
||||
*/
|
||||
virtual std::unique_ptr<jsinspector_modern::RuntimeAgent> createRuntimeAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState);
|
||||
jsinspector_modern::RuntimeTargetDelegate& getInspectorTargetDelegate();
|
||||
|
||||
private:
|
||||
// This is used to avoid a race condition where a proxyCallback gets queued
|
||||
|
||||
@@ -257,8 +257,7 @@ HermesExecutor::HermesExecutor(
|
||||
runtime_(runtime),
|
||||
hermesRuntime_(hermesRuntime) {}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent>
|
||||
HermesExecutor::createRuntimeAgent(
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> HermesExecutor::createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) {
|
||||
std::shared_ptr<HermesRuntime> hermesRuntimeShared(runtime_, &hermesRuntime_);
|
||||
|
||||
@@ -54,7 +54,7 @@ class HermesExecutor : public JSIExecutor {
|
||||
RuntimeInstaller runtimeInstaller,
|
||||
hermes::HermesRuntime& hermesRuntime);
|
||||
|
||||
virtual std::unique_ptr<jsinspector_modern::RuntimeAgent> createRuntimeAgent(
|
||||
virtual std::unique_ptr<jsinspector_modern::RuntimeAgent> createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) override;
|
||||
|
||||
|
||||
@@ -6,16 +6,17 @@
|
||||
*/
|
||||
|
||||
#include <jsinspector-modern/InstanceAgent.h>
|
||||
#include "RuntimeTarget.h"
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
InstanceAgent::InstanceAgent(
|
||||
FrontendChannel frontendChannel,
|
||||
InstanceTarget& target,
|
||||
std::unique_ptr<RuntimeAgent> runtimeAgent)
|
||||
SessionState& sessionState)
|
||||
: frontendChannel_(frontendChannel),
|
||||
target_(target),
|
||||
runtimeAgent_(std::move(runtimeAgent)) {
|
||||
sessionState_(sessionState) {
|
||||
(void)target_;
|
||||
}
|
||||
|
||||
@@ -26,4 +27,12 @@ bool InstanceAgent::handleRequest(const cdp::PreparsedRequest& req) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void InstanceAgent::setCurrentRuntime(RuntimeTarget* runtimeTarget) {
|
||||
if (runtimeTarget) {
|
||||
runtimeAgent_ = runtimeTarget->createAgent(frontendChannel_, sessionState_);
|
||||
} else {
|
||||
runtimeAgent_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RuntimeTarget.h"
|
||||
#include "SessionState.h"
|
||||
|
||||
#include <jsinspector-modern/InspectorInterfaces.h>
|
||||
#include <jsinspector-modern/Parsing.h>
|
||||
#include <jsinspector-modern/RuntimeAgent.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
@@ -28,13 +32,12 @@ class InstanceAgent {
|
||||
* \param target The InstanceTarget that this agent is attached to. The
|
||||
* caller is responsible for ensuring that the InstanceTarget outlives this
|
||||
* object.
|
||||
* \param runtimeAgent The RuntimeAgent that this agent will use to
|
||||
* communicate with the JS runtime.
|
||||
* \param sessionState The state of the session that created this agent.
|
||||
*/
|
||||
explicit InstanceAgent(
|
||||
FrontendChannel frontendChannel,
|
||||
InstanceTarget& target,
|
||||
std::unique_ptr<RuntimeAgent> runtimeAgent);
|
||||
SessionState& sessionState);
|
||||
|
||||
/**
|
||||
* Handle a CDP request. The response will be sent over the provided
|
||||
@@ -43,10 +46,19 @@ class InstanceAgent {
|
||||
*/
|
||||
bool handleRequest(const cdp::PreparsedRequest& req);
|
||||
|
||||
/**
|
||||
* Replace the current RuntimeAgent pageAgent_ with a new one
|
||||
* connected to the new RuntimeTarget.
|
||||
* \param runtime The new runtime target. May be nullptr to indicate
|
||||
* there's no current debuggable runtime.
|
||||
*/
|
||||
void setCurrentRuntime(RuntimeTarget* runtime);
|
||||
|
||||
private:
|
||||
FrontendChannel frontendChannel_;
|
||||
InstanceTarget& target_;
|
||||
std::unique_ptr<RuntimeAgent> runtimeAgent_;
|
||||
SessionState& sessionState_;
|
||||
};
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -19,12 +19,33 @@ InstanceTarget::InstanceTarget(InstanceTargetDelegate& delegate)
|
||||
|
||||
InstanceTargetDelegate::~InstanceTargetDelegate() {}
|
||||
|
||||
std::unique_ptr<InstanceAgent> InstanceTarget::createAgent(
|
||||
std::shared_ptr<InstanceAgent> InstanceTarget::createAgent(
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState) {
|
||||
auto runtimeAgent = delegate_.createRuntimeAgent(channel, sessionState);
|
||||
return std::make_unique<InstanceAgent>(
|
||||
channel, *this, std::move(runtimeAgent));
|
||||
auto instanceAgent =
|
||||
std::make_shared<InstanceAgent>(channel, *this, sessionState);
|
||||
instanceAgent->setCurrentRuntime(
|
||||
currentRuntime_.has_value() ? &*currentRuntime_ : nullptr);
|
||||
agents_.push_back(instanceAgent);
|
||||
return instanceAgent;
|
||||
}
|
||||
|
||||
RuntimeTarget& InstanceTarget::registerRuntime(
|
||||
RuntimeTargetDelegate& delegate) {
|
||||
assert(!currentRuntime_ && "Only one Runtime allowed");
|
||||
currentRuntime_.emplace(delegate);
|
||||
forEachAgent([currentRuntime = &*currentRuntime_](InstanceAgent& agent) {
|
||||
agent.setCurrentRuntime(currentRuntime);
|
||||
});
|
||||
return *currentRuntime_;
|
||||
}
|
||||
|
||||
void InstanceTarget::unregisterRuntime(RuntimeTarget& Runtime) {
|
||||
assert(
|
||||
currentRuntime_.has_value() && ¤tRuntime_.value() == &Runtime &&
|
||||
"Invalid unregistration");
|
||||
forEachAgent([](InstanceAgent& agent) { agent.setCurrentRuntime(nullptr); });
|
||||
currentRuntime_.reset();
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RuntimeTarget.h"
|
||||
#include "SessionState.h"
|
||||
|
||||
#include <jsinspector-modern/InspectorInterfaces.h>
|
||||
@@ -33,18 +34,6 @@ class InstanceTargetDelegate {
|
||||
InstanceTargetDelegate& operator=(const InstanceTargetDelegate&) = delete;
|
||||
InstanceTargetDelegate& operator=(InstanceTargetDelegate&&) = default;
|
||||
|
||||
/**
|
||||
* Create a new RuntimeAgent that can be used to debug the underlying JS VM.
|
||||
* The agent will be destroyed when the session ends or the InstanceTarget is
|
||||
* unregistered from its PageTarget (whichever happens first).
|
||||
* \param channel A thread-safe channel for sending CDP messages to the
|
||||
* frontend.
|
||||
* \returns The new agent, or nullptr if the target does not support JS
|
||||
* debugging.
|
||||
*/
|
||||
virtual std::unique_ptr<RuntimeAgent> createRuntimeAgent(
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState) = 0;
|
||||
virtual ~InstanceTargetDelegate();
|
||||
};
|
||||
|
||||
@@ -65,12 +54,35 @@ class InstanceTarget {
|
||||
InstanceTarget& operator=(const InstanceTarget&) = delete;
|
||||
InstanceTarget& operator=(InstanceTarget&&) = delete;
|
||||
|
||||
std::unique_ptr<InstanceAgent> createAgent(
|
||||
std::shared_ptr<InstanceAgent> createAgent(
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState);
|
||||
|
||||
RuntimeTarget& registerRuntime(RuntimeTargetDelegate& delegate);
|
||||
void unregisterRuntime(RuntimeTarget& runtime);
|
||||
|
||||
private:
|
||||
InstanceTargetDelegate& delegate_;
|
||||
std::optional<RuntimeTarget> currentRuntime_{std::nullopt};
|
||||
std::list<std::weak_ptr<InstanceAgent>> agents_;
|
||||
|
||||
/**
|
||||
* Call the given function for every active agent, and clean up any
|
||||
* references to inactive agents.
|
||||
*/
|
||||
template <typename Fn>
|
||||
void forEachAgent(Fn&& fn) {
|
||||
for (auto it = agents_.begin(); it != agents_.end();) {
|
||||
if (auto agent = it->lock()) {
|
||||
fn(*agent);
|
||||
++it;
|
||||
} else {
|
||||
it = agents_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeExpiredAgents();
|
||||
};
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -128,7 +128,7 @@ void PageAgent::sendInfoLogEntry(std::string_view text) {
|
||||
}
|
||||
|
||||
void PageAgent::setCurrentInstanceAgent(
|
||||
std::unique_ptr<InstanceAgent> instanceAgent) {
|
||||
std::shared_ptr<InstanceAgent> instanceAgent) {
|
||||
auto previousInstanceAgent = std::move(instanceAgent_);
|
||||
instanceAgent_ = std::move(instanceAgent);
|
||||
if (!sessionState_.isRuntimeDomainEnabled) {
|
||||
|
||||
@@ -60,7 +60,7 @@ class PageAgent {
|
||||
* \param agent The new InstanceAgent. May be null to signify that there is
|
||||
* currently no active instance.
|
||||
*/
|
||||
void setCurrentInstanceAgent(std::unique_ptr<InstanceAgent> agent);
|
||||
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> agent);
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -77,7 +77,7 @@ class PageAgent {
|
||||
FrontendChannel frontendChannel_;
|
||||
PageTargetController& targetController_;
|
||||
const PageTarget::SessionMetadata sessionMetadata_;
|
||||
std::unique_ptr<InstanceAgent> instanceAgent_;
|
||||
std::shared_ptr<InstanceAgent> instanceAgent_;
|
||||
|
||||
/**
|
||||
* A shared reference to the session's state. This is only safe to access
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
#include <jsinspector-modern/FallbackRuntimeAgent.h>
|
||||
#include <jsinspector-modern/InstanceTarget.h>
|
||||
#include <jsinspector-modern/PageTarget.h>
|
||||
#include <jsinspector-modern/RuntimeAgent.h>
|
||||
#include <jsinspector-modern/RuntimeTarget.h>
|
||||
#include <jsinspector-modern/SessionState.h>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <jsinspector-modern/RuntimeTarget.h>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
RuntimeTarget::RuntimeTarget(RuntimeTargetDelegate& delegate)
|
||||
: delegate_(delegate) {}
|
||||
|
||||
std::unique_ptr<RuntimeAgent> RuntimeTarget::createAgent(
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState) {
|
||||
return delegate_.createAgent(channel, sessionState);
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InspectorInterfaces.h"
|
||||
#include "RuntimeAgent.h"
|
||||
#include "SessionState.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifndef JSINSPECTOR_EXPORT
|
||||
#ifdef _MSC_VER
|
||||
#ifdef CREATE_SHARED_LIBRARY
|
||||
#define JSINSPECTOR_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define JSINSPECTOR_EXPORT
|
||||
#endif // CREATE_SHARED_LIBRARY
|
||||
#else // _MSC_VER
|
||||
#define JSINSPECTOR_EXPORT __attribute__((visibility("default")))
|
||||
#endif // _MSC_VER
|
||||
#endif // !defined(JSINSPECTOR_EXPORT)
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
/**
|
||||
* Receives events from a RuntimeTarget. This is a shared interface that
|
||||
* each React Native platform needs to implement in order to integrate with
|
||||
* the debugging stack.
|
||||
*/
|
||||
class RuntimeTargetDelegate {
|
||||
public:
|
||||
virtual ~RuntimeTargetDelegate() = default;
|
||||
virtual std::unique_ptr<RuntimeAgent> createAgent(
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Target corresponding to a JavaScript runtime.
|
||||
*/
|
||||
class JSINSPECTOR_EXPORT RuntimeTarget {
|
||||
public:
|
||||
/**
|
||||
* \param delegate The object that will receive events from this target.
|
||||
* The caller is responsible for ensuring that the delegate outlives this
|
||||
* object.
|
||||
*/
|
||||
explicit RuntimeTarget(RuntimeTargetDelegate& delegate);
|
||||
|
||||
RuntimeTarget(const RuntimeTarget&) = delete;
|
||||
RuntimeTarget(RuntimeTarget&&) = delete;
|
||||
RuntimeTarget& operator=(const RuntimeTarget&) = delete;
|
||||
RuntimeTarget& operator=(RuntimeTarget&&) = delete;
|
||||
|
||||
/**
|
||||
* Create a new RuntimeAgent that can be used to debug the underlying JS VM.
|
||||
* The agent will be destroyed when the session ends, the containing
|
||||
* InstanceTarget is unregistered from its PageTarget, or the RuntimeAgent is
|
||||
* unregistered from its InstanceTarget (whichever happens first).
|
||||
* \param channel A thread-safe channel for sending CDP messages to the
|
||||
* frontend.
|
||||
* \returns The new agent, or nullptr if the runtime is not debuggable.
|
||||
*/
|
||||
std::unique_ptr<RuntimeAgent> createAgent(
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState);
|
||||
|
||||
private:
|
||||
RuntimeTargetDelegate& delegate_;
|
||||
};
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
@@ -121,12 +121,14 @@ class MockPageTargetDelegate : public PageTargetDelegate {
|
||||
MOCK_METHOD(void, onReload, (const PageReloadRequest& request), (override));
|
||||
};
|
||||
|
||||
class MockInstanceTargetDelegate : public InstanceTargetDelegate {
|
||||
class MockInstanceTargetDelegate : public InstanceTargetDelegate {};
|
||||
|
||||
class MockRuntimeTargetDelegate : public RuntimeTargetDelegate {
|
||||
public:
|
||||
// InstanceTargetDelegate methods
|
||||
// RuntimeTargetDelegate methods
|
||||
MOCK_METHOD(
|
||||
std::unique_ptr<RuntimeAgent>,
|
||||
createRuntimeAgent,
|
||||
createAgent,
|
||||
(FrontendChannel channel, SessionState& sessionState),
|
||||
(override));
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace {
|
||||
class PageTargetTest : public Test {
|
||||
protected:
|
||||
PageTargetTest() {
|
||||
EXPECT_CALL(instanceTargetDelegate_, createRuntimeAgent(_, _))
|
||||
EXPECT_CALL(runtimeTargetDelegate_, createAgent(_, _))
|
||||
.WillRepeatedly(
|
||||
runtimeAgents_
|
||||
.lazily_make_unique<FrontendChannel, SessionState&>());
|
||||
@@ -55,6 +55,7 @@ class PageTargetTest : public Test {
|
||||
PageTarget page_{pageTargetDelegate_};
|
||||
|
||||
MockInstanceTargetDelegate instanceTargetDelegate_;
|
||||
MockRuntimeTargetDelegate runtimeTargetDelegate_;
|
||||
|
||||
UniquePtrFactory<StrictMock<MockRuntimeAgent>> runtimeAgents_;
|
||||
|
||||
@@ -233,11 +234,6 @@ TEST_F(PageTargetTest, ConnectToAlreadyRegisteredInstanceWithEvents) {
|
||||
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(*runtimeAgents_[0], handleRequest(Eq(cdp::preparse(R"({
|
||||
"id": 1,
|
||||
"method": "Runtime.enable"
|
||||
})"))))
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(fromPage(), onMessage(JsonEq(R"({
|
||||
"id": 1,
|
||||
"result": {}
|
||||
@@ -254,56 +250,14 @@ TEST_F(PageTargetTest, ConnectToAlreadyRegisteredInstanceWithEvents) {
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
TEST_F(PageTargetProtocolTest, RuntimeAgentLifecycle) {
|
||||
{
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
|
||||
EXPECT_TRUE(runtimeAgents_[0]);
|
||||
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(runtimeAgents_[0]);
|
||||
|
||||
{
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
|
||||
EXPECT_TRUE(runtimeAgents_[1]);
|
||||
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(runtimeAgents_[1]);
|
||||
}
|
||||
|
||||
TEST_F(PageTargetProtocolTest, MethodNotHandledByRuntimeAgent) {
|
||||
InSequence s;
|
||||
|
||||
TEST_F(PageTargetTest, ConnectToAlreadyRegisteredRuntimeWithEvents) {
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgents_[0]);
|
||||
EXPECT_CALL(*runtimeAgents_[0], handleRequest(_))
|
||||
.WillOnce(Return(false))
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(
|
||||
fromPage(), onMessage(JsonParsed(AtJsonPtr("/error/code", Eq(-32601)))))
|
||||
.RetiresOnSaturation();
|
||||
toPage_->sendMessage(R"({
|
||||
"id": 1,
|
||||
"method": "CustomRuntimeDomain.Foo",
|
||||
"params": {
|
||||
"expression": "42"
|
||||
}
|
||||
})");
|
||||
connect();
|
||||
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
TEST_F(PageTargetProtocolTest, MethodHandledByRuntimeAgent) {
|
||||
InSequence s;
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgents_[0]);
|
||||
EXPECT_CALL(*runtimeAgents_[0], handleRequest(_))
|
||||
.WillOnce(Return(true))
|
||||
@@ -326,6 +280,92 @@ TEST_F(PageTargetProtocolTest, MethodHandledByRuntimeAgent) {
|
||||
.RetiresOnSaturation();
|
||||
runtimeAgents_[0]->frontendChannel(kFooResponse);
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
TEST_F(PageTargetProtocolTest, RuntimeAgentLifecycle) {
|
||||
{
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
EXPECT_TRUE(runtimeAgents_[0]);
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(runtimeAgents_[0]);
|
||||
|
||||
{
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
EXPECT_TRUE(runtimeAgents_[1]);
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(runtimeAgents_[1]);
|
||||
}
|
||||
|
||||
TEST_F(PageTargetProtocolTest, MethodNotHandledByRuntimeAgent) {
|
||||
InSequence s;
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgents_[0]);
|
||||
EXPECT_CALL(*runtimeAgents_[0], handleRequest(_))
|
||||
.WillOnce(Return(false))
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(
|
||||
fromPage(), onMessage(JsonParsed(AtJsonPtr("/error/code", Eq(-32601)))))
|
||||
.RetiresOnSaturation();
|
||||
toPage_->sendMessage(R"({
|
||||
"id": 1,
|
||||
"method": "CustomRuntimeDomain.Foo",
|
||||
"params": {
|
||||
"expression": "42"
|
||||
}
|
||||
})");
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
TEST_F(PageTargetProtocolTest, MethodHandledByRuntimeAgent) {
|
||||
InSequence s;
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgents_[0]);
|
||||
EXPECT_CALL(*runtimeAgents_[0], handleRequest(_))
|
||||
.WillOnce(Return(true))
|
||||
.RetiresOnSaturation();
|
||||
toPage_->sendMessage(R"({
|
||||
"id": 1,
|
||||
"method": "CustomRuntimeDomain.Foo",
|
||||
"params": {
|
||||
"expression": "42"
|
||||
}
|
||||
})");
|
||||
|
||||
static constexpr auto kFooResponse = R"({
|
||||
"id": 1,
|
||||
"result": {
|
||||
"fooValue": 42
|
||||
}
|
||||
})";
|
||||
EXPECT_CALL(fromPage(), onMessage(JsonEq(kFooResponse)))
|
||||
.RetiresOnSaturation();
|
||||
runtimeAgents_[0]->frontendChannel(kFooResponse);
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
@@ -344,6 +384,7 @@ TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgent) {
|
||||
})");
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgents_[0]);
|
||||
EXPECT_CALL(*runtimeAgents_[0], handleRequest(_))
|
||||
@@ -367,6 +408,7 @@ TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgent) {
|
||||
.RetiresOnSaturation();
|
||||
runtimeAgents_[0]->frontendChannel(kFooResponse);
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
|
||||
EXPECT_FALSE(runtimeAgents_[0]);
|
||||
@@ -386,10 +428,11 @@ TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgent) {
|
||||
TEST_F(PageTargetProtocolTest, InstanceWithNullRuntimeAgent) {
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(instanceTargetDelegate_, createRuntimeAgent(_, _))
|
||||
EXPECT_CALL(runtimeTargetDelegate_, createAgent(_, _))
|
||||
.WillRepeatedly(ReturnNull());
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
|
||||
EXPECT_FALSE(runtimeAgents_[0]);
|
||||
|
||||
@@ -404,6 +447,7 @@ TEST_F(PageTargetProtocolTest, InstanceWithNullRuntimeAgent) {
|
||||
}
|
||||
})");
|
||||
|
||||
instanceTarget.unregisterRuntime(runtimeTarget);
|
||||
page_.unregisterInstance(instanceTarget);
|
||||
}
|
||||
|
||||
@@ -421,7 +465,8 @@ TEST_F(PageTargetProtocolTest, RuntimeAgentHasAccessToSessionState) {
|
||||
"method": "Runtime.enable"
|
||||
})");
|
||||
|
||||
page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
ASSERT_TRUE(runtimeAgents_[0]);
|
||||
|
||||
EXPECT_TRUE(runtimeAgents_[0]->sessionState.isRuntimeDomainEnabled);
|
||||
|
||||
@@ -18,8 +18,7 @@ JSIRuntimeHolder::JSIRuntimeHolder(std::unique_ptr<jsi::Runtime> runtime)
|
||||
assert(runtime_ != nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent>
|
||||
JSIRuntimeHolder::createInspectorAgent(
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> JSIRuntimeHolder::createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) {
|
||||
return std::make_unique<jsinspector_modern::FallbackRuntimeAgent>(
|
||||
|
||||
@@ -17,20 +17,10 @@ namespace facebook::react {
|
||||
/**
|
||||
* An interface that represents an instance of a JS VM
|
||||
*/
|
||||
class JSRuntime {
|
||||
class JSRuntime : public jsinspector_modern::RuntimeTargetDelegate {
|
||||
public:
|
||||
virtual jsi::Runtime& getRuntime() noexcept = 0;
|
||||
|
||||
/**
|
||||
* Creates a new inspector agent for this runtime, if the runtime is
|
||||
* inspectable. Returns nullptr otherwise.
|
||||
* \see InspectorTargetDelegate::createRuntimeAgent
|
||||
*/
|
||||
virtual std::unique_ptr<jsinspector_modern::RuntimeAgent>
|
||||
createInspectorAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) = 0;
|
||||
|
||||
virtual ~JSRuntime() = default;
|
||||
};
|
||||
|
||||
@@ -51,7 +41,7 @@ class JSRuntimeFactory {
|
||||
class JSIRuntimeHolder : public JSRuntime {
|
||||
public:
|
||||
jsi::Runtime& getRuntime() noexcept override;
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> createInspectorAgent(
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) override;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ ReactInstance::ReactInstance(
|
||||
parentInspectorTarget_(parentInspectorTarget) {
|
||||
if (parentInspectorTarget_) {
|
||||
inspectorTarget_ = &parentInspectorTarget_->registerInstance(*this);
|
||||
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(*runtime_);
|
||||
}
|
||||
auto runtimeExecutor = [weakRuntime = std::weak_ptr<JSRuntime>(runtime_),
|
||||
weakTimerManager =
|
||||
@@ -102,6 +103,8 @@ ReactInstance::ReactInstance(
|
||||
|
||||
void ReactInstance::unregisterFromInspector() {
|
||||
if (inspectorTarget_) {
|
||||
assert(runtimeInspectorTarget_);
|
||||
inspectorTarget_->unregisterRuntime(*runtimeInspectorTarget_);
|
||||
assert(parentInspectorTarget_);
|
||||
parentInspectorTarget_->unregisterInstance(*inspectorTarget_);
|
||||
inspectorTarget_ = nullptr;
|
||||
@@ -460,12 +463,4 @@ void ReactInstance::handleMemoryPressureJs(int pressureLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent>
|
||||
ReactInstance::createRuntimeAgent(
|
||||
jsinspector_modern::FrontendChannel channel,
|
||||
jsinspector_modern::SessionState& sessionState) {
|
||||
auto agent = runtime_->createInspectorAgent(std::move(channel), sessionState);
|
||||
return agent;
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -72,10 +72,6 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
|
||||
void unregisterFromInspector();
|
||||
|
||||
private:
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> createRuntimeAgent(
|
||||
jsinspector_modern::FrontendChannel channel,
|
||||
jsinspector_modern::SessionState& sessionState) override;
|
||||
|
||||
std::shared_ptr<JSRuntime> runtime_;
|
||||
std::shared_ptr<MessageQueueThread> jsMessageQueueThread_;
|
||||
std::shared_ptr<BufferedRuntimeExecutor> bufferedRuntimeExecutor_;
|
||||
@@ -88,6 +84,7 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
|
||||
std::shared_ptr<bool> hasFatalJsError_;
|
||||
|
||||
jsinspector_modern::InstanceTarget* inspectorTarget_{nullptr};
|
||||
jsinspector_modern::RuntimeTarget* runtimeInspectorTarget_{nullptr};
|
||||
jsinspector_modern::PageTarget* parentInspectorTarget_{nullptr};
|
||||
};
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class HermesJSRuntime : public JSRuntime {
|
||||
return *runtime_;
|
||||
}
|
||||
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> createInspectorAgent(
|
||||
std::unique_ptr<jsinspector_modern::RuntimeAgent> createAgent(
|
||||
jsinspector_modern::FrontendChannel frontendChannel,
|
||||
jsinspector_modern::SessionState& sessionState) override {
|
||||
return std::unique_ptr<jsinspector_modern::RuntimeAgent>(
|
||||
|
||||
Reference in New Issue
Block a user