diff --git a/.circleci/configurations/top_level.yml b/.circleci/configurations/top_level.yml index 5d3281d36400..165b2be7969e 100644 --- a/.circleci/configurations/top_level.yml +++ b/.circleci/configurations/top_level.yml @@ -92,7 +92,7 @@ references: # Cocoapods - RNTester pods_cache_key: &pods_cache_key v11-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }} cocoapods_cache_key: &cocoapods_cache_key v11-cocoapods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock" }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/hermes/hermesversion" }} - rntester_podfile_lock_cache_key: &rntester_podfile_lock_cache_key v9-podfilelock-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/week_year" }}-{{ checksum "/tmp/hermes/hermesversion" }} + rntester_podfile_lock_cache_key: &rntester_podfile_lock_cache_key v10-podfilelock-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/week_year" }}-{{ checksum "/tmp/hermes/hermesversion" }} # Cocoapods - Template template_cocoapods_cache_key: &template_cocoapods_cache_key v6-cocoapods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile.lock" }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }}-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "packages/rn-tester/Podfile.lock" }} diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index cdfe1189593d..a8ccb3cbf492 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -36,7 +36,7 @@ jobs: uses: actions/cache@v3 with: path: packages/rn-tester/Pods - key: v2-${{ runner.os }}-RNTesterPods-${{ hashFiles('packages/rn-tester/Podfile.lock') }}-${{ hashFiles('packages/rn-tester/Podfile') }}-${{ hashFiles('tmp/hermes/hermesversion') }} + key: v3-${{ runner.os }}-RNTesterPods-${{ hashFiles('packages/rn-tester/Podfile.lock') }}-${{ hashFiles('packages/rn-tester/Podfile') }}-${{ hashFiles('tmp/hermes/hermesversion') }} - name: Pod Install run: | cd packages/rn-tester diff --git a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegate.cpp b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegate.cpp index 2641d55c0098..f3ba43500dd5 100644 --- a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegate.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegate.cpp @@ -13,9 +13,13 @@ #include #include +#include #include +#include + using namespace facebook::hermes; +using namespace std::chrono; namespace facebook::react::jsinspector_modern { @@ -124,11 +128,15 @@ class HermesRuntimeAgentDelegate::Impl final : public RuntimeAgentDelegate { .origin = executionContextDescription.origin, .name = executionContextDescription.name, .auxData = std::nullopt, - .shouldSendNotifications = false})) { + .shouldSendNotifications = false})), + frontendChannel_(std::move(frontendChannel)) { + if (sessionState.isLogDomainEnabled) { + sendHermesIntegrationDescription(); + } hermes_->registerCallbacks( /* msgCallback */ [frontendChannel = - std::move(frontendChannel)](const std::string& messageFromHermes) { + frontendChannel_](const std::string& messageFromHermes) { frontendChannel(messageFromHermes); ; }, @@ -145,6 +153,13 @@ class HermesRuntimeAgentDelegate::Impl final : public RuntimeAgentDelegate { * agent expects another agent to respond to the request instead. */ bool handleRequest(const cdp::PreparsedRequest& req) override { + if (req.method == "Log.enable") { + sendHermesIntegrationDescription(); + + // The parent Agent should send a response. + return false; + } + // TODO: Change to string::starts_with when we're on C++20. if (req.method.rfind("Log.", 0) == 0) { // Since we know Hermes doesn't do anything useful with Log messages, but @@ -165,7 +180,37 @@ class HermesRuntimeAgentDelegate::Impl final : public RuntimeAgentDelegate { } private: + /** + * Send a user-facing message describing the current Hermes integration. You + * must ensure that the frontend has enabled Log notifications (using + * Log.enable) prior to calling this function. + */ + void sendHermesIntegrationDescription() { + sendInfoLogEntry("Hermes integration: CDPHandler"); + } + + /** + * Send a simple Log.entryAdded notification with the given + * \param text. You must ensure that the frontend has enabled Log + * notifications (using Log.enable) prior to calling this function. In Chrome + * DevTools, the message will appear in the Console tab along with regular + * console messages. + */ + void sendInfoLogEntry(std::string_view text) { + frontendChannel_(cdp::jsonNotification( + "Log.entryAdded", + folly::dynamic::object( + "entry", + folly::dynamic::object( + "timestamp", + duration_cast( + system_clock::now().time_since_epoch()) + .count())("source", "other")( + "level", "info")("text", text)))); + } + std::shared_ptr hermes_; + FrontendChannel frontendChannel_; }; HermesRuntimeAgentDelegate::HermesRuntimeAgentDelegate( diff --git a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.cpp b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.cpp index 2f628aef9d15..1ca7fdd20014 100644 --- a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.cpp @@ -17,17 +17,40 @@ #include #include +#include #include +#include + using namespace facebook::hermes; +using namespace std::chrono; namespace facebook::react::jsinspector_modern { class HermesRuntimeAgentDelegateNew::Impl final : public RuntimeAgentDelegate { + using HermesState = hermes::cdp::State; + + struct HermesStateWrapper : public ExportedState { + explicit HermesStateWrapper(HermesState state) : state_(std::move(state)) {} + + static HermesState unwrapDestructively(ExportedState* wrapper) { + if (!wrapper) { + return {}; + } + if (auto* typedWrapper = dynamic_cast(wrapper)) { + return std::move(typedWrapper->state_); + } + return {}; + } + + private: + HermesState state_; + }; + public: Impl( FrontendChannel frontendChannel, - SessionState& /*unused*/, + SessionState& sessionState, std::unique_ptr previouslyExportedState, const ExecutionContextDescription& executionContextDescription, @@ -44,24 +67,27 @@ class HermesRuntimeAgentDelegateNew::Impl final : public RuntimeAgentDelegate { runtimeExecutor( [&runtime, fn = std::move(fn)](auto&) { fn(runtime); }); }, - std::move(frontendChannel))) { - // TODO(T178858701): Pass previouslyExportedState to CDPAgent - (void)previouslyExportedState; + frontendChannel, + HermesStateWrapper::unwrapDestructively( + previouslyExportedState.get()))), + frontendChannel_(frontendChannel) { + if (sessionState.isLogDomainEnabled) { + sendHermesIntegrationDescription(); + } } - /** - * Handle a CDP request. The response will be sent over the provided - * \c FrontendChannel synchronously or asynchronously. - * \param req The parsed request. - * \returns true if this agent has responded, or will respond asynchronously, - * to the request (with either a success or error message). False if the - * agent expects another agent to respond to the request instead. - */ bool handleRequest(const cdp::PreparsedRequest& req) override { + if (req.method == "Log.enable") { + sendHermesIntegrationDescription(); + + // The parent Agent should send a response. + return false; + } + // TODO: Change to string::starts_with when we're on C++20. if (req.method.rfind("Log.", 0) == 0) { - // Since we know Hermes doesn't do anything useful with Log messages, but - // our containing PageAgent will, just bail out early. + // Since we know Hermes doesn't do anything useful with Log messages, + // but our containing HostAgent will, bail out early. // TODO: We need a way to negotiate this more dynamically with Hermes // through the API. return false; @@ -73,8 +99,42 @@ class HermesRuntimeAgentDelegateNew::Impl final : public RuntimeAgentDelegate { return true; } + std::unique_ptr getExportedState() override { + return std::make_unique(hermes_->getState()); + } + private: + /** + * Send a user-facing message describing the current Hermes integration. You + * must ensure that the frontend has enabled Log notifications (using + * Log.enable) prior to calling this function. + */ + void sendHermesIntegrationDescription() { + sendInfoLogEntry("Hermes integration: CDPAgent"); + } + + /** + * Send a simple Log.entryAdded notification with the given + * \param text. You must ensure that the frontend has enabled Log + * notifications (using Log.enable) prior to calling this function. In Chrome + * DevTools, the message will appear in the Console tab along with regular + * console messages. + */ + void sendInfoLogEntry(std::string_view text) { + frontendChannel_(cdp::jsonNotification( + "Log.entryAdded", + folly::dynamic::object( + "entry", + folly::dynamic::object( + "timestamp", + duration_cast( + system_clock::now().time_since_epoch()) + .count())("source", "other")( + "level", "info")("text", text)))); + } + std::unique_ptr hermes_; + FrontendChannel frontendChannel_; }; HermesRuntimeAgentDelegateNew::HermesRuntimeAgentDelegateNew( @@ -100,6 +160,11 @@ bool HermesRuntimeAgentDelegateNew::handleRequest( return impl_->handleRequest(req); } +std::unique_ptr +HermesRuntimeAgentDelegateNew::getExportedState() { + return impl_->getExportedState(); +} + } // namespace facebook::react::jsinspector_modern #endif // HERMES_ENABLE_DEBUGGER diff --git a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.h b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.h index 3a8e959b23fc..674bc8d049cb 100644 --- a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.h +++ b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeAgentDelegateNew.h @@ -66,6 +66,9 @@ class HermesRuntimeAgentDelegateNew : public RuntimeAgentDelegate { */ bool handleRequest(const cdp::PreparsedRequest& req) override; + std::unique_ptr getExportedState() + override; + private: class Impl; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp index ce89fed22456..3778cd7c6923 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp @@ -557,9 +557,7 @@ TYPED_TEST( std::to_string(executionContextId))); } -// TODO(T178858701): Restore breakpoint reload persistence under -// HermesRuntimeAgentDelegateNew -TYPED_TEST(JsiIntegrationHermesLegacyTest, ResolveBreakpointAfterReload) { +TYPED_TEST(JsiIntegrationHermesTest, ResolveBreakpointAfterReload) { this->connect(); InSequence s;