diff --git a/ReactCommon/hermes/inspector/chrome/Connection.cpp b/ReactCommon/hermes/inspector/chrome/Connection.cpp index b7b264093b221d..ac7b36cc35a8cb 100644 --- a/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -107,6 +107,7 @@ class Connection::Impl : public inspector::InspectorObserver, void handle(const m::runtime::EvaluateRequest &req) override; void handle(const m::runtime::GetHeapUsageRequest &req) override; void handle(const m::runtime::GetPropertiesRequest &req) override; + void handle(const m::runtime::GlobalLexicalScopeNamesRequest &req) override; void handle(const m::runtime::RunIfWaitingForDebuggerRequest &req) override; private: @@ -1451,6 +1452,45 @@ void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) { .thenError(sendErrorToClient(req.id)); } +void Connection::Impl::handle( + const m::runtime::GlobalLexicalScopeNamesRequest &req) { + auto resp = std::make_shared(); + resp->id = req.id; + + inspector_ + ->executeIfEnabled( + "Runtime.globalLexicalScopeNames", + [req, resp](const debugger::ProgramState &state) { + if (req.executionContextId.hasValue() && + req.executionContextId.value() != kHermesExecutionContextId) { + throw std::invalid_argument("Invalid execution context"); + } + + const debugger::LexicalInfo &lexicalInfo = state.getLexicalInfo(0); + debugger::ScopeDepth scopeCount = lexicalInfo.getScopesCount(); + if (scopeCount == 0) { + return; + } + + const debugger::ScopeDepth globalScopeIndex = scopeCount - 1; + uint32_t variableCount = + lexicalInfo.getVariablesCountInScope(globalScopeIndex); + resp->names.reserve(variableCount); + for (uint32_t i = 0; i < variableCount; i++) { + debugger::String name = + state.getVariableInfo(0, globalScopeIndex, i).name; + // The global scope has some entries prefixed with '?', which are + // not valid identifiers. + if (!name.empty() && name.front() != '?') { + resp->names.push_back(name); + } + } + }) + .via(executor_.get()) + .thenValue([this, resp](auto &&) { sendResponseToClient(*resp); }) + .thenError(sendErrorToClient(req.id)); +} + void Connection::Impl::handle( const m::runtime::RunIfWaitingForDebuggerRequest &req) { if (inspector_->isAwaitingDebuggerOnStart()) { diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index 551874682ba535..e04f8f7c322c80 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<> +// @generated SignedSource<> #include "MessageTypes.h" @@ -66,6 +66,8 @@ std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { {"Runtime.evaluate", makeUnique}, {"Runtime.getHeapUsage", makeUnique}, {"Runtime.getProperties", makeUnique}, + {"Runtime.globalLexicalScopeNames", + makeUnique}, {"Runtime.runIfWaitingForDebugger", makeUnique}, }; @@ -1199,6 +1201,38 @@ void runtime::GetPropertiesRequest::accept(RequestHandler &handler) const { handler.handle(*this); } +runtime::GlobalLexicalScopeNamesRequest::GlobalLexicalScopeNamesRequest() + : Request("Runtime.globalLexicalScopeNames") {} + +runtime::GlobalLexicalScopeNamesRequest::GlobalLexicalScopeNamesRequest( + const dynamic &obj) + : Request("Runtime.globalLexicalScopeNames") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + auto it = obj.find("params"); + if (it != obj.items().end()) { + dynamic params = it->second; + assign(executionContextId, params, "executionContextId"); + } +} + +dynamic runtime::GlobalLexicalScopeNamesRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "executionContextId", executionContextId); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void runtime::GlobalLexicalScopeNamesRequest::accept( + RequestHandler &handler) const { + handler.handle(*this); +} + runtime::RunIfWaitingForDebuggerRequest::RunIfWaitingForDebuggerRequest() : Request("Runtime.runIfWaitingForDebugger") {} @@ -1481,6 +1515,24 @@ dynamic runtime::GetPropertiesResponse::toDynamic() const { return obj; } +runtime::GlobalLexicalScopeNamesResponse::GlobalLexicalScopeNamesResponse( + const dynamic &obj) { + assign(id, obj, "id"); + + dynamic res = obj.at("result"); + assign(names, res, "names"); +} + +dynamic runtime::GlobalLexicalScopeNamesResponse::toDynamic() const { + dynamic res = dynamic::object; + put(res, "names", names); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "result", std::move(res)); + return obj; +} + /// Notifications debugger::BreakpointResolvedNotification::BreakpointResolvedNotification() : Notification("Debugger.breakpointResolved") {} diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/ReactCommon/hermes/inspector/chrome/MessageTypes.h index c74a21d357e1d4..98b11b5f305257 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<> +// @generated SignedSource<> #pragma once @@ -63,6 +63,8 @@ struct GetHeapUsageRequest; struct GetHeapUsageResponse; struct GetPropertiesRequest; struct GetPropertiesResponse; +struct GlobalLexicalScopeNamesRequest; +struct GlobalLexicalScopeNamesResponse; struct InternalPropertyDescriptor; struct PropertyDescriptor; struct RemoteObject; @@ -142,6 +144,7 @@ struct RequestHandler { virtual void handle(const runtime::EvaluateRequest &req) = 0; virtual void handle(const runtime::GetHeapUsageRequest &req) = 0; virtual void handle(const runtime::GetPropertiesRequest &req) = 0; + virtual void handle(const runtime::GlobalLexicalScopeNamesRequest &req) = 0; virtual void handle(const runtime::RunIfWaitingForDebuggerRequest &req) = 0; }; @@ -180,6 +183,7 @@ struct NoopRequestHandler : public RequestHandler { void handle(const runtime::EvaluateRequest &req) override {} void handle(const runtime::GetHeapUsageRequest &req) override {} void handle(const runtime::GetPropertiesRequest &req) override {} + void handle(const runtime::GlobalLexicalScopeNamesRequest &req) override {} void handle(const runtime::RunIfWaitingForDebuggerRequest &req) override {} }; @@ -686,6 +690,16 @@ struct runtime::GetPropertiesRequest : public Request { folly::Optional ownProperties; }; +struct runtime::GlobalLexicalScopeNamesRequest : public Request { + GlobalLexicalScopeNamesRequest(); + explicit GlobalLexicalScopeNamesRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + folly::Optional executionContextId; +}; + struct runtime::RunIfWaitingForDebuggerRequest : public Request { RunIfWaitingForDebuggerRequest(); explicit RunIfWaitingForDebuggerRequest(const folly::dynamic &obj); @@ -816,6 +830,14 @@ struct runtime::GetPropertiesResponse : public Response { folly::Optional exceptionDetails; }; +struct runtime::GlobalLexicalScopeNamesResponse : public Response { + GlobalLexicalScopeNamesResponse() = default; + explicit GlobalLexicalScopeNamesResponse(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + std::vector names; +}; + /// Notifications struct debugger::BreakpointResolvedNotification : public Notification { BreakpointResolvedNotification(); diff --git a/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp b/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp index b6538ea56e6961..64d29f552108ba 100644 --- a/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp +++ b/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp @@ -2884,6 +2884,54 @@ TEST(ConnectionTests, testBasicProfilerOperation) { asyncRuntime.stop(); } +TEST(ConnectionTests, testGlobalLexicalScopeNames) { + TestContext context; + AsyncHermesRuntime &asyncRuntime = context.runtime(); + SyncConnection &conn = context.conn(); + int msgId = 1; + asyncRuntime.executeScriptAsync(R"( + let globalLet = "let"; + const globalConst = "const"; + var globalVar = "var"; + + let func1 = () => { + let local1 = 111; + func2(); + } + + function func2() { + let func3 = () => { + let local3 = 333; + debugger; + } + + let local2 = 222; + func3(); + } + + func1(); + )"); + send(conn, msgId++); + expectExecutionContextCreated(conn); + expectNotification(conn); + expectNotification(conn); + + m::runtime::GlobalLexicalScopeNamesRequest req; + req.id = msgId; + req.executionContextId = 1; + conn.send(req.toJson()); + + auto resp = + expectResponse(conn, msgId); + EXPECT_EQ(resp.id, msgId++); + std::sort(resp.names.begin(), resp.names.end()); + std::vector expectedNames{"func1", "globalConst", "globalLet"}; + EXPECT_EQ(resp.names, expectedNames); + + send(conn, msgId++); + expectNotification(conn); +} + } // namespace chrome } // namespace inspector } // namespace hermes diff --git a/ReactCommon/hermes/inspector/tools/message_types.txt b/ReactCommon/hermes/inspector/tools/message_types.txt index 801ff6a49dce42..99c92cfca05028 100644 --- a/ReactCommon/hermes/inspector/tools/message_types.txt +++ b/ReactCommon/hermes/inspector/tools/message_types.txt @@ -37,3 +37,4 @@ Runtime.executionContextCreated Runtime.getHeapUsage Runtime.getProperties Runtime.runIfWaitingForDebugger +Runtime.globalLexicalScopeNames