Skip to content

Commit 53951d7

Browse files
robhoganfacebook-github-bot
authored andcommitted
Debugger: Make /json/list ordered (facebook#45069)
Summary: Pull Request resolved: facebook#45069 Currently, `/json/list` returns pages within each device in the iteration order of a C++ `unordered_map`, which doesn't tell us anything useful. Page IDs happen to be sequential, but only as an implementation detail. Change this contract so that we guarantee ordering reflects addition order, allowing clients to consistently select e.g. most recently added page for a given device. The implementation of this is as simple as switching from an `unordered_map` to a key-ordered`map`, because we already assign keys (page IDs) with an incrementing integer. Within the inspector proxy, devices already use an insertion (connection)-ordered JS `Map`, so we just document this guarantee. Changelog: [General][Changed] Debugger: Make `/json/list` return connection-addition-ordered targets. Reviewed By: huntie Differential Revision: D58735947 fbshipit-source-id: 7a132cc5e750475792a2b845afc9a42424690bf1
1 parent 30087a6 commit 53951d7

File tree

5 files changed

+44
-14
lines changed

5 files changed

+44
-14
lines changed

packages/dev-middleware/src/inspector-proxy/Device.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ export default class Device {
491491
// locations).
492492
async #handleMessageFromDevice(message: MessageFromDevice) {
493493
if (message.event === 'getPages') {
494+
// Preserve ordering - getPages guarantees addition order.
494495
this.#pages = new Map(
495496
message.payload.map(({capabilities, ...page}) => [
496497
page.id,

packages/dev-middleware/src/inspector-proxy/InspectorProxy.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ const DEBUGGER_HEARTBEAT_INTERVAL_MS = 10000;
4343
const INTERNAL_ERROR_CODE = 1011;
4444

4545
export interface InspectorProxyQueries {
46+
/**
47+
* Returns list of page descriptions ordered by device connection order, then
48+
* page addition order.
49+
*/
4650
getPageDescriptions(): Array<PageDescription>;
4751
}
4852

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class InspectorImpl : public IInspector {
7373
};
7474
mutable std::mutex mutex_;
7575
int nextPageId_{1};
76-
std::unordered_map<int, Page> pages_;
76+
std::map<int, Page> pages_;
7777
std::list<std::weak_ptr<IPageStatusListener>> listeners_;
7878
};
7979

@@ -109,6 +109,8 @@ int InspectorImpl::addPage(
109109
InspectorTargetCapabilities capabilities) {
110110
std::scoped_lock lock(mutex_);
111111

112+
// Note: getPages guarantees insertion/addition order. As an implementation
113+
// detail, incrementing page IDs takes advantage of std::map's key ordering.
112114
int pageId = nextPageId_++;
113115
assert(pages_.count(pageId) == 0 && "Unexpected duplicate page ID");
114116
pages_.emplace(
@@ -133,6 +135,8 @@ std::vector<InspectorPageDescription> InspectorImpl::getPages() const {
133135
std::scoped_lock lock(mutex_);
134136

135137
std::vector<InspectorPageDescription> inspectorPages;
138+
// pages_ is a std::map keyed on an incremental id, so this is insertion
139+
// ordered.
136140
for (auto& it : pages_) {
137141
inspectorPages.push_back(InspectorPageDescription(it.second));
138142
}

packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ class JSINSPECTOR_EXPORT IInspector : public IDestructible {
109109
/// debuggable pages.
110110
virtual void removePage(int pageId) = 0;
111111

112-
/// getPages is called by the client to list all debuggable pages.
112+
/**
113+
* Called by the client to retrieve all debuggable pages.
114+
* \returns A vector of page descriptions in the order in which they were
115+
* added with \c addPage.
116+
*/
113117
virtual std::vector<InspectorPageDescription> getPages() const = 0;
114118

115119
/**

packages/react-native/ReactCommon/jsinspector-modern/tests/InspectorPackagerConnectionTest.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,34 +199,51 @@ TEST_F(InspectorPackagerConnectionTest, TestGetPages) {
199199
"event": "getPages"
200200
})");
201201

202-
auto pageId = getInspectorInstance().addPage(
203-
"mock-title",
202+
auto pageId1 = getInspectorInstance().addPage(
203+
"mock-title-1",
204+
"mock-vm",
205+
localConnections_
206+
.lazily_make_unique<std::unique_ptr<IRemoteConnection>>(),
207+
{.nativePageReloads = true});
208+
209+
auto pageId2 = getInspectorInstance().addPage(
210+
"mock-title-2",
204211
"mock-vm",
205212
localConnections_
206213
.lazily_make_unique<std::unique_ptr<IRemoteConnection>>(),
207214
{.nativePageReloads = true});
208215

209-
// getPages now reports the page we registered.
216+
// getPages now reports the page we registered, in the order added
210217
EXPECT_CALL(
211218
*webSockets_[0],
212219
send(JsonParsed(AllOf(
213220
AtJsonPtr("/event", Eq("getPages")),
214221
AtJsonPtr(
215222
"/payload",
216-
ElementsAreArray({AllOf(
217-
AtJsonPtr("/app", Eq("my-app")),
218-
AtJsonPtr("/title", Eq("mock-title [C++ connection]")),
219-
AtJsonPtr("/id", Eq(std::to_string(pageId))),
220-
AtJsonPtr("/capabilities/nativePageReloads", Eq(true)),
221-
AtJsonPtr(
222-
"/capabilities/nativeSourceCodeFetching",
223-
Eq(false)))}))))))
223+
ElementsAreArray(
224+
{AllOf(
225+
AtJsonPtr("/app", Eq("my-app")),
226+
AtJsonPtr("/title", Eq("mock-title-1 [C++ connection]")),
227+
AtJsonPtr("/id", Eq(std::to_string(pageId1))),
228+
AtJsonPtr("/capabilities/nativePageReloads", Eq(true)),
229+
AtJsonPtr(
230+
"/capabilities/nativeSourceCodeFetching",
231+
Eq(false))),
232+
AllOf(
233+
AtJsonPtr("/app", Eq("my-app")),
234+
AtJsonPtr("/title", Eq("mock-title-2 [C++ connection]")),
235+
AtJsonPtr("/id", Eq(std::to_string(pageId2))),
236+
AtJsonPtr("/capabilities/nativePageReloads", Eq(true)),
237+
AtJsonPtr(
238+
"/capabilities/nativeSourceCodeFetching",
239+
Eq(false)))}))))))
224240
.RetiresOnSaturation();
225241
webSockets_[0]->getDelegate().didReceiveMessage(R"({
226242
"event": "getPages"
227243
})");
228244

229-
getInspectorInstance().removePage(pageId);
245+
getInspectorInstance().removePage(pageId1);
246+
getInspectorInstance().removePage(pageId2);
230247

231248
// getPages is back to reporting no pages.
232249
EXPECT_CALL(

0 commit comments

Comments
 (0)