mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
RuntimeTarget refactor - Add RuntimeExecutor to RuntimeTarget
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`. ~~
* ~~Make runtime registration explicit (`InstanceTarget::registerRuntime` similar to `PageTarget::registerInstance`). ~~
* ~~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. *← This diff*
* 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: D53266710
fbshipit-source-id: df3a181fcc8e033c37a7f4f430f23a29b326b56a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
496724fbdb
commit
b3a7a13ff8
@@ -67,8 +67,10 @@ void Instance::initializeBridge(
|
||||
|
||||
if (parentInspectorTarget) {
|
||||
inspectorTarget_ = &parentInspectorTarget->registerInstance(*this);
|
||||
RuntimeExecutor runtimeExecutorIfJsi = getRuntimeExecutor();
|
||||
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(
|
||||
nativeToJsBridge_->getInspectorTargetDelegate());
|
||||
nativeToJsBridge_->getInspectorTargetDelegate(),
|
||||
runtimeExecutorIfJsi ? runtimeExecutorIfJsi : [](auto) {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,4 +22,5 @@ target_link_libraries(jsinspector
|
||||
folly_runtime
|
||||
glog
|
||||
react_featureflags
|
||||
runtimeexecutor
|
||||
)
|
||||
|
||||
@@ -46,9 +46,10 @@ InstanceTarget::~InstanceTarget() {
|
||||
}
|
||||
|
||||
RuntimeTarget& InstanceTarget::registerRuntime(
|
||||
RuntimeTargetDelegate& delegate) {
|
||||
RuntimeTargetDelegate& delegate,
|
||||
RuntimeExecutor executor) {
|
||||
assert(!currentRuntime_ && "Only one Runtime allowed");
|
||||
currentRuntime_.emplace(delegate);
|
||||
currentRuntime_.emplace(delegate, executor);
|
||||
forEachAgent([currentRuntime = &*currentRuntime_](InstanceAgent& agent) {
|
||||
agent.setCurrentRuntime(currentRuntime);
|
||||
});
|
||||
|
||||
@@ -59,7 +59,9 @@ class InstanceTarget final {
|
||||
FrontendChannel channel,
|
||||
SessionState& sessionState);
|
||||
|
||||
RuntimeTarget& registerRuntime(RuntimeTargetDelegate& delegate);
|
||||
RuntimeTarget& registerRuntime(
|
||||
RuntimeTargetDelegate& delegate,
|
||||
RuntimeExecutor executor);
|
||||
void unregisterRuntime(RuntimeTarget& runtime);
|
||||
|
||||
private:
|
||||
|
||||
@@ -51,4 +51,5 @@ Pod::Spec.new do |s|
|
||||
s.dependency "RCT-Folly", folly_version
|
||||
s.dependency "React-featureflags"
|
||||
s.dependency "DoubleConversion"
|
||||
s.dependency "React-runtimeexecutor", version
|
||||
end
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
#include <jsinspector-modern/RuntimeTarget.h>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
RuntimeTarget::RuntimeTarget(RuntimeTargetDelegate& delegate)
|
||||
: delegate_(delegate) {}
|
||||
|
||||
RuntimeTarget::RuntimeTarget(
|
||||
RuntimeTargetDelegate& delegate,
|
||||
RuntimeExecutor executor)
|
||||
: delegate_(delegate), executor_(executor) {}
|
||||
|
||||
std::shared_ptr<RuntimeAgent> RuntimeTarget::createAgent(
|
||||
FrontendChannel channel,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ReactCommon/RuntimeExecutor.h>
|
||||
#include "InspectorInterfaces.h"
|
||||
#include "RuntimeAgent.h"
|
||||
#include "SessionState.h"
|
||||
@@ -53,8 +54,12 @@ class JSINSPECTOR_EXPORT RuntimeTarget final {
|
||||
* \param delegate The object that will receive events from this target.
|
||||
* The caller is responsible for ensuring that the delegate outlives this
|
||||
* object.
|
||||
* \param executor A RuntimeExecutor that can be used to schedule work on
|
||||
* the JS runtime's thread. The executor's queue should be empty when
|
||||
* RuntimeTarget is constructed (i.e. anything scheduled during the
|
||||
* constructor should be executed before any user code is run).
|
||||
*/
|
||||
explicit RuntimeTarget(RuntimeTargetDelegate& delegate);
|
||||
RuntimeTarget(RuntimeTargetDelegate& delegate, RuntimeExecutor executor);
|
||||
|
||||
RuntimeTarget(const RuntimeTarget&) = delete;
|
||||
RuntimeTarget(RuntimeTarget&&) = delete;
|
||||
@@ -77,6 +82,7 @@ class JSINSPECTOR_EXPORT RuntimeTarget final {
|
||||
|
||||
private:
|
||||
RuntimeTargetDelegate& delegate_;
|
||||
RuntimeExecutor executor_;
|
||||
std::list<std::weak_ptr<RuntimeAgent>> agents_;
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,9 @@ class PageTargetTest : public Test {
|
||||
|
||||
MockInstanceTargetDelegate instanceTargetDelegate_;
|
||||
MockRuntimeTargetDelegate runtimeTargetDelegate_;
|
||||
// We don't have access to a jsi::Runtime in these tests, so just use an
|
||||
// executor that never runs the scheduled callbacks.
|
||||
RuntimeExecutor runtimeExecutor_ = [](auto) {};
|
||||
|
||||
UniquePtrFactory<StrictMock<MockRuntimeAgentDelegate>> runtimeAgentDelegates_;
|
||||
|
||||
@@ -252,7 +255,8 @@ TEST_F(PageTargetTest, ConnectToAlreadyRegisteredInstanceWithEvents) {
|
||||
|
||||
TEST_F(PageTargetTest, ConnectToAlreadyRegisteredRuntimeWithEvents) {
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
connect();
|
||||
|
||||
@@ -287,8 +291,8 @@ TEST_F(PageTargetTest, ConnectToAlreadyRegisteredRuntimeWithEvents) {
|
||||
TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateLifecycle) {
|
||||
{
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(
|
||||
runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
EXPECT_TRUE(runtimeAgentDelegates_[0]);
|
||||
|
||||
@@ -300,8 +304,8 @@ TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateLifecycle) {
|
||||
|
||||
{
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(
|
||||
runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
EXPECT_TRUE(runtimeAgentDelegates_[1]);
|
||||
|
||||
@@ -316,7 +320,8 @@ TEST_F(PageTargetProtocolTest, MethodNotHandledByRuntimeAgentDelegate) {
|
||||
InSequence s;
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgentDelegates_[0]);
|
||||
EXPECT_CALL(*runtimeAgentDelegates_[0], handleRequest(_))
|
||||
@@ -341,7 +346,8 @@ TEST_F(PageTargetProtocolTest, MethodHandledByRuntimeAgentDelegate) {
|
||||
InSequence s;
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgentDelegates_[0]);
|
||||
EXPECT_CALL(*runtimeAgentDelegates_[0], handleRequest(_))
|
||||
@@ -384,7 +390,8 @@ TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgentDelegate) {
|
||||
})");
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
ASSERT_TRUE(runtimeAgentDelegates_[0]);
|
||||
EXPECT_CALL(*runtimeAgentDelegates_[0], handleRequest(_))
|
||||
@@ -432,7 +439,8 @@ TEST_F(PageTargetProtocolTest, InstanceWithNullRuntimeAgentDelegate) {
|
||||
.WillRepeatedly(ReturnNull());
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
auto& runtimeTarget = instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
auto& runtimeTarget =
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
|
||||
|
||||
EXPECT_FALSE(runtimeAgentDelegates_[0]);
|
||||
|
||||
@@ -466,7 +474,7 @@ TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateHasAccessToSessionState) {
|
||||
})");
|
||||
|
||||
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_);
|
||||
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
|
||||
ASSERT_TRUE(runtimeAgentDelegates_[0]);
|
||||
|
||||
EXPECT_TRUE(runtimeAgentDelegates_[0]->sessionState.isRuntimeDomainEnabled);
|
||||
|
||||
@@ -37,10 +37,6 @@ ReactInstance::ReactInstance(
|
||||
jsErrorHandler_(jsErrorHandlingFunc),
|
||||
hasFatalJsError_(std::make_shared<bool>(false)),
|
||||
parentInspectorTarget_(parentInspectorTarget) {
|
||||
if (parentInspectorTarget_) {
|
||||
inspectorTarget_ = &parentInspectorTarget_->registerInstance(*this);
|
||||
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(*runtime_);
|
||||
}
|
||||
auto runtimeExecutor = [weakRuntime = std::weak_ptr<JSRuntime>(runtime_),
|
||||
weakTimerManager =
|
||||
std::weak_ptr<TimerManager>(timerManager_),
|
||||
@@ -88,6 +84,12 @@ ReactInstance::ReactInstance(
|
||||
}
|
||||
};
|
||||
|
||||
if (parentInspectorTarget_) {
|
||||
inspectorTarget_ = &parentInspectorTarget_->registerInstance(*this);
|
||||
runtimeInspectorTarget_ =
|
||||
&inspectorTarget_->registerRuntime(*runtime_, runtimeExecutor);
|
||||
}
|
||||
|
||||
runtimeScheduler_ =
|
||||
std::make_shared<RuntimeScheduler>(std::move(runtimeExecutor));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user