Skip to content

Commit b0013ea

Browse files
emily8rownmeta-codesync[bot]
authored andcommitted
Extract shared GlobalStateObserver from FuseboxSessionObserver (#55485)
Summary: Pull Request resolved: #55485 The FuseboxSessionObserver pattern (a native global object with boolean state, subscribers Set, and state change callback) is going to be reused for PerformanceTracerObserver in D92527815. This diff extracts the shared logic into reusable components: JS: GlobalStateObserver class parameterized by global name and status property. C++: installGlobalStateObserver() and emitGlobalStateObserverChange() functions parameterized by global name, status property, and callback name. FuseboxSessionObserver and RuntimeTargetDebuggerSessionObserver now delegate to the shared implementations. No behavior change. Changelog: [GENERAL] [CHANGED] - extracting shared logic for fuseboxSessionObserver into reusable components Reviewed By: hoxyq Differential Revision: D92720212 fbshipit-source-id: 55636943e6870406e8da37789bc508dcc7b40d4b
1 parent 233ee9d commit b0013ea

6 files changed

Lines changed: 247 additions & 137 deletions

File tree

packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "SessionState.h"
99

1010
#include <jsinspector-modern/RuntimeTarget.h>
11+
#include <jsinspector-modern/RuntimeTargetGlobalStateObserver.h>
1112
#include <jsinspector-modern/tracing/PerformanceTracer.h>
1213

1314
#include <utility>
@@ -16,21 +17,6 @@ using namespace facebook::jsi;
1617

1718
namespace facebook::react::jsinspector_modern {
1819

19-
namespace {
20-
21-
void emitSessionStatusChangeForObserverWithValue(
22-
jsi::Runtime& runtime,
23-
const jsi::Value& value) {
24-
auto globalObj = runtime.global();
25-
auto observer =
26-
globalObj.getPropertyAsObject(runtime, "__DEBUGGER_SESSION_OBSERVER__");
27-
auto onSessionStatusChange =
28-
observer.getPropertyAsFunction(runtime, "onSessionStatusChange");
29-
onSessionStatusChange.call(runtime, value);
30-
}
31-
32-
} // namespace
33-
3420
std::shared_ptr<RuntimeTarget> RuntimeTarget::create(
3521
const ExecutionContextDescription& executionContextDescription,
3622
RuntimeTargetDelegate& delegate,
@@ -144,7 +130,11 @@ void RuntimeTarget::installBindingHandler(const std::string& bindingName) {
144130
void RuntimeTarget::emitDebuggerSessionCreated() {
145131
jsExecutor_([selfExecutor = executorFromThis()](jsi::Runtime& runtime) {
146132
try {
147-
emitSessionStatusChangeForObserverWithValue(runtime, jsi::Value(true));
133+
emitGlobalStateObserverChange(
134+
runtime,
135+
"__DEBUGGER_SESSION_OBSERVER__",
136+
"onSessionStatusChange",
137+
true);
148138
} catch (jsi::JSError&) {
149139
// Suppress any errors, they should not be visible to the user
150140
// and should not affect runtime.
@@ -155,7 +145,11 @@ void RuntimeTarget::emitDebuggerSessionCreated() {
155145
void RuntimeTarget::emitDebuggerSessionDestroyed() {
156146
jsExecutor_([selfExecutor = executorFromThis()](jsi::Runtime& runtime) {
157147
try {
158-
emitSessionStatusChangeForObserverWithValue(runtime, jsi::Value(false));
148+
emitGlobalStateObserverChange(
149+
runtime,
150+
"__DEBUGGER_SESSION_OBSERVER__",
151+
"onSessionStatusChange",
152+
false);
159153
} catch (jsi::JSError&) {
160154
// Suppress any errors, they should not be visible to the user
161155
// and should not affect runtime.

packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetDebuggerSessionObserver.cpp

Lines changed: 6 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -6,106 +6,17 @@
66
*/
77

88
#include <jsinspector-modern/RuntimeTarget.h>
9+
#include <jsinspector-modern/RuntimeTargetGlobalStateObserver.h>
910

1011
namespace facebook::react::jsinspector_modern {
1112

1213
void RuntimeTarget::installDebuggerSessionObserver() {
1314
jsExecutor_([](jsi::Runtime& runtime) {
14-
auto globalObj = runtime.global();
15-
try {
16-
auto observer = jsi::Object(runtime);
17-
18-
observer.setProperty(runtime, "hasActiveSession", jsi::Value(false));
19-
20-
auto setFunction = globalObj.getPropertyAsFunction(runtime, "Set");
21-
auto set = setFunction.callAsConstructor(runtime);
22-
observer.setProperty(runtime, "subscribers", set);
23-
24-
observer.setProperty(
25-
runtime,
26-
"onSessionStatusChange",
27-
jsi::Function::createFromHostFunction(
28-
runtime,
29-
jsi::PropNameID::forAscii(runtime, "onSessionStatusChange"),
30-
1,
31-
[](jsi::Runtime& onSessionStatusChangeRuntime,
32-
const jsi::Value& /* onSessionStatusChangeThisVal */,
33-
const jsi::Value* onSessionStatusChangeArgs,
34-
size_t onSessionStatusChangeArgsCount) {
35-
if (onSessionStatusChangeArgsCount != 1 ||
36-
!onSessionStatusChangeArgs[0].isBool()) {
37-
throw jsi::JSError(
38-
onSessionStatusChangeRuntime,
39-
"Invalid arguments: onSessionStatusChange expects 1 boolean argument");
40-
}
41-
42-
bool updatedStatus = onSessionStatusChangeArgs[0].getBool();
43-
44-
auto observerInstanceFromOnSessionStatusChange =
45-
onSessionStatusChangeRuntime.global().getPropertyAsObject(
46-
onSessionStatusChangeRuntime,
47-
"__DEBUGGER_SESSION_OBSERVER__");
48-
auto subscribersToNotify =
49-
observerInstanceFromOnSessionStatusChange
50-
.getPropertyAsObject(
51-
onSessionStatusChangeRuntime, "subscribers");
52-
53-
observerInstanceFromOnSessionStatusChange.setProperty(
54-
onSessionStatusChangeRuntime,
55-
"hasActiveSession",
56-
updatedStatus);
57-
58-
if (subscribersToNotify
59-
.getProperty(onSessionStatusChangeRuntime, "size")
60-
.asNumber() == 0) {
61-
return jsi::Value::undefined();
62-
}
63-
64-
auto forEachSubscriber =
65-
subscribersToNotify.getPropertyAsFunction(
66-
onSessionStatusChangeRuntime, "forEach");
67-
auto forEachSubscriberCallback =
68-
jsi::Function::createFromHostFunction(
69-
onSessionStatusChangeRuntime,
70-
jsi::PropNameID::forAscii(
71-
onSessionStatusChangeRuntime, "forEachCallback"),
72-
1,
73-
[updatedStatus](
74-
jsi::Runtime& forEachCallbackRuntime,
75-
const jsi::Value& /* forEachCallbackThisVal */,
76-
const jsi::Value* forEachCallbackArgs,
77-
size_t forEachCallbackArgsCount) {
78-
if (forEachCallbackArgsCount < 1 ||
79-
!forEachCallbackArgs[0].isObject() ||
80-
!forEachCallbackArgs[0]
81-
.getObject(forEachCallbackRuntime)
82-
.isFunction(forEachCallbackRuntime)) {
83-
throw jsi::JSError(
84-
forEachCallbackRuntime,
85-
"Invalid arguments: forEachSubscriberCallback expects function as a first argument");
86-
}
87-
88-
forEachCallbackArgs[0]
89-
.getObject(forEachCallbackRuntime)
90-
.asFunction(forEachCallbackRuntime)
91-
.call(forEachCallbackRuntime, updatedStatus);
92-
93-
return jsi::Value::undefined();
94-
});
95-
96-
forEachSubscriber.callWithThis(
97-
onSessionStatusChangeRuntime,
98-
subscribersToNotify,
99-
forEachSubscriberCallback);
100-
101-
return jsi::Value::undefined();
102-
}));
103-
104-
globalObj.setProperty(runtime, "__DEBUGGER_SESSION_OBSERVER__", observer);
105-
} catch (jsi::JSError&) {
106-
// Suppress any errors, they should not be visible to the user
107-
// and should not affect runtime.
108-
}
15+
installGlobalStateObserver(
16+
runtime,
17+
"__DEBUGGER_SESSION_OBSERVER__",
18+
"hasActiveSession",
19+
"onSessionStatusChange");
10920
});
11021
}
11122

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "RuntimeTargetGlobalStateObserver.h"
9+
10+
#include <string>
11+
12+
namespace facebook::react::jsinspector_modern {
13+
14+
void installGlobalStateObserver(
15+
jsi::Runtime& runtime,
16+
const char* globalName,
17+
const char* statusProperty,
18+
const char* callbackName) {
19+
auto globalObj = runtime.global();
20+
try {
21+
auto observer = jsi::Object(runtime);
22+
23+
observer.setProperty(runtime, statusProperty, jsi::Value(false));
24+
25+
auto setFunction = globalObj.getPropertyAsFunction(runtime, "Set");
26+
auto set = setFunction.callAsConstructor(runtime);
27+
observer.setProperty(runtime, "subscribers", set);
28+
29+
std::string globalNameStr(globalName);
30+
std::string statusPropertyStr(statusProperty);
31+
32+
observer.setProperty(
33+
runtime,
34+
callbackName,
35+
jsi::Function::createFromHostFunction(
36+
runtime,
37+
jsi::PropNameID::forAscii(runtime, callbackName),
38+
1,
39+
[globalNameStr, statusPropertyStr](
40+
jsi::Runtime& callbackRuntime,
41+
const jsi::Value& /* thisVal */,
42+
const jsi::Value* args,
43+
size_t argsCount) {
44+
if (argsCount != 1 || !args[0].isBool()) {
45+
throw jsi::JSError(
46+
callbackRuntime,
47+
"Invalid arguments: state change callback expects 1 boolean argument");
48+
}
49+
50+
bool updatedStatus = args[0].getBool();
51+
52+
auto observerInstance =
53+
callbackRuntime.global().getPropertyAsObject(
54+
callbackRuntime, globalNameStr.c_str());
55+
auto subscribersToNotify = observerInstance.getPropertyAsObject(
56+
callbackRuntime, "subscribers");
57+
58+
observerInstance.setProperty(
59+
callbackRuntime, statusPropertyStr.c_str(), updatedStatus);
60+
61+
if (subscribersToNotify.getProperty(callbackRuntime, "size")
62+
.asNumber() == 0) {
63+
return jsi::Value::undefined();
64+
}
65+
66+
auto forEachSubscriber =
67+
subscribersToNotify.getPropertyAsFunction(
68+
callbackRuntime, "forEach");
69+
auto forEachSubscriberCallback = jsi::Function::createFromHostFunction(
70+
callbackRuntime,
71+
jsi::PropNameID::forAscii(callbackRuntime, "forEachCallback"),
72+
1,
73+
[updatedStatus](
74+
jsi::Runtime& forEachCallbackRuntime,
75+
const jsi::Value& /* forEachCallbackThisVal */,
76+
const jsi::Value* forEachCallbackArgs,
77+
size_t forEachCallbackArgsCount) {
78+
if (forEachCallbackArgsCount < 1 ||
79+
!forEachCallbackArgs[0].isObject() ||
80+
!forEachCallbackArgs[0]
81+
.getObject(forEachCallbackRuntime)
82+
.isFunction(forEachCallbackRuntime)) {
83+
throw jsi::JSError(
84+
forEachCallbackRuntime,
85+
"Invalid arguments: forEachSubscriberCallback expects function as a first argument");
86+
}
87+
88+
forEachCallbackArgs[0]
89+
.getObject(forEachCallbackRuntime)
90+
.asFunction(forEachCallbackRuntime)
91+
.call(forEachCallbackRuntime, updatedStatus);
92+
93+
return jsi::Value::undefined();
94+
});
95+
96+
forEachSubscriber.callWithThis(
97+
callbackRuntime,
98+
subscribersToNotify,
99+
forEachSubscriberCallback);
100+
101+
return jsi::Value::undefined();
102+
}));
103+
104+
globalObj.setProperty(runtime, globalName, observer);
105+
} catch (jsi::JSError&) {
106+
// Suppress any errors, they should not be visible to the user
107+
// and should not affect runtime.
108+
}
109+
}
110+
111+
void emitGlobalStateObserverChange(
112+
jsi::Runtime& runtime,
113+
const char* globalName,
114+
const char* callbackName,
115+
bool value) {
116+
auto globalObj = runtime.global();
117+
auto observer = globalObj.getPropertyAsObject(runtime, globalName);
118+
auto callback = observer.getPropertyAsFunction(runtime, callbackName);
119+
callback.call(runtime, jsi::Value(value));
120+
}
121+
122+
} // namespace facebook::react::jsinspector_modern
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <jsi/jsi.h>
11+
12+
namespace facebook::react::jsinspector_modern {
13+
14+
/**
15+
* Installs a global state observer object on the JavaScript runtime's global
16+
* object. The observer has a boolean status property, a Set of subscribers,
17+
* and a callback that updates the status and notifies subscribers.
18+
*
19+
* @param globalName The name of the global object (e.g.,
20+
* "__DEBUGGER_SESSION_OBSERVER__").
21+
* @param statusProperty The name of the boolean property (e.g.,
22+
* "hasActiveSession").
23+
* @param callbackName The name of the state change callback (e.g.,
24+
* "onSessionStatusChange").
25+
*/
26+
void installGlobalStateObserver(
27+
jsi::Runtime &runtime,
28+
const char *globalName,
29+
const char *statusProperty,
30+
const char *callbackName);
31+
32+
/**
33+
* Emits a state change to an installed global state observer by calling its
34+
* callback function.
35+
*
36+
* @param globalName The name of the global object.
37+
* @param callbackName The name of the state change callback.
38+
* @param value The new boolean state value.
39+
*/
40+
void emitGlobalStateObserverChange(jsi::Runtime &runtime, const char *globalName, const char *callbackName, bool value);
41+
42+
} // namespace facebook::react::jsinspector_modern

packages/react-native/src/private/devsupport/rndevtools/FuseboxSessionObserver.js

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,21 @@
88
* @format
99
*/
1010

11-
class FuseboxSessionObserver {
12-
#hasNativeSupport: boolean;
11+
import GlobalStateObserver from './GlobalStateObserver';
1312

14-
constructor() {
15-
this.#hasNativeSupport = global.hasOwnProperty(
16-
'__DEBUGGER_SESSION_OBSERVER__',
17-
);
18-
}
13+
const observer = new GlobalStateObserver(
14+
'__DEBUGGER_SESSION_OBSERVER__',
15+
'hasActiveSession',
16+
);
1917

18+
const FuseboxSessionObserver = {
2019
hasActiveSession(): boolean {
21-
if (!this.#hasNativeSupport) {
22-
return false;
23-
}
24-
25-
return global.__DEBUGGER_SESSION_OBSERVER__.hasActiveSession;
26-
}
20+
return observer.getStatus();
21+
},
2722

2823
subscribe(callback: (status: boolean) => void): () => void {
29-
if (!this.#hasNativeSupport) {
30-
return () => {};
31-
}
32-
33-
global.__DEBUGGER_SESSION_OBSERVER__.subscribers.add(callback);
34-
return () => {
35-
global.__DEBUGGER_SESSION_OBSERVER__.subscribers.delete(callback);
36-
};
37-
}
38-
}
24+
return observer.subscribe(callback);
25+
},
26+
};
3927

40-
const observerInstance: FuseboxSessionObserver = new FuseboxSessionObserver();
41-
export default observerInstance;
28+
export default FuseboxSessionObserver;

0 commit comments

Comments
 (0)