diff --git a/ReactCommon/hermes/inspector/Inspector.cpp b/ReactCommon/hermes/inspector/Inspector.cpp index 8ab8e8643aa..7376b578aa1 100644 --- a/ReactCommon/hermes/inspector/Inspector.cpp +++ b/ReactCommon/hermes/inspector/Inspector.cpp @@ -382,6 +382,34 @@ folly::Future Inspector::setPauseOnExceptions( return promise->getFuture(); }; +folly::Future Inspector::setPauseOnLoads( + const PauseOnLoadMode mode) { + std::unique_lock lock(mutex_); + auto promise = std::make_shared>(); + pauseOnLoadMode_ = mode; + promise->setValue(); + return promise->getFuture(); +}; + +bool Inspector::shouldPauseOnThisScriptLoad() { + switch (pauseOnLoadMode_) { + case None: + return false; + case All: + return true; + case Smart: + // If we don't have active breakpoints, there's nothing to set or update. + if (debugger_.getBreakpoints().size() == 0) { + return false; + } + // If there's no source map URL, it's probably not a file we care about. + if (getScriptInfoFromTopCallFrame().sourceMappingUrl.size() == 0) { + return false; + } + return true; + } +}; + debugger::Command Inspector::didPause(debugger::Debugger &debugger) { std::unique_lock lock(mutex_); diff --git a/ReactCommon/hermes/inspector/Inspector.h b/ReactCommon/hermes/inspector/Inspector.h index 0cc0b8f4dd4..bf4fc553c1d 100644 --- a/ReactCommon/hermes/inspector/Inspector.h +++ b/ReactCommon/hermes/inspector/Inspector.h @@ -53,6 +53,8 @@ struct ConsoleMessageInfo { args(std::move(args)) {} }; +enum PauseOnLoadMode { None, Smart, All }; + /** * InspectorObserver notifies the observer of events that occur in the VM. */ @@ -200,6 +202,19 @@ class Inspector : public facebook::hermes::debugger::EventObserver, folly::Future setPauseOnExceptions( const facebook::hermes::debugger::PauseOnThrowMode &mode); + /** + * Set whether to pause on loads. This does not require runtime modifications, + * but returns a future for consistency. + */ + folly::Future setPauseOnLoads(const PauseOnLoadMode mode); + + /** + * If called during a script load event, return true if we should pause. + * Assumed to be called from a script load event where we already hold + * `mutex_`. + */ + bool shouldPauseOnThisScriptLoad(); + /** * didPause implements the pause callback from Hermes. This callback arrives * on the JS thread. @@ -297,6 +312,9 @@ class Inspector : public facebook::hermes::debugger::EventObserver, // this state is here rather than in the Running class. AsyncPauseState pendingPauseState_ = AsyncPauseState::None; + // Whether we should enter a paused state when a script loads. + PauseOnLoadMode pauseOnLoadMode_ = PauseOnLoadMode::None; + // All scripts loaded in to the VM, along with whether we've notified the // client about the script yet. struct LoadedScriptInfo { diff --git a/ReactCommon/hermes/inspector/InspectorState.cpp b/ReactCommon/hermes/inspector/InspectorState.cpp index 7b7552f3e1e..7d6c35e05bb 100644 --- a/ReactCommon/hermes/inspector/InspectorState.cpp +++ b/ReactCommon/hermes/inspector/InspectorState.cpp @@ -236,6 +236,10 @@ std::pair InspectorState::Running::didPause( } else if (reason == debugger::PauseReason::ScriptLoaded) { inspector_.addCurrentScriptToLoadedScripts(); inspector_.notifyScriptsLoaded(); + if (inspector_.shouldPauseOnThisScriptLoad()) { + return std::make_pair( + InspectorState::Paused::make(inspector_), nullptr); + } } else if (reason == debugger::PauseReason::EvalComplete) { assert(pendingEvalPromise_); diff --git a/ReactCommon/hermes/inspector/chrome/Connection.cpp b/ReactCommon/hermes/inspector/chrome/Connection.cpp index ca3f4191c4d..354eede529c 100644 --- a/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -88,6 +88,7 @@ class Connection::Impl : public inspector::InspectorObserver, const m::heapProfiler::StopTrackingHeapObjectsRequest &req) override; void handle(const m::runtime::EvaluateRequest &req) override; void handle(const m::runtime::GetPropertiesRequest &req) override; + void handle(const m::hermes::SetPauseOnLoadRequest &req) override; private: std::vector makePropsFromScope( @@ -290,6 +291,9 @@ void Connection::Impl::onPause( case debugger::PauseReason::Exception: note.reason = "exception"; break; + case debugger::PauseReason::ScriptLoaded: + note.reason = "load"; + break; default: note.reason = "other"; break; @@ -757,6 +761,21 @@ void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) { .thenError(sendErrorToClient(req.id)); } +void Connection::Impl::handle(const m::hermes::SetPauseOnLoadRequest &req) { + PauseOnLoadMode mode; + if (req.state == "none") { + mode = PauseOnLoadMode::None; + } else if (req.state == "all") { + mode = PauseOnLoadMode::All; + } else if (req.state == "smart") { + mode = PauseOnLoadMode::Smart; + } else { + sendErrorToClientViaExecutor(req.id, "Unrecognized pause on load mode"); + return; + } + sendResponseToClientViaExecutor(inspector_->setPauseOnLoads(mode), req.id); +} + /* * Send-to-client methods */ diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index ae0f77851a1..18ce5ac65a6 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<<4ab81efd6f767bd583d00c806b7d1d9b>> +// @generated SignedSource<<0d7691362d081e7bc44d2b7a0ed24371>> #include "MessageTypes.h" @@ -46,6 +46,7 @@ std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { makeUnique}, {"HeapProfiler.takeHeapSnapshot", makeUnique}, + {"Hermes.setPauseOnLoad", makeUnique}, {"Runtime.evaluate", makeUnique}, {"Runtime.getProperties", makeUnique}, }; @@ -682,6 +683,33 @@ void heapProfiler::TakeHeapSnapshotRequest::accept( handler.handle(*this); } +hermes::SetPauseOnLoadRequest::SetPauseOnLoadRequest() + : Request("Hermes.setPauseOnLoad") {} + +hermes::SetPauseOnLoadRequest::SetPauseOnLoadRequest(const dynamic &obj) + : Request("Hermes.setPauseOnLoad") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(state, params, "state"); +} + +dynamic hermes::SetPauseOnLoadRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "state", state); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void hermes::SetPauseOnLoadRequest::accept(RequestHandler &handler) const { + handler.handle(*this); +} + runtime::EvaluateRequest::EvaluateRequest() : Request("Runtime.evaluate") {} runtime::EvaluateRequest::EvaluateRequest(const dynamic &obj) diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/ReactCommon/hermes/inspector/chrome/MessageTypes.h index 503b486743c..3d7222fd6ac 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<<0a1a011902fd18d4eebd2fe12fafb8b1>> +// @generated SignedSource<<08b66e22784e225b926d36131b9a7693>> #pragma once @@ -73,6 +73,10 @@ struct StopTrackingHeapObjectsRequest; struct TakeHeapSnapshotRequest; } // namespace heapProfiler +namespace hermes { +struct SetPauseOnLoadRequest; +} // namespace hermes + /// RequestHandler handles requests via the visitor pattern. struct RequestHandler { virtual ~RequestHandler() = default; @@ -95,6 +99,7 @@ struct RequestHandler { virtual void handle( const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0; virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; + virtual void handle(const hermes::SetPauseOnLoadRequest &req) = 0; virtual void handle(const runtime::EvaluateRequest &req) = 0; virtual void handle(const runtime::GetPropertiesRequest &req) = 0; }; @@ -119,6 +124,7 @@ struct NoopRequestHandler : public RequestHandler { void handle( const heapProfiler::StopTrackingHeapObjectsRequest &req) override {} void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} + void handle(const hermes::SetPauseOnLoadRequest &req) override {} void handle(const runtime::EvaluateRequest &req) override {} void handle(const runtime::GetPropertiesRequest &req) override {} }; @@ -411,6 +417,16 @@ struct heapProfiler::TakeHeapSnapshotRequest : public Request { folly::Optional treatGlobalObjectsAsRoots; }; +struct hermes::SetPauseOnLoadRequest : public Request { + SetPauseOnLoadRequest(); + explicit SetPauseOnLoadRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + std::string state; +}; + struct runtime::EvaluateRequest : public Request { EvaluateRequest(); explicit EvaluateRequest(const folly::dynamic &obj); diff --git a/ReactCommon/hermes/inspector/tools/message_types.txt b/ReactCommon/hermes/inspector/tools/message_types.txt index 420fdad15d6..6d02ed4e754 100644 --- a/ReactCommon/hermes/inspector/tools/message_types.txt +++ b/ReactCommon/hermes/inspector/tools/message_types.txt @@ -23,3 +23,4 @@ Runtime.consoleAPICalled Runtime.evaluate Runtime.executionContextCreated Runtime.getProperties +Hermes.setPauseOnLoad diff --git a/ReactCommon/hermes/inspector/tools/msggen/src/custom.json b/ReactCommon/hermes/inspector/tools/msggen/src/custom.json index cf7aab3e622..c9650a9c293 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/src/custom.json +++ b/ReactCommon/hermes/inspector/tools/msggen/src/custom.json @@ -1,3 +1,26 @@ { - "domains": [] + "domains": [ + { + "domain": "Hermes", + "description": "Hermes specific messages", + "commands": [ + { + "name": "setPauseOnLoad", + "description": "Pause VM when new scripts are loaded (reason='load')", + "parameters": [ + { + "name": "state", + "description": "Pause on script load mode", + "type": "string", + "enum": [ + "none", + "smart", + "all" + ] + } + ] + } + ] + } + ] }